From 35bd6f97b5d048ff29951d3b75606239b6f8d440 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Tue, 29 Jul 2025 01:49:31 +0300 Subject: [PATCH] [NTDLL_WINETEST] Sync to wine-10.0 Co-authored-by: Carl J. Bialorucki --- .../rostests/winetests/ntdll/CMakeLists.txt | 38 +- modules/rostests/winetests/ntdll/atom.c | 186 +- modules/rostests/winetests/ntdll/change.c | 61 +- modules/rostests/winetests/ntdll/directory.c | 604 +- modules/rostests/winetests/ntdll/env.c | 647 +- modules/rostests/winetests/ntdll/error.c | 450 +- modules/rostests/winetests/ntdll/exception.c | 11200 ++++++++++++++-- modules/rostests/winetests/ntdll/file.c | 4073 ++++-- modules/rostests/winetests/ntdll/generated.c | 165 +- modules/rostests/winetests/ntdll/info.c | 3776 ++++-- modules/rostests/winetests/ntdll/large_int.c | 243 +- modules/rostests/winetests/ntdll/msvc-x64.asm | 55 + modules/rostests/winetests/ntdll/om.c | 4066 ++++-- modules/rostests/winetests/ntdll/path.c | 474 +- modules/rostests/winetests/ntdll/pipe.c | 2356 +++- modules/rostests/winetests/ntdll/port.c | 26 +- modules/rostests/winetests/ntdll/process.c | 209 - modules/rostests/winetests/ntdll/reg.c | 2547 +++- .../rostests/winetests/ntdll/ros_diff.patch | 69 - modules/rostests/winetests/ntdll/rtl.c | 2441 ++-- modules/rostests/winetests/ntdll/rtlbitmap.c | 58 +- modules/rostests/winetests/ntdll/rtlstr.c | 758 +- modules/rostests/winetests/ntdll/string.c | 1209 +- modules/rostests/winetests/ntdll/sync.c | 1106 ++ modules/rostests/winetests/ntdll/testlist.c | 15 +- modules/rostests/winetests/ntdll/thread.c | 216 + modules/rostests/winetests/ntdll/threadpool.c | 23 +- modules/rostests/winetests/ntdll/time.c | 454 +- modules/rostests/winetests/ntdll/unwind.c | 3459 +++++ modules/rostests/winetests/ntdll/virtual.c | 3039 +++++ modules/rostests/winetests/ntdll/wow64.c | 3228 +++++ 31 files changed, 39403 insertions(+), 7848 deletions(-) create mode 100644 modules/rostests/winetests/ntdll/msvc-x64.asm delete mode 100644 modules/rostests/winetests/ntdll/process.c delete mode 100644 modules/rostests/winetests/ntdll/ros_diff.patch create mode 100644 modules/rostests/winetests/ntdll/sync.c create mode 100644 modules/rostests/winetests/ntdll/thread.c create mode 100644 modules/rostests/winetests/ntdll/unwind.c create mode 100644 modules/rostests/winetests/ntdll/virtual.c create mode 100644 modules/rostests/winetests/ntdll/wow64.c diff --git a/modules/rostests/winetests/ntdll/CMakeLists.txt b/modules/rostests/winetests/ntdll/CMakeLists.txt index 3a2d7c8c0ae..1b939d1bf8c 100644 --- a/modules/rostests/winetests/ntdll/CMakeLists.txt +++ b/modules/rostests/winetests/ntdll/CMakeLists.txt @@ -1,7 +1,15 @@ include_directories(BEFORE ${REACTOS_SOURCE_DIR}/sdk/include/wine) +remove_definitions(-D_CRT_NON_CONFORMING_SWPRINTFS) add_definitions(-D__WINESRC__ -DWINETEST_USE_DBGSTR_LONGLONG) +remove_definitions(-D_WIN32_WINNT=0x502 -DWINVER=0x502) +add_definitions(-D_WIN32_WINNT=0x601) + +if(_WINKD_) + add_definitions(-D_WINKD_) +endif() + list(APPEND SOURCE atom.c change.c @@ -10,25 +18,31 @@ list(APPEND SOURCE error.c exception.c file.c + generated.c info.c large_int.c om.c path.c pipe.c port.c - process.c reg.c rtl.c rtlbitmap.c rtlstr.c string.c + sync.c testlist.c + thread.c threadpool.c - time.c) + time.c + unwind.c + virtual.c + wow64.c +) -if(ARCH STREQUAL "i386") - list(APPEND SOURCE - generated.c) +if(MSVC AND ARCH STREQUAL "amd64") + add_asm_files(ntdll_winetest_asm msvc-x64.asm) + list(APPEND SOURCE ${ntdll_winetest_asm}) endif() add_executable(ntdll_winetest @@ -36,15 +50,23 @@ add_executable(ntdll_winetest target_link_libraries(ntdll_winetest pseh) -if(MSVC AND ARCH STREQUAL "amd64") - # warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) - target_compile_options(ntdll_winetest PRIVATE /wd4334) +if(MSVC) + target_compile_options(ntdll_winetest PRIVATE + /wd4024 # warning C4024: 'function': different types for formal and actual parameter 1 + /wd4047 # warning C4047: 'function': 'type' differs in levels of indirection from 'type' + /wd4101 # warning C4101: 'variable': unreferenced local variable + /wd4319 # warning C4319: '~': zero extending 'unsigned int' to 'UINT_PTR' of greater size + /wd4334 # warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + ) endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") target_compile_options(ntdll_winetest PRIVATE "-Wno-format") + target_compile_options(ntdll_winetest PRIVATE "-Wno-int-conversion") + target_compile_options(ntdll_winetest PRIVATE "-Wno-unused-variable") endif() set_module_type(ntdll_winetest win32cui) add_importlibs(ntdll_winetest user32 ole32 advapi32 msvcrt kernel32 ntdll) add_rostests_file(TARGET ntdll_winetest) +target_compile_definitions(ntdll_winetest PRIVATE wcsnicmp=_wcsnicmp) diff --git a/modules/rostests/winetests/ntdll/atom.c b/modules/rostests/winetests/ntdll/atom.c index a481758ee6e..a93a76eef2f 100644 --- a/modules/rostests/winetests/ntdll/atom.c +++ b/modules/rostests/winetests/ntdll/atom.c @@ -100,30 +100,30 @@ static DWORD WINAPI RtlAtomTestThread(LPVOID Table) WCHAR Name[64]; res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &Atom); - ok(!res, "Unable to find atom from another thread, retval: %x\n", res); + ok(!res, "Unable to find atom from another thread, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &Atom); - ok(!res, "Unable to lookup pinned atom in table, retval: %x\n", res); + ok(!res, "Unable to lookup pinned atom in table, retval: %lx\n", res); res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, Name, &Len); - ok(res == STATUS_BUFFER_TOO_SMALL, "We got wrong retval: %x\n", res); + ok(res == STATUS_BUFFER_TOO_SMALL, "We got wrong retval: %lx\n", res); Len = 64; res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, Name, &Len); - ok(!res, "Failed with long enough buffer, retval: %x\n", res); - ok(RefCount == 1, "Refcount was not 1 but %x\n", RefCount); - ok(PinCount == 1, "Pincount was not 1 but %x\n", PinCount); + ok(!res, "Failed with long enough buffer, retval: %lx\n", res); + ok(RefCount == 1, "Refcount was not 1 but %lx\n", RefCount); + ok(PinCount == 1, "Pincount was not 1 but %lx\n", PinCount); ok(!lstrcmpW(Name, testAtom2), "We found wrong atom!!\n"); - ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %d\n", Len); + ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len); Len = 64; res = pRtlQueryAtomInAtomTable(AtomTable, Atom, NULL, NULL, Name, &Len); - ok(!res, "RtlQueryAtomInAtomTable with optional args invalid failed, retval: %x\n", res); + ok(!res, "RtlQueryAtomInAtomTable with optional args invalid failed, retval: %lx\n", res); ok(!lstrcmpW(Name, testAtom2), "Found Wrong atom!\n"); - ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %d\n", Len); + ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len); res = pRtlPinAtomInAtomTable(AtomTable, Atom); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); return 0; } @@ -145,37 +145,39 @@ static void test_NtAtom(void) if (!res) { res = pRtlDestroyAtomTable(AtomTable); - ok(!res, "We could create the atom table, but we couldn't destroy it! retval: %x\n", res); + ok(!res, "We could create the atom table, but we couldn't destroy it! retval: %lx\n", res); } AtomTable = NULL; res = pRtlCreateAtomTable(37, &AtomTable); - ok(!res, "We're unable to create an atom table with a valid table size retval: %x\n", res); + ok(!res, "We're unable to create an atom table with a valid table size retval: %lx\n", res); if (!res) { + ok( *(DWORD *)AtomTable == 0x6d6f7441, "wrong signature %lx\n", *(DWORD *)AtomTable ); + res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1); - ok(!res, "We were unable to add a simple atom to the atom table, retval: %x\n", res); + ok(!res, "We were unable to add a simple atom to the atom table, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1Cap, &testAtom); - ok(!res, "We were unable to find capital version of the atom, retval: %x\n", res); + ok(!res, "We were unable to find capital version of the atom, retval: %lx\n", res); ok(Atom1 == testAtom, "Found wrong atom in table when querying capital atom\n"); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1Low, &testAtom); - ok(!res, "Unable to find lowercase version of the atom, retval: %x\n", res); + ok(!res, "Unable to find lowercase version of the atom, retval: %lx\n", res); ok(testAtom == Atom1, "Found wrong atom when querying lowercase atom\n"); res = pRtlAddAtomToAtomTable(AtomTable, EmptyAtom, &testEAtom); - ok(res == STATUS_OBJECT_NAME_INVALID, "Got wrong retval, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_INVALID, "Got wrong retval, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(!res, "Failed to find totally legitimate atom, retval: %x\n", res); + ok(!res, "Failed to find totally legitimate atom, retval: %lx\n", res); ok(testAtom == Atom1, "Found wrong atom!\n"); res = pRtlAddAtomToAtomTable(AtomTable, testAtom2, &Atom2); - ok(!res, "Unable to add other legitimate atom to table, retval: %x\n", res); + ok(!res, "Unable to add other legitimate atom to table, retval: %lx\n", res); res = pRtlPinAtomInAtomTable(AtomTable, Atom2); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); testThread = CreateThread(NULL, 0, RtlAtomTestThread, &AtomTable, 0, NULL); WaitForSingleObject(testThread, INFINITE); @@ -183,28 +185,28 @@ static void test_NtAtom(void) Len = 64; res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, &RefCount, &PinCount, Name, &Len); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); - ok(RefCount == 1, "RefCount is not 1 but %x\n", RefCount); - ok(PinCount == 1, "PinCount is not 1 but %x\n", PinCount); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); + ok(RefCount == 1, "RefCount is not 1 but %lx\n", RefCount); + ok(PinCount == 1, "PinCount is not 1 but %lx\n", PinCount); ok(!lstrcmpW(Name, testAtom2), "We found wrong atom\n"); - ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %d\n", Len); + ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len); res = pRtlEmptyAtomTable(AtomTable, FALSE); - ok(!res, "Unable to empty atom table, retval %x\n", res); + ok(!res, "Unable to empty atom table, retval %lx\n", res); Len = 64; res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, &RefCount, &PinCount, Name, &Len); - ok(!res, "It seems RtlEmptyAtomTable deleted our pinned atom eaven though we asked it not to, retval: %x\n", res); - ok(RefCount == 1, "RefCount is not 1 but %x\n", RefCount); - ok(PinCount == 1, "PinCount is not 1 but %x\n", PinCount); + ok(!res, "It seems RtlEmptyAtomTable deleted our pinned atom eaven though we asked it not to, retval: %lx\n", res); + ok(RefCount == 1, "RefCount is not 1 but %lx\n", RefCount); + ok(PinCount == 1, "PinCount is not 1 but %lx\n", PinCount); ok(!lstrcmpW(Name, testAtom2), "We found wrong atom\n"); - ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %d\n", Len); + ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len); Len = 8; Name[0] = Name[1] = Name[2] = Name[3] = Name[4] = 0x1337; res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, NULL, NULL, Name, &Len); - ok(!res, "query atom %x\n", res); - ok(Len == 6, "wrong length %u\n", Len); + ok(!res, "query atom %lx\n", res); + ok(Len == 6, "wrong length %lu\n", Len); ok(!memcmp(Name, testAtom2, Len), "wrong atom string\n"); ok(!Name[3], "wrong string termination\n"); ok(Name[4] == 0x1337, "buffer overwrite\n"); @@ -212,75 +214,75 @@ static void test_NtAtom(void) Len = lstrlenW(testAtom2) * sizeof(WCHAR); memset(Name, '.', sizeof(Name)); res = pRtlQueryAtomInAtomTable( AtomTable, Atom2, NULL, NULL, Name, &Len ); - ok(!res, "query atom %x\n", res); - ok(Len == (lstrlenW(testAtom2) - 1) * sizeof(WCHAR), "wrong length %u\n", Len); + ok(!res, "query atom %lx\n", res); + ok(Len == (lstrlenW(testAtom2) - 1) * sizeof(WCHAR), "wrong length %lu\n", Len); ok(!memcmp(testAtom2, Name, (lstrlenW(testAtom2) - 1) * sizeof(WCHAR)), "wrong atom name\n"); ok(Name[lstrlenW(testAtom2) - 1] == '\0', "wrong char\n"); ok(Name[lstrlenW(testAtom2)] == ('.' << 8) + '.', "wrong char\n"); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &testAtom); - ok(!res, "We can't find our pinned atom!! retval: %x\n", res); + ok(!res, "We can't find our pinned atom!! retval: %lx\n", res); ok(testAtom == Atom2, "We found wrong atom!!!\n"); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "We found the atom in our table eaven though we asked RtlEmptyAtomTable to remove it, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "We found the atom in our table eaven though we asked RtlEmptyAtomTable to remove it, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtom3, &Atom3); - ok(!res, "Unable to add atom to table, retval: %x\n", res); + ok(!res, "Unable to add atom to table, retval: %lx\n", res); res = pRtlEmptyAtomTable(AtomTable, TRUE); - ok(!res, "Unable to empty atom table, retval: %x\n", res); + ok(!res, "Unable to empty atom table, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &testAtom); - ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "The pinned atom should be removed, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "The pinned atom should be removed, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom3, &testAtom); - ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Non pinned atom should also be removed, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Non pinned atom should also be removed, retval: %lx\n", res); res = pRtlDestroyAtomTable(AtomTable); - ok(!res, "Can't destroy atom table, retval: %x\n", res); + ok(!res, "Can't destroy atom table, retval: %lx\n", res); } AtomTable = NULL; res = pRtlCreateAtomTable(37, &AtomTable); - ok(!res, "Unable to create atom table, retval: %x\n", res); + ok(!res, "Unable to create atom table, retval: %lx\n", res); if (!res) { res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Didn't get expected retval with querying an empty atom table, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Didn't get expected retval with querying an empty atom table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1); - ok(!res, "Unable to add atom to atom table, retval %x\n", res); + ok(!res, "Unable to add atom to atom table, retval %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(!res, "Can't find previously added atom in table, retval: %x\n", res); - ok(testAtom == Atom1, "Found wrong atom! retval: %x\n", res); + ok(!res, "Can't find previously added atom in table, retval: %lx\n", res); + ok(testAtom == Atom1, "Found wrong atom! retval: %lx\n", res); res = pRtlDeleteAtomFromAtomTable(AtomTable, Atom1); - ok(!res, "Unable to delete atom from table, retval: %x\n", res); + ok(!res, "Unable to delete atom from table, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Able to find previously deleted atom in table, retval: %x\n", res); + ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Able to find previously deleted atom in table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1); - ok(!res, "Unable to add atom to atom table, retval: %x\n", res); + ok(!res, "Unable to add atom to atom table, retval: %lx\n", res); Len = 0; res = pRtlQueryAtomInAtomTable(AtomTable, Atom1, NULL, NULL, Name, &Len); - ok(res == STATUS_BUFFER_TOO_SMALL, "Got wrong retval, retval: %x\n", res); - ok((lstrlenW(testAtom1) * sizeof(WCHAR)) == Len || broken(!Len) /* nt4 */, "Got wrong length %x\n", Len); + ok(res == STATUS_BUFFER_TOO_SMALL, "Got wrong retval, retval: %lx\n", res); + ok((lstrlenW(testAtom1) * sizeof(WCHAR)) == Len || broken(!Len) /* nt4 */, "Got wrong length %lx\n", Len); if (!Len) pNtAddAtomNT4 = (void *)pNtAddAtom; res = pRtlPinAtomInAtomTable(AtomTable, Atom1); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); - ok(!res, "Unable to find atom in atom table, retval: %x\n", res); + ok(!res, "Unable to find atom in atom table, retval: %lx\n", res); ok(testAtom == Atom1, "Wrong atom found\n"); res = pRtlDeleteAtomFromAtomTable(AtomTable, Atom1); - ok(res == STATUS_WAS_LOCKED, "Unable to delete atom from table, retval: %x\n", res); + ok(res == STATUS_WAS_LOCKED, "Unable to delete atom from table, retval: %lx\n", res); res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom); ok(!res, "Able to find deleted atom in table\n"); @@ -303,82 +305,82 @@ static void test_NtIntAtom(void) AtomTable = NULL; res = pRtlCreateAtomTable(37, &AtomTable); - ok(!res, "Unable to create atom table, %x\n", res); + ok(!res, "Unable to create atom table, %lx\n", res); if (!res) { /* According to the kernel32 functions, integer atoms are only allowed from * 0x0001 to 0xbfff and not 0xc000 to 0xffff, which is correct */ res = pRtlAddAtomToAtomTable(AtomTable, NULL, &testAtom); - ok(res == STATUS_INVALID_PARAMETER, "Didn't get expected result from adding 0 int atom, retval: %x\n", res); + ok(res == STATUS_INVALID_PARAMETER, "Didn't get expected result from adding 0 int atom, retval: %lx\n", res); for (i = 1; i <= 0xbfff; i++) { res = pRtlAddAtomToAtomTable(AtomTable, (LPWSTR)i, &testAtom); - ok(!res, "Unable to add valid integer atom %li, retval: %x\n", i, res); + ok(!res, "Unable to add valid integer atom %Ii, retval: %lx\n", i, res); } for (i = 1; i <= 0xbfff; i++) { res = pRtlLookupAtomInAtomTable(AtomTable, (LPWSTR)i, &testAtom); - ok(!res, "Unable to find int atom %li, retval: %x\n", i, res); + ok(!res, "Unable to find int atom %Ii, retval: %lx\n", i, res); if (!res) { res = pRtlPinAtomInAtomTable(AtomTable, testAtom); - ok(!res, "Unable to pin int atom %li, retval: %x\n", i, res); + ok(!res, "Unable to pin int atom %Ii, retval: %lx\n", i, res); } } for (i = 0xc000; i <= 0xffff; i++) { res = pRtlAddAtomToAtomTable(AtomTable, (LPWSTR)i, &testAtom); - ok(res, "Able to illeageal integer atom %li, retval: %x\n", i, res); + ok(res, "Able to illeageal integer atom %Ii, retval: %lx\n", i, res); } res = pRtlDestroyAtomTable(AtomTable); - ok(!res, "Unable to destroy atom table, retval: %x\n", res); + ok(!res, "Unable to destroy atom table, retval: %lx\n", res); } AtomTable = NULL; res = pRtlCreateAtomTable(37, &AtomTable); - ok(!res, "Unable to create atom table, %x\n", res); + ok(!res, "Unable to create atom table, %lx\n", res); if (!res) { res = pRtlLookupAtomInAtomTable(AtomTable, (PWSTR)123, &testAtom); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtomInt, &testAtom); - ok(!res, "Unable to add int atom to table, retval: %x\n", res); + ok(!res, "Unable to add int atom to table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtomIntInv, &testAtom); - ok(!res, "Unable to add int atom to table, retval: %x\n", res); + ok(!res, "Unable to add int atom to table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)123, &testAtom); - ok(!res, "Unable to add int atom to table, retval: %x\n", res); + ok(!res, "Unable to add int atom to table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)123, &testAtom); - ok(!res, "Unable to re-add int atom to table, retval: %x\n", res); + ok(!res, "Unable to re-add int atom to table, retval: %lx\n", res); Len = 64; res = pRtlQueryAtomInAtomTable(AtomTable, testAtom, &RefCount, &PinCount, Name, &Len); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); - ok(PinCount == 1, "Expected pincount 1 but got %x\n", PinCount); - ok(RefCount == 1, "Expected refcount 1 but got %x\n", RefCount); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); + ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount); + ok(RefCount == 1, "Expected refcount 1 but got %lx\n", RefCount); ok(!lstrcmpW(testAtomOTT, Name), "Got wrong atom name\n"); - ok((lstrlenW(testAtomOTT) * sizeof(WCHAR)) == Len, "Got wrong len %d\n", Len); + ok((lstrlenW(testAtomOTT) * sizeof(WCHAR)) == Len, "Got wrong len %ld\n", Len); res = pRtlPinAtomInAtomTable(AtomTable, testAtom); - ok(!res, "Unable to pin int atom, retval: %x\n", res); + ok(!res, "Unable to pin int atom, retval: %lx\n", res); res = pRtlPinAtomInAtomTable(AtomTable, testAtom); - ok(!res, "Unable to pin int atom, retval: %x\n", res); + ok(!res, "Unable to pin int atom, retval: %lx\n", res); res = pRtlQueryAtomInAtomTable(AtomTable, testAtom, &RefCount, &PinCount, NULL, NULL); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); - ok(PinCount == 1, "Expected pincount 1 but got %x\n", PinCount); - ok(RefCount == 1, "Expected refcount 1 but got %x\n", RefCount); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); + ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount); + ok(RefCount == 1, "Expected refcount 1 but got %lx\n", RefCount); res = pRtlDestroyAtomTable(AtomTable); - ok(!res, "Unable to destroy atom table, retval: %x\n", res); + ok(!res, "Unable to destroy atom table, retval: %lx\n", res); } } @@ -392,40 +394,40 @@ static void test_NtRefPinAtom(void) AtomTable = NULL; res = pRtlCreateAtomTable(37, &AtomTable); - ok(!res, "Unable to create atom table, %x\n", res); + ok(!res, "Unable to create atom table, %lx\n", res); if (!res) { res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom); - ok(!res, "Unable to add our atom to the atom table, retval: %x\n", res); + ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom); - ok(!res, "Unable to add our atom to the atom table, retval: %x\n", res); + ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res); res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom); - ok(!res, "Unable to add our atom to the atom table, retval: %x\n", res); + ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res); res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, NULL, NULL); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); - ok(PinCount == 0, "Expected pincount 0 but got %x\n", PinCount); - ok(RefCount == 3, "Expected refcount 3 but got %x\n", RefCount); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); + ok(PinCount == 0, "Expected pincount 0 but got %lx\n", PinCount); + ok(RefCount == 3, "Expected refcount 3 but got %lx\n", RefCount); res = pRtlPinAtomInAtomTable(AtomTable, Atom); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); res = pRtlPinAtomInAtomTable(AtomTable, Atom); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); res = pRtlPinAtomInAtomTable(AtomTable, Atom); - ok(!res, "Unable to pin atom in atom table, retval: %x\n", res); + ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res); res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, NULL, NULL); - ok(!res, "Unable to query atom in atom table, retval: %x\n", res); - ok(PinCount == 1, "Expected pincount 1 but got %x\n", PinCount); - ok(RefCount == 3, "Expected refcount 3 but got %x\n", RefCount); + ok(!res, "Unable to query atom in atom table, retval: %lx\n", res); + ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount); + ok(RefCount == 3, "Expected refcount 3 but got %lx\n", RefCount); res = pRtlDestroyAtomTable(AtomTable); - ok(!res, "Unable to destroy atom table, retval: %x\n", res); + ok(!res, "Unable to destroy atom table, retval: %lx\n", res); } } @@ -442,7 +444,7 @@ static void test_Global(void) else res = pNtAddAtom(testAtom1, lstrlenW(testAtom1) * sizeof(WCHAR), &atom); - ok(!res, "Added atom (%x)\n", res); + ok(!res, "Added atom (%lx)\n", res); memset( ptr, 0xcc, sizeof(ptr) ); res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); @@ -454,14 +456,14 @@ static void test_Global(void) ptr_size = sizeof(ATOM_BASIC_INFORMATION); res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); - ok(res == STATUS_BUFFER_TOO_SMALL, "wrong return status (%x)\n", res); + ok(res == STATUS_BUFFER_TOO_SMALL, "wrong return status (%lx)\n", res); ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR) || broken(abi->NameLength == sizeof(WCHAR)), /* nt4 */ "string length %u\n",abi->NameLength); memset( ptr, 0xcc, sizeof(ptr) ); ptr_size = sizeof(ATOM_BASIC_INFORMATION) + lstrlenW(testAtom1) * sizeof(WCHAR); res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL ); - ok(!res, "atom lookup %x\n", res); + ok(!res, "atom lookup %lx\n", res); ok(!lstrcmpW(abi->Name, testAtom1), "strings don't match\n"); ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n"); ok(abi->Name[lstrlenW(testAtom1)] == 0, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1)]); diff --git a/modules/rostests/winetests/ntdll/change.c b/modules/rostests/winetests/ntdll/change.c index 0e76fb5635a..f738196e31f 100644 --- a/modules/rostests/winetests/ntdll/change.c +++ b/modules/rostests/winetests/ntdll/change.c @@ -86,15 +86,15 @@ static void test_ntncdf(void) filter |= FILE_NOTIFY_CHANGE_CREATION; filter |= FILE_NOTIFY_CHANGE_SECURITY; - U(iosb).Status = 1; + iosb.Status = 1; iosb.Information = 1; r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,-1,0); ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n"); - ok( U(iosb).Status == 1, "information wrong\n"); + ok( iosb.Status == 1, "information wrong\n"); ok( iosb.Information == 1, "information wrong\n"); - U(iosb).Status = 1; + iosb.Status = 1; iosb.Information = 0; r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0); ok(r==STATUS_PENDING, "should return status pending\n"); @@ -114,7 +114,7 @@ static void test_ntncdf(void) r = WaitForSingleObject( hEvent, 100 ); ok( r == WAIT_OBJECT_0, "event should be ready\n" ); - ok( U(iosb).Status == STATUS_SUCCESS, "information wrong\n"); + ok( iosb.Status == STATUS_SUCCESS, "information wrong\n"); ok( iosb.Information == 0x12, "information wrong\n"); pfni = (PFILE_NOTIFY_INFORMATION) buffer; @@ -131,12 +131,12 @@ static void test_ntncdf(void) filter = FILE_NOTIFY_CHANGE_SIZE; - U(iosb).Status = 1; + iosb.Status = 1; iosb.Information = 1; r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0); ok(r==STATUS_PENDING, "should status pending\n"); - ok( U(iosb).Status == 1, "information wrong\n"); + ok( iosb.Status == 1, "information wrong\n"); ok( iosb.Information == 1, "information wrong\n"); r = WaitForSingleObject( hdir, 0 ); @@ -151,7 +151,7 @@ static void test_ntncdf(void) r = WaitForSingleObject( hdir, 100 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok( U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "information wrong\n"); + ok( iosb.Status == STATUS_NOTIFY_ENUM_DIR, "information wrong\n"); ok( iosb.Information == 0, "information wrong\n"); CloseHandle(hdir); @@ -168,7 +168,7 @@ static void test_ntncdf_async(void) HANDLE hdir, hEvent; char buffer[0x1000]; DWORD fflags, filter = 0; - IO_STATUS_BLOCK iosb, iosb2; + IO_STATUS_BLOCK iosb, iosb2, iosb3; WCHAR path[MAX_PATH], subdir[MAX_PATH]; static const WCHAR szBoo[] = { '\\','b','o','o',0 }; static const WCHAR szHoo[] = { '\\','h','o','o',0 }; @@ -209,11 +209,11 @@ static void test_ntncdf_async(void) filter |= FILE_NOTIFY_CHANGE_SECURITY; - U(iosb).Status = 0x01234567; + iosb.Status = 0x01234567; iosb.Information = 0x12345678; r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0); ok(r==STATUS_PENDING, "should status pending\n"); - ok(U(iosb).Status == 0x01234567, "status set too soon\n"); + ok(iosb.Status == 0x01234567, "status set too soon\n"); ok(iosb.Information == 0x12345678, "info set too soon\n"); r = CreateDirectoryW( subdir, NULL ); @@ -222,7 +222,7 @@ static void test_ntncdf_async(void) r = WaitForSingleObject( hdir, 100 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n"); + ok(iosb.Status == STATUS_SUCCESS, "status not successful\n"); ok(iosb.Information == 0x12, "info not set\n"); pfni = (PFILE_NOTIFY_INFORMATION) buffer; @@ -240,7 +240,7 @@ static void test_ntncdf_async(void) r = WaitForSingleObject( hdir, 0 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n"); + ok(iosb.Status == STATUS_SUCCESS, "status not successful\n"); ok(iosb.Information == 0x12, "info not set\n"); ok( pfni->NextEntryOffset == 0, "offset wrong\n" ); @@ -249,7 +249,7 @@ static void test_ntncdf_async(void) ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" ); /* check APCs */ - U(iosb).Status = 0; + iosb.Status = 0; iosb.Information = 0; r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0); @@ -261,10 +261,10 @@ static void test_ntncdf_async(void) r = WaitForSingleObject( hdir, 0 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok(U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "status not successful\n"); + ok(iosb.Status == STATUS_NOTIFY_ENUM_DIR, "status not successful\n"); ok(iosb.Information == 0, "info not set\n"); - U(iosb).Status = 0; + iosb.Status = 0; iosb.Information = 0; r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0); @@ -276,33 +276,45 @@ static void test_ntncdf_async(void) r = WaitForSingleObject( hEvent, 0 ); ok( r == WAIT_OBJECT_0, "should be ready\n" ); - ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n"); + ok(iosb.Status == STATUS_SUCCESS, "status not successful\n"); ok(iosb.Information == 0x12, "info not set\n"); - U(iosb).Status = 0x01234567; + iosb.Status = 0x01234567; iosb.Information = 0x12345678; r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0); ok(r==STATUS_PENDING, "should status pending\n"); - U(iosb2).Status = 0x01234567; + iosb2.Status = 0x01234567; iosb2.Information = 0x12345678; r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb2,buffer,sizeof buffer,filter,0); ok(r==STATUS_PENDING, "should status pending\n"); - ok(U(iosb).Status == 0x01234567, "status set too soon\n"); + ok(iosb.Status == 0x01234567, "status set too soon\n"); ok(iosb.Information == 0x12345678, "info set too soon\n"); - r = pNtCancelIoFile(hdir, &iosb); + iosb3.Status = 0x111111; + iosb3.Information = 0x222222; + + r = pNtCancelIoFile(hdir, &iosb3); ok( r == STATUS_SUCCESS, "cancel failed\n"); CloseHandle(hdir); - ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n"); - ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong %x\n",U(iosb2).Status); + ok(iosb.Status == STATUS_CANCELLED, "status wrong %lx\n",iosb.Status); + ok(iosb2.Status == STATUS_CANCELLED, "status wrong %lx\n",iosb2.Status); + ok(iosb3.Status == STATUS_SUCCESS, "status wrong %lx\n",iosb3.Status); ok(iosb.Information == 0, "info wrong\n"); ok(iosb2.Information == 0, "info wrong\n"); + ok(iosb3.Information == 0, "info wrong\n"); + + iosb3.Status = 0x111111; + iosb3.Information = 0x222222; + r = pNtCancelIoFile(hdir, &iosb3); + ok( r == STATUS_INVALID_HANDLE, "cancel failed %lx\n", r); + ok(iosb3.Status == 0x111111, "status wrong %lx\n",iosb3.Status); + ok(iosb3.Information == 0x222222, "info wrong\n"); r = RemoveDirectoryW( path ); ok( r == TRUE, "failed to remove directory\n"); @@ -313,11 +325,6 @@ static void test_ntncdf_async(void) START_TEST(change) { HMODULE hntdll = GetModuleHandleA("ntdll"); - if (!hntdll) - { - win_skip("not running on NT, skipping test\n"); - return; - } pNtNotifyChangeDirectoryFile = (void *)GetProcAddress(hntdll, "NtNotifyChangeDirectoryFile"); pNtCancelIoFile = (void *)GetProcAddress(hntdll, "NtCancelIoFile"); diff --git a/modules/rostests/winetests/ntdll/directory.c b/modules/rostests/winetests/ntdll/directory.c index 5e458a4f129..52ff9936560 100644 --- a/modules/rostests/winetests/ntdll/directory.c +++ b/modules/rostests/winetests/ntdll/directory.c @@ -70,11 +70,19 @@ static struct testfile_s { { 0, FILE_ATTRIBUTE_NORMAL, {0xe9,'a','.','t','m','p'}, "normal" }, { 0, FILE_ATTRIBUTE_NORMAL, {0xc9,'b','.','t','m','p'}, "normal" }, { 0, FILE_ATTRIBUTE_NORMAL, {'e','a','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'e','a'}, "normal" }, { 0, FILE_ATTRIBUTE_DIRECTORY, {'.'}, ". directory" }, - { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'}, ".. directory" } + { 0, FILE_ATTRIBUTE_DIRECTORY, {'.','.'}, ".. directory" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'e','a','.','t','m','p','.','t','m','p'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'.','a'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'.','a','.','a'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'a','.'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'.','.','a'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'.','a','a'}, "normal" }, + { 0, FILE_ATTRIBUTE_NORMAL, {'a','.', '.'}, "normal" }, }; -static const int test_dir_count = sizeof(testfiles) / sizeof(testfiles[0]); -static const int max_test_dir_size = sizeof(testfiles) / sizeof(testfiles[0]) + 5; /* size of above plus some for .. etc */ +static const int test_dir_count = ARRAY_SIZE(testfiles); +static const int max_test_dir_size = ARRAY_SIZE(testfiles) + 5; /* size of above plus some for .. etc */ static const WCHAR dummyW[] = {'d','u','m','m','y',0}; static const WCHAR dotW[] = {'.',0}; @@ -88,19 +96,20 @@ static void set_up_attribute_test(const WCHAR *testdir) BOOL ret; ret = CreateDirectoryW(testdir, NULL); - ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(testdir), GetLastError()); + ok(ret, "couldn't create dir %s, error %ld\n", wine_dbgstr_w(testdir), GetLastError()); for (i=0; i < test_dir_count; i++) { WCHAR buf[MAX_PATH]; if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0) continue; - lstrcpyW( buf, testdir ); + lstrcpyW( buf, L"\\\\?\\" ); + lstrcatW( buf, testdir ); lstrcatW( buf, backslashW ); lstrcatW( buf, testfiles[i].name ); if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) { ret = CreateDirectoryW(buf, NULL); - ok(ret, "couldn't create dir %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); + ok(ret, "couldn't create dir %s, error %ld\n", wine_dbgstr_w(buf), GetLastError()); } else { HANDLE h = CreateFileW(buf, GENERIC_READ|GENERIC_WRITE, @@ -130,17 +139,18 @@ static void tear_down_attribute_test(const WCHAR *testdir) WCHAR buf[MAX_PATH]; if (lstrcmpW(testfiles[i].name, dotW) == 0 || lstrcmpW(testfiles[i].name, dotdotW) == 0) continue; - lstrcpyW( buf, testdir ); + lstrcpyW( buf, L"\\\\?\\" ); + lstrcatW( buf, testdir ); lstrcatW( buf, backslashW ); lstrcatW( buf, testfiles[i].name ); if (testfiles[i].attr & FILE_ATTRIBUTE_DIRECTORY) { ret = RemoveDirectoryW(buf); ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND), - "Failed to rmdir %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); + "Failed to rmdir %s, error %ld\n", wine_dbgstr_w(buf), GetLastError()); } else { ret = DeleteFileW(buf); ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND), - "Failed to rm %s, error %d\n", wine_dbgstr_w(buf), GetLastError()); + "Failed to rm %s, error %ld\n", wine_dbgstr_w(buf), GetLastError()); } } RemoveDirectoryW(testdir); @@ -161,7 +171,8 @@ static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info) if (namelen != len || memcmp(nameW, testfiles[i].name, len*sizeof(WCHAR))) continue; if (!testfiles[i].attr_done) { - ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", wine_dbgstr_w(testfiles[i].name), testfiles[i].description, testfiles[i].attr, attrib); + ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%lx), got %lx\n", + wine_dbgstr_w(testfiles[i].name), testfiles[i].description, testfiles[i].attr, attrib); testfiles[i].attr_done = TRUE; } testfiles[i].nfound++; @@ -172,7 +183,7 @@ static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info) static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA, UNICODE_STRING *mask, - BOOLEAN single_entry, BOOLEAN restart_flag) + BOOLEAN single_entry, BOOLEAN restart_flag, BOOLEAN expect_empty) { UNICODE_STRING dummy_mask; HANDLE dirh, new_dirh; @@ -193,17 +204,23 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char /* Read the directory and note which files are found */ status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE); - ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdirA, status, GetLastError()); + ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%lx, error %ld\n", testdirA, status, GetLastError()); if (status != STATUS_SUCCESS) { skip("can't test if we can't open the directory\n"); return; } - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, single_entry, mask, restart_flag ); - ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); - ok (U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status); + if (expect_empty) + { + ok( status == STATUS_NO_SUCH_FILE, "got %#lx.\n", status ); + pNtClose( dirh ); + return; + } + ok (status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status); + ok (io.Status == STATUS_SUCCESS, "failed to query directory; status %lx\n", io.Status); data_len = io.Information; ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n"); @@ -219,12 +236,12 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char tally_test_file(dir_info); if (dir_info->NextEntryOffset == 0) { - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( new_dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, single_entry, &dummy_mask, FALSE ); - ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status); + ok (io.Status == status, "wrong status %lx / %lx\n", status, io.Status); if (status == STATUS_NO_MORE_FILES) break; - ok (status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); + ok (status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status); data_len = io.Information; if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION)) break; @@ -236,13 +253,13 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char } ok(numfiles < max_test_dir_size, "too many loops\n"); - if (mask) + if (mask && !wcspbrk( mask->Buffer, L"*?<\">" )) for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer), "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n", testfiles[i].nfound, testfiles[i].description, single_entry, wine_dbgstr_wn(mask->Buffer, mask->Length/sizeof(WCHAR) )); - else + else if (!mask) for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (single_entry=%d,restart=%d)\n", testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag); @@ -273,11 +290,11 @@ static void test_directory_sort( const WCHAR *testdir ) FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE ); ok(status == STATUS_SUCCESS, "failed to open dir %s\n", wine_dbgstr_w(testdir) ); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, data, sizeof(data), FileBothDirectoryInformation, FALSE, NULL, TRUE ); - ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status ); - ok( U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status ); + ok( status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status ); + ok( io.Status == STATUS_SUCCESS, "failed to query directory; status %lx\n", io.Status ); data_len = io.Information; ok( data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n" ); data_pos = 0; @@ -311,12 +328,12 @@ static void test_directory_sort( const WCHAR *testdir ) if (info->NextEntryOffset == 0) { - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, sizeof(data), FileBothDirectoryInformation, FALSE, NULL, FALSE ); - ok (U(io).Status == status, "wrong status %x / %x\n", status, U(io).Status); + ok (io.Status == status, "wrong status %lx / %lx\n", status, io.Status); if (status == STATUS_NO_MORE_FILES) break; - ok( status == STATUS_SUCCESS, "failed to query directory; status %x\n", status ); + ok( status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status ); data_len = io.Information; data_pos = 0; } @@ -337,16 +354,16 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma for (class = 0; class < FileMaximumInformation; class++) { - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; data_size = 0; memset( data, 0x55, sizeof(data) ); status = pNtQueryDirectoryFile( handle, 0, NULL, NULL, &io, data, data_size, class, FALSE, mask, TRUE ); - ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); - ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); - ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + ok( io.Status == 0xdeadbeef, "%u: wrong status %lx\n", class, io.Status ); + ok( io.Information == 0xdeadbeef, "%u: wrong info %Ix\n", class, io.Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %lx\n", class, data[0] ); switch (class) { @@ -364,11 +381,11 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma case FileObjectIdInformation: case FileQuotaInformation: case FileReparsePointInformation: - ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "%u: wrong status %lx\n", class, status ); break; default: ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED, - "%u: wrong status %x\n", class, status ); + "%u: wrong status %lx\n", class, status ); continue; } @@ -378,15 +395,16 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma class, FALSE, mask, TRUE ); if (status == STATUS_BUFFER_OVERFLOW) { - ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, U(io).Status ); - ok( U(io).Information == data_size, "%u: wrong info %lx\n", class, U(io).Information ); - ok(data[0] == 0, "%u: wrong offset %x\n", class, data[0] ); + ok( io.Status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, io.Status ); + ok( io.Information == data_size || broken(!io.Information), /* win10 1709 */ + "%u: wrong info %Ix\n", class, io.Information ); + if (io.Information) ok(data[0] == 0, "%u: wrong offset %lx\n", class, data[0] ); } else { - ok( U(io).Status == 0xdeadbeef, "%u: wrong status %x\n", class, U(io).Status ); - ok( U(io).Information == 0xdeadbeef, "%u: wrong info %lx\n", class, U(io).Information ); - ok(data[0] == 0x55555555, "%u: wrong offset %x\n", class, data[0] ); + ok( io.Status == 0xdeadbeef || io.Status == status, "%u: wrong status %lx\n", class, io.Status ); + ok( io.Information == (io.Status == 0xdeadbeef ? 0xdeadbeef : 0), "%u: wrong info %Ix\n", class, io.Information ); + ok(data[0] == 0x55555555, "%u: wrong offset %lx\n", class, data[0] ); } if (status != STATUS_INFO_LENGTH_MISMATCH) break; } @@ -394,50 +412,50 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma switch (class) { case FileDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileFullDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileBothDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileNamesInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileIdBothDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_ID_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileIdFullDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_ID_FULL_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileIdGlobalTxDirectoryInformation: - ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_BUFFER_OVERFLOW, "%u: wrong status %lx\n", class, status ); ok( data_size == ((offsetof( FILE_ID_GLOBAL_TX_DIR_INFORMATION, FileName[1] ) + 7) & ~7), "%u: wrong size %u\n", class, data_size ); break; case FileObjectIdInformation: - ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %lx\n", class, status ); ok( data_size == sizeof(FILE_OBJECTID_INFORMATION), "%u: wrong size %u\n", class, data_size ); break; case FileQuotaInformation: - ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %lx\n", class, status ); ok( data_size == sizeof(FILE_QUOTA_INFORMATION), "%u: wrong size %u\n", class, data_size ); break; case FileReparsePointInformation: - ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %x\n", class, status ); + ok( status == STATUS_INVALID_INFO_CLASS, "%u: wrong status %lx\n", class, status ); ok( data_size == sizeof(FILE_REPARSE_POINT_INFORMATION), "%u: wrong size %u\n", class, data_size ); break; } @@ -446,11 +464,97 @@ static void test_NtQueryDirectoryFile_classes( HANDLE handle, UNICODE_STRING *ma static void test_NtQueryDirectoryFile(void) { + static const struct + { + const WCHAR *mask; + int found[ARRAY_SIZE(testfiles)]; + } + mask_tests[] = + { + {L"*.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1}}, + {L"*. ", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"* .", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L" *.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"*.*", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"* *", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"*.**", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"*", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"**", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"?", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}}, + {L"?.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}, + {L"?..", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {L"??", {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0}}, + {L"??.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {L"??.???", {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<", {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1}}, + {L"*", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}}, + {L">.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0}}, + {L">..", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {L">>", {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0}}, + {L">>.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0}}, + {L">>>", {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0}}, + {L">>.>>>", {0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1}}, + {L">.>", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1}}, + {L">>.tmp", {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L">>tmp", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L">>>tmp", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + }; + OBJECT_ATTRIBUTES attr; UNICODE_STRING ntdirname, mask; - char testdirA[MAX_PATH]; + char testdirA[MAX_PATH], buffer[MAX_PATH]; WCHAR testdirW[MAX_PATH]; - int i; + int i, j; IO_STATUS_BLOCK io; WCHAR short_name[12]; UINT data_size; @@ -459,8 +563,9 @@ static void test_NtQueryDirectoryFile(void) FILE_POSITION_INFORMATION pos_info; FILE_NAMES_INFORMATION *names; const WCHAR *filename = fbdi->FileName; + BOOLEAN expect_empty; NTSTATUS status; - HANDLE dirh; + HANDLE dirh, h; /* Clean up from prior aborted run, if any, then set up test files */ ok(GetTempPathA(MAX_PATH, testdirA), "couldn't get temp dir\n"); @@ -476,20 +581,39 @@ static void test_NtQueryDirectoryFile(void) } InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, FALSE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, FALSE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE, FALSE); for (i = 0; i < test_dir_count; i++) { if (testfiles[i].name[0] == '.') continue; /* . and .. as masks are broken on Windows */ mask.Buffer = testfiles[i].name; mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].name) * sizeof(WCHAR); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE, FALSE); + } + + for (i = 0; i < ARRAY_SIZE(mask_tests); ++i) + { + winetest_push_context("mask %s", debugstr_w(mask_tests[i].mask)); + RtlInitUnicodeString(&mask, mask_tests[i].mask); + expect_empty = TRUE; + for (j = 0; j < ARRAY_SIZE(mask_tests[i].found); ++j) + { + if (mask_tests[i].found[j]) + { + expect_empty = FALSE; + break; + } + } + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE, expect_empty); + for (j = 0; j < test_dir_count; j++) + ok(testfiles[j].nfound == mask_tests[i].found[j], "%S, got %d.\n", testfiles[j].name, testfiles[j].nfound); + winetest_pop_context(); } /* short path passed as mask */ @@ -501,84 +625,88 @@ static void test_NtQueryDirectoryFile(void) return; } status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); - ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", status ); ok( pos_info.CurrentByteOffset.QuadPart == 0, "wrong pos %s\n", wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart)); pos_info.CurrentByteOffset.QuadPart = 0xbeef; status = pNtSetInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); - ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", status ); status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); - ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", status ); ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n", wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart)); mask.Buffer = testfiles[0].name; mask.Length = mask.MaximumLength = lstrlenW(testfiles[0].name) * sizeof(WCHAR); data_size = offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[256]); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, FALSE); - ok(status == STATUS_SUCCESS, "failed to query directory; status %x\n", status); - ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status); + ok(status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "failed to query directory; status %lx\n", io.Status); ok(fbdi->ShortName[0], "ShortName is empty\n"); status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); - ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", status ); ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n", wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) ); mask.Length = mask.MaximumLength = fbdi->ShortNameLength; memcpy(short_name, fbdi->ShortName, mask.Length); mask.Buffer = short_name; - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, TRUE); - ok(status == STATUS_SUCCESS, "failed to query directory status %x\n", status); - ok(U(io).Status == STATUS_SUCCESS, "failed to query directory status %x\n", U(io).Status); - ok(U(io).Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[lstrlenW(testfiles[0].name)]), - "wrong info %lx\n", U(io).Information); + ok(status == STATUS_SUCCESS, "failed to query directory status %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "failed to query directory status %lx\n", io.Status); + ok(io.Information == offsetof(FILE_BOTH_DIRECTORY_INFORMATION, FileName[lstrlenW(testfiles[0].name)]), + "wrong info %Ix\n", io.Information); ok(fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) && !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength), "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); status = pNtQueryInformationFile( dirh, &io, &pos_info, sizeof(pos_info), FilePositionInformation ); - ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", status ); ok( pos_info.CurrentByteOffset.QuadPart == 0xbeef, "wrong pos %s\n", wine_dbgstr_longlong(pos_info.CurrentByteOffset.QuadPart) ); /* tests with short buffer */ memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ); status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, TRUE); - ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status ); - ok( U(io).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == data_size || broken( U(io).Information == 0), - "wrong info %lx\n", U(io).Information ); - ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); - ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name) * sizeof(WCHAR), - "wrong length %x\n", fbdi->FileNameLength ); - ok( filename[0] == testfiles[0].name[0], "incorrect long file name: %s\n", - wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); - ok( filename[1] == 0x5555, "incorrect long file name: %s\n", - wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %lx\n", status ); + ok( io.Status == STATUS_BUFFER_OVERFLOW, "wrong status %lx\n", io.Status ); + ok( io.Information == data_size || broken( io.Information == 0), + "wrong info %Ix\n", io.Information ); + ok( fbdi->NextEntryOffset == 0 || fbdi->NextEntryOffset == 0x55555555, /* win10 >= 1709 */ + "wrong offset %lx\n", fbdi->NextEntryOffset ); + if (!fbdi->NextEntryOffset) + { + ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name) * sizeof(WCHAR), + "wrong length %lx\n", fbdi->FileNameLength ); + ok( filename[0] == testfiles[0].name[0], "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + ok( filename[1] == 0x5555, "incorrect long file name: %s\n", + wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); + } test_NtQueryDirectoryFile_classes( dirh, &mask ); /* mask may or may not be ignored when restarting the search */ pRtlInitUnicodeString( &mask, dummyW ); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; data_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, TRUE); - ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %x\n", status ); - ok( U(io).Status == status, "wrong status %x / %x\n", U(io).Status, status ); + ok( status == STATUS_SUCCESS || status == STATUS_NO_MORE_FILES, "wrong status %lx\n", status ); + ok( io.Status == status, "wrong status %lx / %lx\n", io.Status, status ); if (!status) ok( fbdi->FileNameLength == lstrlenW(testfiles[0].name)*sizeof(WCHAR) && !memcmp(fbdi->FileName, testfiles[0].name, fbdi->FileNameLength), @@ -593,73 +721,74 @@ static void test_NtQueryDirectoryFile(void) memset( data, 0x55, data_size ); data_size = sizeof(data); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, FALSE, NULL, TRUE); - ok(status == STATUS_SUCCESS, "wrong status %x\n", status); - ok(U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status); - ok(U(io).Information > 0 && U(io).Information < data_size, "wrong info %lx\n", U(io).Information); + ok(status == STATUS_SUCCESS, "wrong status %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "wrong status %lx\n", io.Status); + ok(io.Information > 0 && io.Information < data_size, "wrong info %Ix\n", io.Information); ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), - "wrong offset %x\n", fbdi->NextEntryOffset ); - ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + "wrong offset %lx\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %lx\n", fbdi->FileNameLength ); ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset); ok( next->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ) + 7) & ~7), - "wrong offset %x\n", next->NextEntryOffset ); - ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength ); + "wrong offset %lx\n", next->NextEntryOffset ); + ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %lx\n", next->FileNameLength ); filename = next->FileName; ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR))); - data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ); memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, FALSE, NULL, TRUE ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), - "wrong info %lx\n", U(io).Information ); - ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); - ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( io.Status == STATUS_SUCCESS, "wrong status %lx\n", io.Status ); + ok( io.Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + "wrong info %Ix\n", io.Information ); + ok( fbdi->NextEntryOffset == 0, "wrong offset %lx\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %lx\n", fbdi->FileNameLength ); ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); next = (FILE_BOTH_DIRECTORY_INFORMATION *)&fbdi->FileName[1]; - ok( next->NextEntryOffset == 0x55555555, "wrong offset %x\n", next->NextEntryOffset ); + ok( next->NextEntryOffset == 0x55555555, "wrong offset %lx\n", next->NextEntryOffset ); - data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ), + data_size = fbdi->NextEntryOffset + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ); memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, FALSE, NULL, TRUE ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), - "wrong info %lx\n", U(io).Information ); - ok( fbdi->NextEntryOffset == 0, "wrong offset %x\n", fbdi->NextEntryOffset ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( io.Status == STATUS_SUCCESS, "wrong status %lx\n", io.Status ); + ok( io.Information == offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ), + "wrong info %Ix\n", io.Information ); + ok( fbdi->NextEntryOffset == 0, "wrong offset %lx\n", fbdi->NextEntryOffset ); data_size = ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7) + offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[2] ); memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; - status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; + status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size + 32, FileBothDirectoryInformation, FALSE, NULL, TRUE ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( io.Status == STATUS_SUCCESS, "wrong status %lx\n", io.Status ); + ok( io.Information == data_size || io.Information == ((data_size + 7) & ~7), + "wrong info %Ix / %x\n", io.Information, data_size ); ok( fbdi->NextEntryOffset == ((offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[1] ) + 7) & ~7), - "wrong offset %x\n", fbdi->NextEntryOffset ); - ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %x\n", fbdi->FileNameLength ); + "wrong offset %lx\n", fbdi->NextEntryOffset ); + ok( fbdi->FileNameLength == sizeof(WCHAR), "wrong length %lx\n", fbdi->FileNameLength ); ok( fbdi->FileName[0] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(fbdi->FileName, fbdi->FileNameLength/sizeof(WCHAR))); next = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + fbdi->NextEntryOffset); - ok( next->NextEntryOffset == 0, "wrong offset %x\n", next->NextEntryOffset ); - ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", next->FileNameLength ); + ok( next->NextEntryOffset == 0, "wrong offset %lx\n", next->NextEntryOffset ); + ok( next->FileNameLength == 2 * sizeof(WCHAR), "wrong length %lx\n", next->FileNameLength ); filename = next->FileName; ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(next->FileName, next->FileNameLength/sizeof(WCHAR))); @@ -667,22 +796,22 @@ static void test_NtQueryDirectoryFile(void) data_size = ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7) + offsetof( FILE_NAMES_INFORMATION, FileName[2] ); memset( data, 0x55, data_size ); - U(io).Status = 0xdeadbeef; - U(io).Information = 0xdeadbeef; + io.Status = 0xdeadbeef; + io.Information = 0xdeadbeef; status = pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, data_size, FileNamesInformation, FALSE, NULL, TRUE ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(io).Status == STATUS_SUCCESS, "wrong status %x\n", U(io).Status ); - ok( U(io).Information == data_size, "wrong info %lx / %x\n", U(io).Information, data_size ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( io.Status == STATUS_SUCCESS, "wrong status %lx\n", io.Status ); + ok( io.Information == data_size, "wrong info %Ix / %x\n", io.Information, data_size ); names = (FILE_NAMES_INFORMATION *)data; ok( names->NextEntryOffset == ((offsetof( FILE_NAMES_INFORMATION, FileName[1] ) + 7) & ~7), - "wrong offset %x\n", names->NextEntryOffset ); - ok( names->FileNameLength == sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + "wrong offset %lx\n", names->NextEntryOffset ); + ok( names->FileNameLength == sizeof(WCHAR), "wrong length %lx\n", names->FileNameLength ); ok( names->FileName[0] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); names = (FILE_NAMES_INFORMATION *)(data + names->NextEntryOffset); - ok( names->NextEntryOffset == 0, "wrong offset %x\n", names->NextEntryOffset ); - ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %x\n", names->FileNameLength ); + ok( names->NextEntryOffset == 0, "wrong offset %lx\n", names->NextEntryOffset ); + ok( names->FileNameLength == 2 * sizeof(WCHAR), "wrong length %lx\n", names->FileNameLength ); filename = names->FileName; ok( filename[0] == '.' && filename[1] == '.', "incorrect long file name: %s\n", wine_dbgstr_wn(names->FileName, names->FileNameLength/sizeof(WCHAR))); @@ -695,32 +824,44 @@ static void test_NtQueryDirectoryFile(void) ok(status == STATUS_SUCCESS, "failed to open dir '%s'\n", testdirA); pRtlInitUnicodeString( &mask, dummyW ); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; data_size = sizeof(data); status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, TRUE); - ok(status == STATUS_NO_SUCH_FILE, "wrong status %x\n", status); - ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status); + ok(status == STATUS_NO_SUCH_FILE, "wrong status %lx\n", status); + ok(io.Status == 0xdeadbeef, "wrong status %lx\n", io.Status); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, NULL, FALSE); - ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); - ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + ok(status == STATUS_NO_MORE_FILES, "wrong status %lx\n", status); + ok(io.Status == STATUS_NO_MORE_FILES, "wrong status %lx\n", io.Status); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile(dirh, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, NULL, TRUE); - ok(status == STATUS_NO_MORE_FILES, "wrong status %x\n", status); - ok(U(io).Status == STATUS_NO_MORE_FILES, "wrong status %x\n", U(io).Status); + ok(status == STATUS_NO_MORE_FILES, "wrong status %lx\n", status); + ok(io.Status == STATUS_NO_MORE_FILES, "wrong status %lx\n", io.Status); pNtClose(dirh); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( (HANDLE)0xbeef, 0, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, NULL, TRUE ); - ok(status == STATUS_INVALID_HANDLE, "wrong status %x\n", status); - ok(U(io).Status == 0xdeadbeef, "wrong status %x\n", U(io).Status); + ok(status == STATUS_INVALID_HANDLE, "wrong status %lx\n", status); + ok(io.Status == 0xdeadbeef, "wrong status %lx\n", io.Status); + + GetModuleFileNameA( 0, buffer, sizeof(buffer) ); + h = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (h != INVALID_HANDLE_VALUE) + { + io.Status = 0xdeadbeef; + status = pNtQueryDirectoryFile( h, 0, NULL, NULL, &io, data, data_size, + FileBothDirectoryInformation, TRUE, NULL, TRUE ); + ok(status == STATUS_INVALID_PARAMETER, "wrong status %lx\n", status); + ok(io.Status == 0xdeadbeef, "wrong status %lx\n", io.Status); + CloseHandle ( h ); + } done: test_directory_sort( testdirW ); @@ -731,11 +872,11 @@ done: static void set_up_case_test(const char *testdir) { BOOL ret; - char buf[MAX_PATH]; + char buf[MAX_PATH + 5]; HANDLE h; ret = CreateDirectoryA(testdir, NULL); - ok(ret, "couldn't create dir '%s', error %d\n", testdir, GetLastError()); + ok(ret, "couldn't create dir '%s', error %ld\n", testdir, GetLastError()); sprintf(buf, "%s\\%s", testdir, "TesT"); h = CreateFileA(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, @@ -752,7 +893,7 @@ static void tear_down_case_test(const char *testdir) sprintf(buf, "%s\\%s", testdir, "TesT"); ret = DeleteFileA(buf); ok(ret || (GetLastError() == ERROR_PATH_NOT_FOUND), - "Failed to rm %s, error %d\n", buf, GetLastError()); + "Failed to rm %s, error %ld\n", buf, GetLastError()); RemoveDirectoryA(testdir); } @@ -794,7 +935,7 @@ static void test_NtQueryDirectoryFile_case(void) status = pNtOpenFile(&dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE); - ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", testdir, status, GetLastError()); + ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%lx, error %ld\n", testdir, status, GetLastError()); if (status != STATUS_SUCCESS) { skip("can't test if we can't open the directory\n"); @@ -805,14 +946,14 @@ static void test_NtQueryDirectoryFile_case(void) mask.Length = mask.MaximumLength = sizeof(testmask); pNtQueryDirectoryFile(dirh, NULL, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, TRUE, &mask, FALSE); - ok(U(io).Status == STATUS_SUCCESS, "failed to query directory; status %x\n", U(io).Status); + ok(io.Status == STATUS_SUCCESS, "failed to query directory; status %lx\n", io.Status); data_len = io.Information; ok(data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n"); name = dir_info->FileName; name_len = dir_info->FileNameLength / sizeof(WCHAR); - ok(name_len == testfile_len, "unexpected filename length %u\n", name_len); + ok(name_len == testfile_len, "unexpected filename length %lu\n", name_len); ok(!memcmp(name, testfile_w, testfile_len * sizeof(WCHAR)), "unexpected filename %s\n", wine_dbgstr_wn(name, name_len)); @@ -823,10 +964,45 @@ done: pRtlFreeUnicodeString(&ntdirname); } +static NTSTATUS get_file_id( FILE_INTERNAL_INFORMATION *info, const WCHAR *root, const WCHAR *name ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL ); + if (root) + { + RtlInitUnicodeString( &nameW, root ); + status = pNtOpenFile( &attr.RootDirectory, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, + FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | + FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE ); + if (status) return status; + } + if (name) + { + RtlInitUnicodeString( &nameW, name ); + status = pNtOpenFile( &handle, FILE_GENERIC_READ, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); + if (attr.RootDirectory) NtClose( attr.RootDirectory ); + } + else handle = attr.RootDirectory; + + if (!status) + { + status = pNtQueryInformationFile( handle, &io, info, sizeof(*info), FileInternalInformation ); + NtClose( handle ); + } + return status; +} + static void test_redirection(void) { ULONG old, cur; NTSTATUS status; + ULONGLONG *tls64 = NULL; if (!pRtlWow64EnableFsRedirection || !pRtlWow64EnableFsRedirectionEx) { @@ -839,37 +1015,122 @@ static void test_redirection(void) skip( "Wow64 redirection not supported\n" ); return; } - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + +#ifndef _WIN64 + if (NtCurrentTeb()->GdiBatchCount) + tls64 = ((TEB64 *)NtCurrentTeb()->GdiBatchCount)->TlsSlots + WOW64_TLS_FILESYSREDIR; +#endif status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); - ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( !cur, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == FALSE, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); - ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == TRUE, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); status = pRtlWow64EnableFsRedirection( TRUE ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); status = pRtlWow64EnableFsRedirectionEx( TRUE, &cur ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); - ok( !cur, "RtlWow64EnableFsRedirectionEx got %u\n", cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( !cur, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == TRUE, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); + + status = pRtlWow64EnableFsRedirectionEx( 123, &cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( cur == TRUE, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == 123, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); + + status = pRtlWow64EnableFsRedirectionEx( 0xdeadbeef, &cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( cur == 123, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == 0xdeadbeef, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); status = pRtlWow64EnableFsRedirectionEx( TRUE, NULL ); - ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %lx\n", status ); status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)1 ); - ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %lx\n", status ); status = pRtlWow64EnableFsRedirectionEx( TRUE, (void*)0xDEADBEEF ); - ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "RtlWow64EnableFsRedirectionEx failed with status %lx\n", status ); status = pRtlWow64EnableFsRedirection( FALSE ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); status = pRtlWow64EnableFsRedirectionEx( FALSE, &cur ); - ok( !status, "RtlWow64EnableFsRedirectionEx failed status %x\n", status ); - ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %u\n", cur ); + ok( !status, "RtlWow64EnableFsRedirectionEx failed status %lx\n", status ); + ok( cur == 1, "RtlWow64EnableFsRedirectionEx got %lu\n", cur ); + if (tls64) ok( *tls64 == FALSE, "wrong tls %s\n", wine_dbgstr_longlong(*tls64) ); + if (tls64) + { + static const struct + { + const WCHAR *root, *name; + NTSTATUS expect; + BOOL redirected; + NTSTATUS alt; + } tests[] = + { + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll", STATUS_SUCCESS, TRUE }, + { NULL, L"\\??\\C:\\\\windows\\system32\\kernel32.dll", STATUS_SUCCESS, FALSE, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\", L"windows\\system32\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\??\\C:\\windows", L"system32\\kernel32.dll", STATUS_SUCCESS, TRUE }, + { L"\\??\\C:\\\\windows", L"system32\\kernel32.dll", STATUS_SUCCESS, TRUE, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\system32", L"kernel32.dll", STATUS_SUCCESS, TRUE }, + { L"\\??\\C:\\windows\\system32", NULL, STATUS_SUCCESS, TRUE }, + { L"\\??\\C:\\windows\\system32", L"drivers\\ndis.sys", STATUS_OBJECT_NAME_NOT_FOUND, FALSE, STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows\\system32", L"drivers\\etc\\hosts", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows\\system32\\drivers", NULL, STATUS_SUCCESS, TRUE, STATUS_OBJECT_NAME_NOT_FOUND }, + { L"\\??\\C:\\windows\\system32\\drivers\\etc", L"hosts", STATUS_SUCCESS, FALSE }, + { NULL, L"\\DosDevices\\C:\\windows\\system32\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\", L"windows\\system32\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\windows", L"system32\\kernel32.dll", STATUS_SUCCESS, TRUE }, + { L"\\DosDevices\\C:\\windows\\system32", L"kernel32.dll", STATUS_SUCCESS, TRUE }, + { L"\\DosDevices\\C:\\windows\\system32", NULL, STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\windows\\system32", L"drivers\\ndis.sys", STATUS_OBJECT_NAME_NOT_FOUND, FALSE, STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\DosDevices\\C:\\windows\\system32", L"drivers\\etc\\hosts", STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\windows\\system32\\drivers", NULL, STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\windows\\system32\\drivers\\etc", NULL, STATUS_SUCCESS, FALSE }, + { NULL, L"\\??\\C:\\windows\\sysnative\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\??\\C:\\", L"windows\\sysnative\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows", L"sysnative\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\??\\C:\\windows\\sysnative", L"kernel32.dll" , STATUS_SUCCESS, TRUE }, + { L"\\??\\C:\\windows\\sysnative", NULL, STATUS_SUCCESS, FALSE }, + { NULL, L"\\DosDevices\\C:\\windows\\sysnative\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\DosDevices\\C:\\", L"windows\\sysnative\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\DosDevices\\C:\\windows", L"sysnative\\kernel32.dll", STATUS_SUCCESS, FALSE }, + { L"\\DosDevices\\C:\\windows\\sysnative", L"kernel32.dll" , STATUS_OBJECT_NAME_NOT_FOUND }, + { L"\\DosDevices\\C:\\windows\\sysnative", NULL, STATUS_OBJECT_NAME_NOT_FOUND }, + }; + FILE_INTERNAL_INFORMATION info, info_redir; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + pRtlWow64EnableFsRedirection( FALSE ); + status = get_file_id( &info, tests[i].root, tests[i].name ); + ok( !status || status == tests[i].expect || (tests[i].alt && status == tests[i].alt), + "%u: got %lx / %lx for %s + %s without redirect\n", i, status, tests[i].expect, + debugstr_w( tests[i].root ), debugstr_w( tests[i].name )); + if (status) memset( &info, 0xcc, sizeof(info) ); + pRtlWow64EnableFsRedirection( TRUE ); + status = get_file_id( &info_redir, tests[i].root, tests[i].name ); + ok( status == tests[i].expect || (tests[i].alt && status == tests[i].alt), + "%u: got %lx / %lx for %s + %s\n", i, status, tests[i].expect, + debugstr_w( tests[i].root ), debugstr_w( tests[i].name )); + if (!status) + { + BOOL redirected = memcmp( &info_redir, &info, sizeof(info) ); + ok( !redirected == !tests[i].redirected, + "%u: was %sredirected for %s + %s\n", i, redirected ? "" : "not ", + debugstr_w( tests[i].root ), debugstr_w( tests[i].name )); + } + } + } pRtlWow64EnableFsRedirectionEx( old, &cur ); } @@ -877,11 +1138,6 @@ START_TEST(directory) { WCHAR sysdir[MAX_PATH]; HMODULE hntdll = GetModuleHandleA("ntdll.dll"); - if (!hntdll) - { - skip("not running on NT, skipping test\n"); - return; - } pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); diff --git a/modules/rostests/winetests/ntdll/env.c b/modules/rostests/winetests/ntdll/env.c index 8989760f43c..7e4510957a4 100644 --- a/modules/rostests/winetests/ntdll/env.c +++ b/modules/rostests/winetests/ntdll/env.c @@ -1,5 +1,5 @@ /* - * Unit test suite for ntdll path functions + * Unit test suite for ntdll env functions * * Copyright 2003 Eric Pouech * @@ -19,17 +19,29 @@ */ #include +#include -#include "ntdll_test.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/test.h" static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen, LPCSTR src, DWORD srclen ); -static NTSTATUS (WINAPI *pRtlCreateEnvironment)(BOOLEAN, PWSTR*); -static NTSTATUS (WINAPI *pRtlDestroyEnvironment)(PWSTR); static NTSTATUS (WINAPI *pRtlQueryEnvironmentVariable_U)(PWSTR, PUNICODE_STRING, PUNICODE_STRING); -static void (WINAPI *pRtlSetCurrentEnvironment)(PWSTR, PWSTR*); -static NTSTATUS (WINAPI *pRtlSetEnvironmentVariable)(PWSTR*, PUNICODE_STRING, PUNICODE_STRING); +static NTSTATUS (WINAPI* pRtlQueryEnvironmentVariable)(WCHAR*, WCHAR*, SIZE_T, WCHAR*, SIZE_T, SIZE_T*); +static NTSTATUS (WINAPI *pRtlExpandEnvironmentStrings)(WCHAR*, WCHAR*, SIZE_T, WCHAR*, SIZE_T, SIZE_T*); static NTSTATUS (WINAPI *pRtlExpandEnvironmentStrings_U)(LPWSTR, PUNICODE_STRING, PUNICODE_STRING, PULONG); +static NTSTATUS (WINAPI *pRtlCreateProcessParameters)(RTL_USER_PROCESS_PARAMETERS**, + const UNICODE_STRING*, const UNICODE_STRING*, + const UNICODE_STRING*, const UNICODE_STRING*, + PWSTR, const UNICODE_STRING*, const UNICODE_STRING*, + const UNICODE_STRING*, const UNICODE_STRING*); +static void (WINAPI *pRtlDestroyProcessParameters)(RTL_USER_PROCESS_PARAMETERS *); + +static void *initial_env; static WCHAR small_env[] = {'f','o','o','=','t','o','t','o',0, 'f','o','=','t','i','t','i',0, @@ -87,6 +99,9 @@ static void testQuery(void) UNICODE_STRING name; UNICODE_STRING value; NTSTATUS nts; + SIZE_T name_length; + SIZE_T value_length; + SIZE_T return_length; unsigned int i; for (i = 0; tests[i].var; i++) @@ -103,7 +118,7 @@ static void testQuery(void) pRtlMultiByteToUnicodeN( bn, sizeof(bn), NULL, test->var, strlen(test->var)+1 ); nts = pRtlQueryEnvironmentVariable_U(small_env, &name, &value); ok( nts == test->status || (test->alt && nts == test->alt), - "[%d]: Wrong status for '%s', expecting %x got %x\n", + "[%d]: Wrong status for '%s', expecting %lx got %lx\n", i, test->var, test->status, nts ); if (nts == test->status) switch (nts) { @@ -122,84 +137,39 @@ static void testQuery(void) break; } } -} -static void testSetHelper(LPWSTR* env, const char* var, const char* val, NTSTATUS ret, NTSTATUS alt) -{ - WCHAR bvar[256], bval1[256], bval2[256]; - UNICODE_STRING uvar; - UNICODE_STRING uval; - NTSTATUS nts; - - uvar.Length = strlen(var) * sizeof(WCHAR); - uvar.MaximumLength = uvar.Length + sizeof(WCHAR); - uvar.Buffer = bvar; - pRtlMultiByteToUnicodeN( bvar, sizeof(bvar), NULL, var, strlen(var)+1 ); - if (val) + if (pRtlQueryEnvironmentVariable) { - uval.Length = strlen(val) * sizeof(WCHAR); - uval.MaximumLength = uval.Length + sizeof(WCHAR); - uval.Buffer = bval1; - pRtlMultiByteToUnicodeN( bval1, sizeof(bval1), NULL, val, strlen(val)+1 ); - } - nts = pRtlSetEnvironmentVariable(env, &uvar, val ? &uval : NULL); - ok(nts == ret || (alt && nts == alt), "Setting var %s=%s (%x/%x)\n", var, val, nts, ret); - if (nts == STATUS_SUCCESS) - { - uval.Length = 0; - uval.MaximumLength = sizeof(bval2); - uval.Buffer = bval2; - nts = pRtlQueryEnvironmentVariable_U(*env, &uvar, &uval); - switch (nts) + for (i = 0; tests[i].var; i++) { - case STATUS_SUCCESS: - ok(lstrcmpW(bval1, bval2) == 0, "Cannot get value written to environment\n"); - break; - case STATUS_VARIABLE_NOT_FOUND: - ok(val == NULL || - broken(strchr(var,'=') != NULL), /* variable containing '=' may be set but not found again on NT4 */ - "Couldn't find variable, but didn't delete it. val = %s\n", val); - break; - default: - ok(0, "Wrong ret %u for %s\n", nts, var); - break; + const struct test* test = &tests[i]; + name_length = strlen(test->var); + value_length = test->len; + value.Buffer = bv; + bv[test->len] = '@'; + + pRtlMultiByteToUnicodeN(bn, sizeof(bn), NULL, test->var, strlen(test->var) + 1); + nts = pRtlQueryEnvironmentVariable(small_env, bn, name_length, bv, value_length, &return_length); + ok(nts == test->status || (test->alt && nts == test->alt), + "[%d]: Wrong status for '%s', expecting %lx got %lx\n", + i, test->var, test->status, nts); + if (nts == test->status) switch (nts) + { + case STATUS_SUCCESS: + pRtlMultiByteToUnicodeN(bn, sizeof(bn), NULL, test->val, strlen(test->val) + 1); + ok(return_length == strlen(test->val), "Wrong length %Id for %s\n", + return_length, test->var); + ok(!memcmp(bv, bn, return_length), "Wrong result for %s/%d\n", test->var, test->len); + ok(bv[test->len] == '@', "Writing too far away in the buffer for %s/%d\n", test->var, test->len); + break; + case STATUS_BUFFER_TOO_SMALL: + ok(return_length == (strlen(test->val) + 1), + "Wrong returned length %Id (too small buffer) for %s\n", return_length, test->var); + break; + } } } -} - -static void testSet(void) -{ - LPWSTR env; - char tmp[16]; - int i; - - ok(pRtlCreateEnvironment(FALSE, &env) == STATUS_SUCCESS, "Creating environment\n"); - - testSetHelper(&env, "cat", "dog", STATUS_SUCCESS, 0); - testSetHelper(&env, "cat", "horse", STATUS_SUCCESS, 0); - testSetHelper(&env, "cat", "zz", STATUS_SUCCESS, 0); - testSetHelper(&env, "cat", NULL, STATUS_SUCCESS, 0); - testSetHelper(&env, "cat", NULL, STATUS_SUCCESS, STATUS_VARIABLE_NOT_FOUND); - testSetHelper(&env, "foo", "meouw", STATUS_SUCCESS, 0); - testSetHelper(&env, "me=too", "also", STATUS_SUCCESS, STATUS_INVALID_PARAMETER); - testSetHelper(&env, "me", "too=also", STATUS_SUCCESS, 0); - testSetHelper(&env, "=too", "also", STATUS_SUCCESS, 0); - testSetHelper(&env, "=", "also", STATUS_SUCCESS, 0); - - for (i = 0; i < 128; i++) - { - sprintf(tmp, "zork%03d", i); - testSetHelper(&env, tmp, "is alive", STATUS_SUCCESS, 0); - } - - for (i = 0; i < 128; i++) - { - sprintf(tmp, "zork%03d", i); - testSetHelper(&env, tmp, NULL, STATUS_SUCCESS, 0); - } - testSetHelper(&env, "fOo", NULL, STATUS_SUCCESS, 0); - - ok(pRtlDestroyEnvironment(env) == STATUS_SUCCESS, "Destroying environment\n"); + else win_skip("RtlQueryEnvironmentVariable not available, skipping tests\n"); } static void testExpand(void) @@ -244,20 +214,20 @@ static void testExpand(void) us_dst.Buffer = NULL; nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul); - ok(nts == STATUS_BUFFER_TOO_SMALL, "Call failed (%u)\n", nts); + ok(nts == STATUS_BUFFER_TOO_SMALL, "Call failed (%lu)\n", nts); ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR), - "Wrong returned length for %s: %u\n", test->src, ul ); + "Wrong returned length for %s: %lu\n", test->src, ul ); us_dst.Length = 0; us_dst.MaximumLength = sizeof(dst); us_dst.Buffer = dst; nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul); - ok(nts == STATUS_SUCCESS, "Call failed (%u)\n", nts); + ok(nts == STATUS_SUCCESS, "Call failed (%lu)\n", nts); ok(ul == us_dst.Length + sizeof(WCHAR), - "Wrong returned length for %s: %u\n", test->src, ul); + "Wrong returned length for %s: %lu\n", test->src, ul); ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR), - "Wrong returned length for %s: %u\n", test->src, ul); + "Wrong returned length for %s: %lu\n", test->src, ul); ok(lstrcmpW(dst, rst) == 0, "Wrong result for %s: expecting %s\n", test->src, test->dst); @@ -266,35 +236,508 @@ static void testExpand(void) us_dst.Buffer = dst; dst[8] = '-'; nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul); - ok(nts == STATUS_BUFFER_TOO_SMALL, "Call failed (%u)\n", nts); + ok(nts == STATUS_BUFFER_TOO_SMALL, "Call failed (%lu)\n", nts); ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR), - "Wrong returned length for %s (with buffer too small): %u\n", test->src, ul); + "Wrong returned length for %s (with buffer too small): %lu\n", test->src, ul); ok(dst[8] == '-', "Writing too far in buffer (got %c/%d)\n", dst[8], dst[8]); } } +static void test_RtlExpandEnvironmentStrings(void) +{ + int i; + WCHAR buf[256]; + HRESULT status; + UNICODE_STRING us_src, us_dst, us_name, us_value; + static const struct test_info + { + const WCHAR *input; + const WCHAR *expected_str; + int count_in; + int expected_count_out; + } tests[] = + { + /* 0 */ { L"Long long value", L"abcdefghijklmnopqrstuv", 0, 16 }, + /* 1 */ { L"Long long value", L"abcdefghijklmnopqrstuv", 1, 16 }, + /* 2 */ { L"Long long value", L"Lbcdefghijklmnopqrstuv", 2, 16 }, + /* 3 */ { L"Long long value", L"Locdefghijklmnopqrstuv", 3, 16 }, + /* 4 */ { L"Long long value", L"Long long valuopqrstuv", 15, 16 }, + /* 5 */ { L"Long long value", L"Long long value", 16, 16 }, + /* 6 */ { L"Long long value", L"Long long value", 17, 16 }, + /* 7 */ { L"%TVAR% long long", L"abcdefghijklmnopqrstuv", 0, 15 }, + /* 8 */ { L"%TVAR% long long", L"", 1, 15 }, + /* 9 */ { L"%TVAR% long long", L"", 2, 15 }, + /* 10 */ { L"%TVAR% long long", L"", 4, 15 }, + /* 11 */ { L"%TVAR% long long", L"WINE", 5, 15 }, + /* 12 */ { L"%TVAR% long long", L"WINE fghijklmnopqrstuv", 6, 15 }, + /* 13 */ { L"%TVAR% long long", L"WINE lghijklmnopqrstuv", 7, 15 }, + /* 14 */ { L"%TVAR% long long", L"WINE long long", 15, 15 }, + /* 15 */ { L"%TVAR% long long", L"WINE long long", 16, 15 }, + /* 16 */ { L"%TVAR%%TVAR% long", L"", 4, 14 }, + /* 17 */ { L"%TVAR%%TVAR% long", L"WINE", 5, 14 }, + /* 18 */ { L"%TVAR%%TVAR% long", L"WINE", 6, 14 }, + /* 19 */ { L"%TVAR%%TVAR% long", L"WINE", 8, 14 }, + /* 20 */ { L"%TVAR%%TVAR% long", L"WINEWINE", 9, 14 }, + /* 21 */ { L"%TVAR%%TVAR% long", L"WINEWINE jklmnopqrstuv", 10, 14 }, + /* 22 */ { L"%TVAR%%TVAR% long", L"WINEWINE long", 14, 14 }, + /* 23 */ { L"%TVAR%%TVAR% long", L"WINEWINE long", 15, 14 }, + /* 24 */ { L"%TVAR% %TVAR% long", L"WINE", 5, 15 }, + /* 25 */ { L"%TVAR% %TVAR% long", L"WINE ", 6, 15 }, + /* 26 */ { L"%TVAR% %TVAR% long", L"WINE ", 8, 15 }, + /* 27 */ { L"%TVAR% %TVAR% long", L"WINE ", 9, 15 }, + /* 28 */ { L"%TVAR% %TVAR% long", L"WINE WINE", 10, 15 }, + /* 29 */ { L"%TVAR% %TVAR% long", L"WINE WINE klmnopqrstuv", 11, 15 }, + /* 30 */ { L"%TVAR% %TVAR% long", L"WINE WINE llmnopqrstuv", 12, 15 }, + /* 31 */ { L"%TVAR% %TVAR% long", L"WINE WINE lonnopqrstuv", 14, 15 }, + /* 32 */ { L"%TVAR% %TVAR% long", L"WINE WINE long", 15, 15 }, + /* 33 */ { L"%TVAR% %TVAR% long", L"WINE WINE long", 16, 15 }, + /* 34 */ { L"%TVAR2% long long", L"abcdefghijklmnopqrstuv", 1, 18 }, + /* 35 */ { L"%TVAR2% long long", L"%bcdefghijklmnopqrstuv", 2, 18 }, + /* 36 */ { L"%TVAR2% long long", L"%TVdefghijklmnopqrstuv", 4, 18 }, + /* 37 */ { L"%TVAR2% long long", L"%TVAR2ghijklmnopqrstuv", 7, 18 }, + /* 38 */ { L"%TVAR2% long long", L"%TVAR2%hijklmnopqrstuv", 8, 18 }, + /* 39 */ { L"%TVAR2% long long", L"%TVAR2% ijklmnopqrstuv", 9, 18 }, + /* 40 */ { L"%TVAR2% long long", L"%TVAR2% ljklmnopqrstuv", 10, 18 }, + /* 41 */ { L"%TVAR2% long long", L"%TVAR2% long long", 18, 18 }, + /* 42 */ { L"%TVAR2% long long", L"%TVAR2% long long", 19, 18 }, + /* 43 */ { L"%TVAR long long", L"abcdefghijklmnopqrstuv", 1, 16 }, + /* 44 */ { L"%TVAR long long", L"%bcdefghijklmnopqrstuv", 2, 16 }, + /* 45 */ { L"%TVAR long long", L"%Tcdefghijklmnopqrstuv", 3, 16 }, + /* 46 */ { L"%TVAR long long", L"%TVAR long lonopqrstuv", 15, 16 }, + /* 47 */ { L"%TVAR long long", L"%TVAR long long", 16, 16 }, + /* 48 */ { L"%TVAR long long", L"%TVAR long long", 17, 16 }, + }; + + RtlInitUnicodeString(&us_name, L"TVAR"); + RtlInitUnicodeString(&us_value, L"WINE"); + status = RtlSetEnvironmentVariable(NULL, &us_name, &us_value); + ok(status == STATUS_SUCCESS, "RtlSetEnvironmentVariable failed with %lx\n", status); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + const struct test_info *test = &tests[i]; + SIZE_T out_len; + HRESULT expected_status = test->count_in >= test->expected_count_out ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL; + + wcscpy(buf, L"abcdefghijklmnopqrstuv"); + status = pRtlExpandEnvironmentStrings(NULL, (WCHAR*)test->input, wcslen(test->input), buf, test->count_in, &out_len); + ok(out_len == test->expected_count_out, "Test %d: got %Iu\n", i, out_len); + ok(status == expected_status, "Test %d: Expected status %lx, got %lx\n", i, expected_status, status); + ok(!wcscmp(buf, test->expected_str), "Test %d: got %s\n", i, debugstr_w(buf)); + } + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + const struct test_info *test = &tests[i]; + DWORD out_len; + HRESULT expected_status = test->count_in >= test->expected_count_out ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL; + + us_src.Length = wcslen(test->input) * sizeof(WCHAR); + us_src.MaximumLength = us_src.Length; + us_src.Buffer = (WCHAR*)test->input; + + us_dst.Length = test->count_in * sizeof(WCHAR); + us_dst.MaximumLength = us_dst.Length; + us_dst.Buffer = buf; + + wcscpy(buf, L"abcdefghijklmnopqrstuv"); + status = pRtlExpandEnvironmentStrings_U(NULL, &us_src, &us_dst, &out_len); + ok(out_len / sizeof(WCHAR) == test->expected_count_out, "Test %d: got %lu\n", i, out_len); + ok(status == expected_status, "Test %d: Expected status %lx, got %lx\n", i, expected_status, status); + ok(!wcscmp(buf, test->expected_str), "Test %d: got %s\n", i, debugstr_w(buf)); + } + status = RtlSetEnvironmentVariable(NULL, &us_name, NULL); + ok(status == STATUS_SUCCESS, "RtlSetEnvironmentVariable failed with %lx\n", status); +} + +static WCHAR *get_params_string( RTL_USER_PROCESS_PARAMETERS *params, UNICODE_STRING *str ) +{ + if (params->Flags & PROCESS_PARAMS_FLAG_NORMALIZED) return str->Buffer; + return (WCHAR *)((char *)params + (UINT_PTR)str->Buffer); +} + +static SIZE_T get_env_length( const WCHAR *env ) +{ + const WCHAR *end = env; + while (*end) end += wcslen(end) + 1; + return end + 1 - env; +} + +static UINT_PTR align(UINT_PTR size, unsigned int alignment) +{ + return (size + (alignment - 1)) & ~(alignment - 1); +} + +static UINT_PTR check_string_( int line, RTL_USER_PROCESS_PARAMETERS *params, UNICODE_STRING *str, + const UNICODE_STRING *expect, UINT_PTR pos ) +{ + if (expect) + { + ok_(__FILE__,line)( str->Length == expect->Length, "wrong length %u/%u\n", + str->Length, expect->Length ); + ok_(__FILE__,line)( str->MaximumLength == expect->MaximumLength, + "wrong maxlength %u/%u\n", str->MaximumLength, expect->MaximumLength ); + } + if (!str->MaximumLength) + { + ok_(__FILE__,line)( str->Buffer == NULL, "buffer not null %p\n", str->Buffer ); + return pos; + } + if (expect) + ok_(__FILE__,line)( (UINT_PTR)str->Buffer == align(pos, sizeof(void *)) || + broken( (UINT_PTR)str->Buffer == align(pos, 4) ), /* win7 */ + "wrong buffer %Ix/%Ix\n", (UINT_PTR)str->Buffer, pos ); + else /* initial params are not aligned */ + ok_(__FILE__,line)( (UINT_PTR)str->Buffer == pos, + "wrong buffer %Ix/%Ix\n", (UINT_PTR)str->Buffer, pos ); + if (str->Length < str->MaximumLength) + { + WCHAR *ptr = get_params_string( params, str ); + ok_(__FILE__,line)( !ptr[str->Length / sizeof(WCHAR)], "string not null-terminated %s\n", + wine_dbgstr_wn( ptr, str->MaximumLength / sizeof(WCHAR) )); + } + return (UINT_PTR)str->Buffer + str->MaximumLength; +} +#define check_string(params,str,expect,pos) check_string_(__LINE__,params,str,expect,pos) + +static void test_process_params(void) +{ + static WCHAR empty[] = {0}; + static const UNICODE_STRING empty_str = { 0, sizeof(empty), empty }; + static const UNICODE_STRING null_str = { 0, 0, NULL }; + static WCHAR exeW[] = {'c',':','\\','f','o','o','.','e','x','e',0}; + static WCHAR dummyW[] = {'d','u','m','m','y','1',0}; + static WCHAR dummy_dirW[MAX_PATH] = {'d','u','m','m','y','2',0}; + static WCHAR dummy_env[] = {'a','=','b',0,'c','=','d',0,0}; + UNICODE_STRING image = { sizeof(exeW) - sizeof(WCHAR), sizeof(exeW), exeW }; + UNICODE_STRING dummy = { sizeof(dummyW) - sizeof(WCHAR), sizeof(dummyW), dummyW }; + UNICODE_STRING dummy_dir = { 6*sizeof(WCHAR), sizeof(dummy_dirW), dummy_dirW }; + RTL_USER_PROCESS_PARAMETERS *params = NULL; + RTL_USER_PROCESS_PARAMETERS *cur_params = NtCurrentTeb()->Peb->ProcessParameters; + SIZE_T size; + WCHAR *str; + UINT_PTR pos; + NTSTATUS status = pRtlCreateProcessParameters( ¶ms, &image, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL ); + ok( !status, "failed %lx\n", status ); + size = HeapSize( GetProcessHeap(), 0, params ); + ok( size != ~(SIZE_T)0, "not a heap block %p\n", params ); + ok( params->AllocationSize == params->Size, + "wrong AllocationSize %lx/%lx\n", params->AllocationSize, params->Size ); + ok( params->Size < size, "wrong Size %lx/%Ix\n", params->Size, size ); + ok( params->Flags == 0, "wrong Flags %lu\n", params->Flags ); + ok( params->DebugFlags == 0, "wrong Flags %lu\n", params->DebugFlags ); + ok( params->ConsoleHandle == 0, "wrong ConsoleHandle %p\n", params->ConsoleHandle ); + ok( params->ConsoleFlags == 0, "wrong ConsoleFlags %lu\n", params->ConsoleFlags ); + ok( params->hStdInput == 0, "wrong hStdInput %p\n", params->hStdInput ); + ok( params->hStdOutput == 0, "wrong hStdOutput %p\n", params->hStdOutput ); + ok( params->hStdError == 0, "wrong hStdError %p\n", params->hStdError ); + ok( params->dwX == 0, "wrong dwX %lu\n", params->dwX ); + ok( params->dwY == 0, "wrong dwY %lu\n", params->dwY ); + ok( params->dwXSize == 0, "wrong dwXSize %lu\n", params->dwXSize ); + ok( params->dwYSize == 0, "wrong dwYSize %lu\n", params->dwYSize ); + ok( params->dwXCountChars == 0, "wrong dwXCountChars %lu\n", params->dwXCountChars ); + ok( params->dwYCountChars == 0, "wrong dwYCountChars %lu\n", params->dwYCountChars ); + ok( params->dwFillAttribute == 0, "wrong dwFillAttribute %lu\n", params->dwFillAttribute ); + ok( params->dwFlags == 0, "wrong dwFlags %lu\n", params->dwFlags ); + ok( params->wShowWindow == 0, "wrong wShowWindow %lu\n", params->wShowWindow ); + pos = (UINT_PTR)params->CurrentDirectory.DosPath.Buffer; + + ok( params->CurrentDirectory.DosPath.MaximumLength == MAX_PATH * sizeof(WCHAR), + "wrong length %x\n", params->CurrentDirectory.DosPath.MaximumLength ); + pos = check_string( params, ¶ms->CurrentDirectory.DosPath, + &cur_params->CurrentDirectory.DosPath, pos ); + if (params->DllPath.MaximumLength) + pos = check_string( params, ¶ms->DllPath, &cur_params->DllPath, pos ); + else + pos = check_string( params, ¶ms->DllPath, &null_str, pos ); + pos = check_string( params, ¶ms->ImagePathName, &image, pos ); + pos = check_string( params, ¶ms->CommandLine, &image, pos ); + pos = check_string( params, ¶ms->WindowTitle, &empty_str, pos ); + pos = check_string( params, ¶ms->Desktop, &empty_str, pos ); + pos = check_string( params, ¶ms->ShellInfo, &empty_str, pos ); + pos = check_string( params, ¶ms->RuntimeInfo, &null_str, pos ); + pos = align(pos, 4); + ok( pos == params->Size || pos + 4 == params->Size, + "wrong pos %Ix/%lx\n", pos, params->Size ); + pos = params->Size; + ok( (char *)params->Environment - (char *)params == (UINT_PTR)pos, + "wrong env %Ix/%Ix\n", (UINT_PTR)((char *)params->Environment - (char *)params), pos); + pos += get_env_length(params->Environment) * sizeof(WCHAR); + ok( align(pos, sizeof(void *)) == size || + broken( align(pos, 4) == size ), "wrong size %Ix/%Ix\n", pos, size ); + ok( params->EnvironmentSize == size - ((char *)params->Environment - (char *)params), + "wrong len %Ix/%Ix\n", params->EnvironmentSize, + size - ((char *)params->Environment - (char *)params) ); + pRtlDestroyProcessParameters( params ); + + status = pRtlCreateProcessParameters( ¶ms, &image, &dummy, &dummy, &dummy, dummy_env, + &dummy, &dummy, &dummy, &dummy ); + ok( !status, "failed %lx\n", status ); + size = HeapSize( GetProcessHeap(), 0, params ); + ok( size != ~(SIZE_T)0, "not a heap block %p\n", params ); + ok( params->AllocationSize == params->Size, + "wrong AllocationSize %lx/%lx\n", params->AllocationSize, params->Size ); + ok( params->Size < size, "wrong Size %lx/%Ix\n", params->Size, size ); + pos = (UINT_PTR)params->CurrentDirectory.DosPath.Buffer; + + if (params->CurrentDirectory.DosPath.Length == dummy_dir.Length + sizeof(WCHAR)) + { + /* win10 appends a backslash */ + dummy_dirW[dummy_dir.Length / sizeof(WCHAR)] = '\\'; + dummy_dir.Length += sizeof(WCHAR); + } + pos = check_string( params, ¶ms->CurrentDirectory.DosPath, &dummy_dir, pos ); + pos = check_string( params, ¶ms->DllPath, &dummy, pos ); + pos = check_string( params, ¶ms->ImagePathName, &image, pos ); + pos = check_string( params, ¶ms->CommandLine, &dummy, pos ); + pos = check_string( params, ¶ms->WindowTitle, &dummy, pos ); + pos = check_string( params, ¶ms->Desktop, &dummy, pos ); + pos = check_string( params, ¶ms->ShellInfo, &dummy, pos ); + pos = check_string( params, ¶ms->RuntimeInfo, &dummy, pos ); + pos = align(pos, 4); + ok( pos == params->Size || pos + 4 == params->Size, + "wrong pos %Ix/%lx\n", pos, params->Size ); + pos = params->Size; + ok( (char *)params->Environment - (char *)params == pos, + "wrong env %Ix/%Ix\n", (UINT_PTR)((char *)params->Environment - (char *)params), pos); + pos += get_env_length(params->Environment) * sizeof(WCHAR); + ok( align(pos, sizeof(void *)) == size || + broken( align(pos, 4) == size ), "wrong size %Ix/%Ix\n", pos, size ); + ok( params->EnvironmentSize == size - ((char *)params->Environment - (char *)params), + "wrong len %Ix/%Ix\n", params->EnvironmentSize, + size - ((char *)params->Environment - (char *)params) ); + pRtlDestroyProcessParameters( params ); + + /* also test the actual parameters of the current process */ + + ok( cur_params->Flags & PROCESS_PARAMS_FLAG_NORMALIZED, "current params not normalized\n" ); + size = HeapSize( GetProcessHeap(), 0, cur_params ); + ok( size != ~(SIZE_T)0, "not a heap block %p\n", cur_params ); + ok( cur_params->AllocationSize == cur_params->Size, + "wrong AllocationSize %lx/%lx\n", cur_params->AllocationSize, cur_params->Size ); + ok( cur_params->Size == size, "wrong Size %lx/%Ix\n", cur_params->Size, size ); + + /* CurrentDirectory points outside the params, and DllPath may be null */ + pos = (UINT_PTR)cur_params->DllPath.Buffer; + if (!pos) pos = (UINT_PTR)cur_params->ImagePathName.Buffer; + pos = check_string( cur_params, &cur_params->DllPath, NULL, pos ); + pos = check_string( cur_params, &cur_params->ImagePathName, NULL, pos ); + pos = check_string( cur_params, &cur_params->CommandLine, NULL, pos ); + pos = check_string( cur_params, &cur_params->WindowTitle, NULL, pos ); + pos = check_string( cur_params, &cur_params->Desktop, NULL, pos ); + pos = check_string( cur_params, &cur_params->ShellInfo, NULL, pos ); + pos = check_string( cur_params, &cur_params->RuntimeInfo, NULL, pos ); + /* environment may follow */ + str = (WCHAR *)pos; + if (pos - (UINT_PTR)cur_params < cur_params->Size) str += get_env_length(str); + ok( (char *)str == (char *)cur_params + cur_params->Size, + "wrong end ptr %p/%p\n", str, (char *)cur_params + cur_params->Size ); + + /* initial environment is a separate block */ + + ok( (char *)initial_env < (char *)cur_params || (char *)initial_env >= (char *)cur_params + size, + "initial environment inside block %p / %p\n", cur_params, initial_env ); + size = HeapSize( GetProcessHeap(), 0, initial_env ); + ok( size != ~(SIZE_T)0, "env is not a heap block %p / %p\n", cur_params, initial_env ); + ok( cur_params->EnvironmentSize == size, + "wrong len %Ix/%Ix\n", cur_params->EnvironmentSize, size ); +} + +static NTSTATUS set_env_var(WCHAR **env, const WCHAR *var, const WCHAR *value) +{ + UNICODE_STRING var_string, value_string; + RtlInitUnicodeString(&var_string, var); + if (value) RtlInitUnicodeString(&value_string, value); + return RtlSetEnvironmentVariable(env, &var_string, value ? &value_string : NULL); +} + +static void check_env_var_(int line, const char *var, const char *value) +{ + char buffer[20]; + DWORD size = GetEnvironmentVariableA(var, buffer, sizeof(buffer)); + if (value) + { + ok_(__FILE__, line)(size == strlen(value), "wrong size %lu\n", size); + ok_(__FILE__, line)(!strcmp(buffer, value), "wrong value %s\n", debugstr_a(buffer)); + } + else + { + ok_(__FILE__, line)(!size, "wrong size %lu\n", size); + ok_(__FILE__, line)(GetLastError() == ERROR_ENVVAR_NOT_FOUND, "got error %lu\n", GetLastError()); + } +} +#define check_env_var(a, b) check_env_var_(__LINE__, a, b) + +static void test_RtlSetCurrentEnvironment(void) +{ + NTSTATUS status; + WCHAR *old_env, *env, *prev; + BOOL ret; + SIZE_T size; + + status = RtlCreateEnvironment(FALSE, &env); + ok(!status, "got %#lx\n", status); + + ret = SetEnvironmentVariableA("testenv1", "heis"); + ok(ret, "got error %lu\n", GetLastError()); + ret = SetEnvironmentVariableA("testenv2", "dyo"); + ok(ret, "got error %lu\n", GetLastError()); + + status = set_env_var(&env, L"testenv1", L"unus"); + ok(!status, "got %#lx\n", status); + status = set_env_var(&env, L"testenv3", L"tres"); + ok(!status, "got %#lx\n", status); + + old_env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == get_env_length(old_env) * sizeof(WCHAR), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == HeapSize( GetProcessHeap(), 0, old_env ), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + + RtlSetCurrentEnvironment(env, &prev); + ok(prev == old_env, "got wrong previous env %p\n", prev); + ok(NtCurrentTeb()->Peb->ProcessParameters->Environment == env, "got wrong current env\n"); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == get_env_length(env) * sizeof(WCHAR), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == HeapSize( GetProcessHeap(), 0, env ), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + + check_env_var("testenv1", "unus"); + check_env_var("testenv2", NULL); + check_env_var("testenv3", "tres"); + check_env_var("PATH", NULL); + + env = HeapReAlloc( GetProcessHeap(), 0, env, HeapSize( GetProcessHeap(), 0, env) + 120 ); + RtlSetCurrentEnvironment(env, &prev); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == HeapSize( GetProcessHeap(), 0, env ), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + + RtlSetCurrentEnvironment(old_env, NULL); + ok(NtCurrentTeb()->Peb->ProcessParameters->Environment == old_env, "got wrong current env\n"); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == get_env_length(old_env) * sizeof(WCHAR), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + ok(NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == HeapSize( GetProcessHeap(), 0, old_env ), + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize); + + check_env_var("testenv1", "heis"); + check_env_var("testenv2", "dyo"); + check_env_var("testenv3", NULL); + + env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + size = get_env_length(env) * sizeof(WCHAR); + ok( NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == size, + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize ); + ok( size == HeapSize( GetProcessHeap(), 0, env ), + "got wrong size %Iu / %Iu\n", size, HeapSize( GetProcessHeap(), 0, env )); + + SetEnvironmentVariableA("testenv1", NULL); + SetEnvironmentVariableA("testenv2", NULL); + + env = NtCurrentTeb()->Peb->ProcessParameters->Environment; + ok( NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize == size, + "got wrong size %Iu\n", NtCurrentTeb()->Peb->ProcessParameters->EnvironmentSize ); + ok( size == HeapSize( GetProcessHeap(), 0, env ), + "got wrong size %Iu / %Iu\n", size, HeapSize( GetProcessHeap(), 0, env )); + ok( size > get_env_length(env) * sizeof(WCHAR), "got wrong size %Iu\n", size ); +} + +static void query_env_var_(int line, WCHAR *env, const WCHAR *var, const WCHAR *value) +{ + UNICODE_STRING var_string, value_string; + WCHAR value_buffer[9]; + NTSTATUS status; + + RtlInitUnicodeString(&var_string, var); + value_string.Buffer = value_buffer; + value_string.MaximumLength = sizeof(value_buffer); + + status = RtlQueryEnvironmentVariable_U(env, &var_string, &value_string); + if (value) + { + ok_(__FILE__, line)(!status, "got %#lx\n", status); + ok_(__FILE__, line)(value_string.Length/sizeof(WCHAR) == wcslen(value), + "wrong size %Iu\n", value_string.Length/sizeof(WCHAR)); + ok_(__FILE__, line)(!wcscmp(value_string.Buffer, value), "wrong value %s\n", debugstr_w(value_string.Buffer)); + } + else + ok_(__FILE__, line)(status == STATUS_VARIABLE_NOT_FOUND, "got %#lx\n", status); +} +#define query_env_var(a, b, c) query_env_var_(__LINE__, a, b, c) + +static void test_RtlSetEnvironmentVariable(void) +{ + NTSTATUS status; + WCHAR *env; + + status = RtlCreateEnvironment(FALSE, &env); + ok(!status, "got %#lx\n", status); + + status = set_env_var(&env, L"cat", L"dog"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"cat", L"dog"); + + status = set_env_var(&env, L"cat", L"horse"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"cat", L"horse"); + + status = set_env_var(&env, L"cat", NULL); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"cat", NULL); + + status = set_env_var(&env, L"cat", NULL); + ok(!status, "got %#lx\n", status); + + status = set_env_var(&env, L"foo", L"meouw"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"foo", L"meouw"); + + status = set_env_var(&env, L"fOo", NULL); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"foo", NULL); + + status = set_env_var(&env, L"horse", NULL); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"horse", NULL); + + status = set_env_var(&env, L"me=too", L"also"); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); + + status = set_env_var(&env, L"me", L"too=also"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"me", L"too=also"); + + status = set_env_var(&env, L"=too", L"also"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"=too", L"also"); + + status = set_env_var(&env, L"=", L"also"); + ok(!status, "got %#lx\n", status); + query_env_var(env, L"=", L"also"); + + status = RtlDestroyEnvironment(env); + ok(!status, "got %#lx\n", status); +} + START_TEST(env) { HMODULE mod = GetModuleHandleA("ntdll.dll"); - if (!mod) - { - win_skip("Not running on NT, skipping tests\n"); - return; - } + + initial_env = NtCurrentTeb()->Peb->ProcessParameters->Environment; pRtlMultiByteToUnicodeN = (void *)GetProcAddress(mod,"RtlMultiByteToUnicodeN"); - pRtlCreateEnvironment = (void*)GetProcAddress(mod, "RtlCreateEnvironment"); - pRtlDestroyEnvironment = (void*)GetProcAddress(mod, "RtlDestroyEnvironment"); pRtlQueryEnvironmentVariable_U = (void*)GetProcAddress(mod, "RtlQueryEnvironmentVariable_U"); - pRtlSetCurrentEnvironment = (void*)GetProcAddress(mod, "RtlSetCurrentEnvironment"); - pRtlSetEnvironmentVariable = (void*)GetProcAddress(mod, "RtlSetEnvironmentVariable"); + pRtlQueryEnvironmentVariable = (void*)GetProcAddress(mod, "RtlQueryEnvironmentVariable"); + pRtlExpandEnvironmentStrings = (void*)GetProcAddress(mod, "RtlExpandEnvironmentStrings"); pRtlExpandEnvironmentStrings_U = (void*)GetProcAddress(mod, "RtlExpandEnvironmentStrings_U"); + pRtlCreateProcessParameters = (void*)GetProcAddress(mod, "RtlCreateProcessParameters"); + pRtlDestroyProcessParameters = (void*)GetProcAddress(mod, "RtlDestroyProcessParameters"); - if (pRtlQueryEnvironmentVariable_U) - testQuery(); - if (pRtlSetEnvironmentVariable) - testSet(); - if (pRtlExpandEnvironmentStrings_U) - testExpand(); + testQuery(); + testExpand(); + test_process_params(); + test_RtlSetCurrentEnvironment(); + test_RtlSetEnvironmentVariable(); + test_RtlExpandEnvironmentStrings(); } diff --git a/modules/rostests/winetests/ntdll/error.c b/modules/rostests/winetests/ntdll/error.c index fbbc07ee770..70eadaadea1 100644 --- a/modules/rostests/winetests/ntdll/error.c +++ b/modules/rostests/winetests/ntdll/error.c @@ -47,6 +47,10 @@ * (of course older Windows platforms will fail to pass the strict mode) */ +#ifdef __REACTOS__ +/* This isn't defined in the Windows SDK but Wine needs it anyways. */ +#define STATUS_VRF_CFG_ENABLED ((NTSTATUS)0xC000049F) +#endif static ULONG (WINAPI *pRtlNtStatusToDosError)(NTSTATUS Status); static int strict; @@ -75,7 +79,7 @@ static void cmp_call(NTSTATUS win_nt, ULONG win32, const char* message) err = pRtlNtStatusToDosError(win_nt); ok(err == win32, - "%s (%x): got %u, expected %u\n", + "%s (%lx): got %lu, expected %lu\n", message, win_nt, err, win32); } @@ -86,7 +90,7 @@ static void cmp_call2(NTSTATUS win_nt, ULONG win32, const char* message) err = pRtlNtStatusToDosError(win_nt); ok(err == win32 || (!strict && err == ERROR_MR_MID_NOT_FOUND), - "%s (%x): got %u, expected %u (or MID_NOT_FOUND)\n", + "%s (%lx): got %lu, expected %lu (or MID_NOT_FOUND)\n", message, win_nt, err, win32); } @@ -96,7 +100,7 @@ static void cmp_call3(NTSTATUS win_nt, ULONG win32_1, ULONG win32_2, const char* err = pRtlNtStatusToDosError(win_nt); ok(err == win32_1 || (!strict && err == win32_2), - "%s (%x): got %u, expected %u or %u\n", + "%s (%lx): got %lu, expected %lu or %lu\n", message, win_nt, err, win32_1, win32_2); } @@ -107,7 +111,7 @@ static void cmp_call4(NTSTATUS win_nt, ULONG win32_1, ULONG win32_2, const char* err = pRtlNtStatusToDosError(win_nt); ok(err == win32_1 || (!strict && (err == win32_2 || err == ERROR_MR_MID_NOT_FOUND)), - "%s (%x): got %u, expected %u or %u\n", + "%s (%lx): got %lu, expected %lu or %lu\n", message, win_nt, err, win32_1, win32_2); } @@ -247,7 +251,7 @@ static void run_error_tests(void) cmp(STATUS_RESOURCE_TYPE_NOT_FOUND, ERROR_RESOURCE_TYPE_NOT_FOUND); cmp(STATUS_RESOURCE_NAME_NOT_FOUND, ERROR_RESOURCE_NAME_NOT_FOUND); cmp(STATUS_RESOURCE_LANG_NOT_FOUND, ERROR_RESOURCE_LANG_NOT_FOUND); - cmp(STATUS_NO_SUCH_DEVICE, ERROR_FILE_NOT_FOUND); + cmp3(STATUS_NO_SUCH_DEVICE, ERROR_NO_SUCH_DEVICE, ERROR_FILE_NOT_FOUND); cmp(STATUS_NO_SUCH_FILE, ERROR_FILE_NOT_FOUND); cmp(STATUS_INVALID_DEVICE_REQUEST, ERROR_INVALID_FUNCTION); cmp2(STATUS_VOLUME_NOT_UPGRADED, ERROR_INVALID_FUNCTION); @@ -542,6 +546,8 @@ static void run_error_tests(void) cmp2(STATUS_WAKE_SYSTEM_DEBUGGER, ERROR_WAKE_SYSTEM_DEBUGGER); cmp2(DBG_EXCEPTION_HANDLED, ERROR_DBG_EXCEPTION_HANDLED); cmp2(DBG_CONTINUE, ERROR_DBG_CONTINUE); + cmp2(DBG_REPLY_LATER, ERROR_DBG_REPLY_LATER); + cmp2(DBG_UNABLE_TO_PROVIDE_HANDLE, ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE); cmp2(DBG_TERMINATE_THREAD, ERROR_DBG_TERMINATE_THREAD); cmp2(DBG_TERMINATE_PROCESS, ERROR_DBG_TERMINATE_PROCESS); cmp2(DBG_CONTROL_C, ERROR_DBG_CONTROL_C); @@ -549,6 +555,7 @@ static void run_error_tests(void) cmp2(DBG_RIPEXCEPTION, ERROR_DBG_RIPEXCEPTION); cmp2(DBG_CONTROL_BREAK, ERROR_DBG_CONTROL_BREAK); cmp2(DBG_COMMAND_EXCEPTION, ERROR_DBG_COMMAND_EXCEPTION); + cmp2(DBG_PRINTEXCEPTION_WIDE_C, ERROR_DBG_PRINTEXCEPTION_C); cmp2(DBG_EXCEPTION_NOT_HANDLED, ERROR_DBG_EXCEPTION_NOT_HANDLED); cmp2(STATUS_DEBUG_ATTACH_FAILED, ERROR_DEBUG_ATTACH_FAILED); cmp2(STATUS_VALIDATE_CONTINUE, ERROR_VALIDATE_CONTINUE); @@ -1134,6 +1141,439 @@ static void run_error_tests(void) cmp2(STATUS_CLUSTER_NODE_NOT_PAUSED, ERROR_CLUSTER_NODE_NOT_PAUSED); cmp2(STATUS_CLUSTER_NO_SECURITY_CONTEXT, ERROR_CLUSTER_NO_SECURITY_CONTEXT); cmp2(STATUS_CLUSTER_NETWORK_NOT_INTERNAL, ERROR_CLUSTER_NETWORK_NOT_INTERNAL); + cmp2(STATUS_NOTHING_TO_TERMINATE, ERROR_NOTHING_TO_TERMINATE); + cmp2(STATUS_PROCESS_NOT_IN_JOB, ERROR_PROCESS_NOT_IN_JOB); + cmp2(STATUS_PROCESS_IN_JOB, ERROR_PROCESS_IN_JOB); + cmp2(STATUS_VOLSNAP_HIBERNATE_READY, ERROR_VOLSNAP_HIBERNATE_READY); + cmp2(STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY, ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY); + cmp2(STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED, ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED); + cmp2(STATUS_INTERRUPT_STILL_CONNECTED, ERROR_INTERRUPT_STILL_CONNECTED); + cmp2(STATUS_WX86_UNSIMULATE, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_CONTINUE, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_SINGLE_STEP, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_BREAKPOINT, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_EXCEPTION_CONTINUE, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_EXCEPTION_LASTCHANCE, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_EXCEPTION_CHAIN, ERROR_WX86_WARNING); + cmp2(STATUS_WX86_CREATEWX86TIB, ERROR_WX86_WARNING); + cmp2(STATUS_DRIVERS_LEAKING_LOCKED_PAGES, ERROR_DRIVERS_LEAKING_LOCKED_PAGES); + cmp2(STATUS_MESSAGE_RETRIEVED, ERROR_INTERNAL_ERROR); + cmp2(STATUS_SYSTEM_POWERSTATE_TRANSITION, ERROR_SYSTEM_POWERSTATE_TRANSITION); + cmp2(STATUS_ALPC_CHECK_COMPLETION_LIST, ERROR_INTERNAL_ERROR); + cmp2(STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION, ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION); + cmp2(STATUS_ACCESS_AUDIT_BY_POLICY, ERROR_ACCESS_AUDIT_BY_POLICY); + cmp2(STATUS_ABANDON_HIBERFILE, ERROR_ABANDON_HIBERFILE); + cmp2(STATUS_BIZRULES_NOT_ENABLED, ERROR_BIZRULES_NOT_ENABLED); + cmp2(STATUS_IMAGE_AT_DIFFERENT_BASE, ERROR_IMAGE_AT_DIFFERENT_BASE); + cmp2(STATUS_STOPPED_ON_SYMLINK, ERROR_STOPPED_ON_SYMLINK); + cmp2(STATUS_CANNOT_GRANT_REQUESTED_OPLOCK, ERROR_CANNOT_GRANT_REQUESTED_OPLOCK); + cmp2(STATUS_NO_ACE_CONDITION, ERROR_NO_ACE_CONDITION); + cmp2(STATUS_DEVICE_SUPPORT_IN_PROGRESS, ERROR_DEVICE_SUPPORT_IN_PROGRESS); + cmp2(STATUS_DEVICE_POWER_CYCLE_REQUIRED, STG_S_POWER_CYCLE_REQUIRED); + cmp2(STATUS_NO_WORK_DONE, ERROR_NO_WORK_DONE); + cmp2(SEC_E_UNFINISHED_CONTEXT_DELETED, ERROR_CONTEXT_EXPIRED); + cmp2(SEC_E_SHUTDOWN_IN_PROGRESS, ERROR_SHUTDOWN_IN_PROGRESS); + cmp2(SEC_E_CERT_WRONG_USAGE, ERROR_BAD_TOKEN_TYPE); + cmp2(SEC_E_INVALID_PARAMETER, ERROR_INVALID_PARAMETER); + cmp2(SEC_E_DELEGATION_POLICY, ERROR_ACCESS_DENIED); + cmp2(SEC_E_POLICY_NLTM_ONLY, ERROR_ACCESS_DENIED); + cmp2(SEC_E_MUTUAL_AUTH_FAILED, ERROR_MUTUAL_AUTH_FAILED); + cmp2(SEC_E_APPLICATION_PROTOCOL_MISMATCH, ERROR_NOT_SUPPORTED); + cmp2(STATUS_TIMER_NOT_CANCELED, ERROR_TIMER_NOT_CANCELED); + cmp2(STATUS_ILLEGAL_INSTRUCTION, STATUS_ILLEGAL_INSTRUCTION); + cmp2(STATUS_UNWIND, ERROR_UNWIND); + cmp2(STATUS_BAD_STACK, ERROR_BAD_STACK); + cmp2(STATUS_INVALID_UNWIND_TARGET, ERROR_INVALID_UNWIND_TARGET); + cmp2(STATUS_INVALID_PORT_ATTRIBUTES, ERROR_INVALID_PORT_ATTRIBUTES); + cmp2(STATUS_PORT_MESSAGE_TOO_LONG, ERROR_PORT_MESSAGE_TOO_LONG); + cmp2(STATUS_INVALID_QUOTA_LOWER, ERROR_INVALID_QUOTA_LOWER); + cmp2(STATUS_PORT_DO_NOT_DISTURB, RPC_S_DO_NOT_DISTURB); + cmp2(STATUS_DEVICE_ALREADY_ATTACHED, ERROR_DEVICE_ALREADY_ATTACHED); + cmp2(STATUS_INSTRUCTION_MISALIGNMENT, ERROR_INSTRUCTION_MISALIGNMENT); + cmp2(STATUS_PROFILING_NOT_STARTED, ERROR_PROFILING_NOT_STARTED); + cmp2(STATUS_PROFILING_NOT_STOPPED, ERROR_PROFILING_NOT_STOPPED); + cmp2(STATUS_COULD_NOT_INTERPRET, ERROR_COULD_NOT_INTERPRET); + cmp2(STATUS_PROFILING_AT_LIMIT, ERROR_PROFILING_AT_LIMIT); + cmp2(STATUS_CANT_WAIT, ERROR_CANT_WAIT); + cmp2(STATUS_CANT_TERMINATE_SELF, ERROR_CANT_TERMINATE_SELF); + cmp2(STATUS_UNEXPECTED_IO_ERROR, ERROR_IO_DEVICE); + cmp2(STATUS_UNEXPECTED_MM_CREATE_ERR, ERROR_UNEXPECTED_MM_CREATE_ERR); + cmp2(STATUS_UNEXPECTED_MM_MAP_ERROR, ERROR_UNEXPECTED_MM_MAP_ERROR); + cmp2(STATUS_UNEXPECTED_MM_EXTEND_ERR, ERROR_UNEXPECTED_MM_EXTEND_ERR); + cmp2(STATUS_BAD_FUNCTION_TABLE, ERROR_BAD_FUNCTION_TABLE); + cmp2(STATUS_NO_GUID_TRANSLATION, ERROR_NO_GUID_TRANSLATION); + cmp2(STATUS_ABIOS_NOT_PRESENT, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_LID_NOT_EXIST, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_LID_ALREADY_OWNED, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_NOT_LID_OWNER, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_INVALID_COMMAND, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_INVALID_LID, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_SELECTOR_NOT_AVAILABLE, ERROR_ABIOS_ERROR); + cmp2(STATUS_ABIOS_INVALID_SELECTOR, ERROR_ABIOS_ERROR); + cmp2(STATUS_ILL_FORMED_SERVICE_ENTRY, ERROR_SERVICE_NOT_FOUND); + cmp2(STATUS_IMAGE_ALREADY_LOADED_AS_DLL, ERROR_SERVICE_ALREADY_RUNNING); + cmp2(STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING, ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING); + cmp2(STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME, ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME); + cmp2(STATUS_SECURITY_STREAM_IS_INCONSISTENT, ERROR_SECURITY_STREAM_IS_INCONSISTENT); + cmp2(STATUS_INVALID_LOCK_RANGE, ERROR_INVALID_LOCK_RANGE); + cmp2(STATUS_INVALID_ACE_CONDITION, ERROR_INVALID_ACE_CONDITION); + cmp2(STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT, ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT); + cmp2(STATUS_NOTIFICATION_GUID_ALREADY_DEFINED, ERROR_NOTIFICATION_GUID_ALREADY_DEFINED); + cmp2(STATUS_INVALID_EXCEPTION_HANDLER, ERROR_INVALID_EXCEPTION_HANDLER); + cmp2(STATUS_DUPLICATE_PRIVILEGES, ERROR_DUPLICATE_PRIVILEGES); + cmp2(STATUS_NOT_ALLOWED_ON_SYSTEM_FILE, ERROR_NOT_ALLOWED_ON_SYSTEM_FILE); + cmp2(STATUS_REPAIR_NEEDED, ERROR_DATA_LOST_REPAIR); + cmp2(STATUS_QUOTA_NOT_ENABLED, ERROR_NOT_SUPPORTED); + cmp2(STATUS_NO_APPLICATION_PACKAGE, APPMODEL_ERROR_NO_PACKAGE); + cmp2(STATUS_FILE_METADATA_OPTIMIZATION_IN_PROGRESS, ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS); + cmp2(STATUS_NOT_SAME_OBJECT, ERROR_NOT_SAME_OBJECT); + cmp2(STATUS_FATAL_MEMORY_EXHAUSTION, ERROR_NOT_ENOUGH_MEMORY); + cmp2(STATUS_ERROR_PROCESS_NOT_IN_JOB, ERROR_PROCESS_NOT_IN_JOB); + cmp2(STATUS_CPU_SET_INVALID, ERROR_CPU_SET_INVALID); + cmp2(STATUS_NETWORK_OPEN_RESTRICTION, ERROR_NETWORK_ACCESS_DENIED); + cmp2(STATUS_RECOVERY_FAILURE, ERROR_RECOVERY_FAILURE); + cmp2(STATUS_WX86_INTERNAL_ERROR, ERROR_WX86_ERROR); + cmp2(STATUS_WX86_FLOAT_STACK_CHECK, ERROR_WX86_ERROR); + cmp2(STATUS_PWD_TOO_LONG, ERROR_PWD_TOO_LONG); + cmp2(STATUS_STOWED_EXCEPTION, ERROR_INTERNAL_ERROR); + cmp2(STATUS_CONTEXT_STOWED_EXCEPTION, ERROR_INTERNAL_ERROR); + cmp2(STATUS_DS_RIDMGR_DISABLED, ERROR_DS_RIDMGR_DISABLED); + cmp2(STATUS_POWER_STATE_INVALID, ERROR_NOT_READY); + cmp2(STATUS_PASSWORD_CHANGE_REQUIRED, ERROR_PASSWORD_CHANGE_REQUIRED); + cmp2(STATUS_LOST_MODE_LOGON_RESTRICTION, ERROR_LOST_MODE_LOGON_RESTRICTION); + cmp2(STATUS_DEBUGGER_INACTIVE, ERROR_DEBUGGER_INACTIVE); + cmp2(STATUS_APPHELP_BLOCK, ERROR_APPHELP_BLOCK); + cmp2(STATUS_NO_SECRETS, ERROR_NO_SECRETS); + cmp2(STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY, ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY); + cmp2(STATUS_FAILED_STACK_SWITCH, ERROR_NOT_ENOUGH_MEMORY); + cmp2(STATUS_HEAP_CORRUPTION, ERROR_INTERNAL_ERROR); + cmp2(STATUS_DRIVER_FAILED_PRIOR_UNLOAD, ERROR_DRIVER_FAILED_PRIOR_UNLOAD); + cmp2(STATUS_SMARTCARD_SILENT_CONTEXT, NTE_SILENT_CONTEXT); + cmp2(STATUS_PER_USER_TRUST_QUOTA_EXCEEDED, ERROR_PER_USER_TRUST_QUOTA_EXCEEDED); + cmp2(STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED, ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED); + cmp2(STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED, ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED); + cmp2(STATUS_DS_NAME_NOT_UNIQUE, ERROR_DS_NAME_NOT_UNIQUE); + cmp2(STATUS_DS_DUPLICATE_ID_FOUND, ERROR_DS_DUPLICATE_ID_FOUND); + cmp2(STATUS_DS_GROUP_CONVERSION_ERROR, ERROR_DS_GROUP_CONVERSION_ERROR); + cmp2(STATUS_VOLSNAP_PREPARE_HIBERNATE, ERROR_VOLSNAP_PREPARE_HIBERNATE); + cmp2(STATUS_USER2USER_REQUIRED, ERROR_LOGON_FAILURE); + cmp2(STATUS_STACK_BUFFER_OVERRUN, ERROR_STACK_BUFFER_OVERRUN); + cmp2(STATUS_NO_S4U_PROT_SUPPORT, SEC_E_NO_S4U_PROT_SUPPORT); + cmp2(STATUS_CROSSREALM_DELEGATION_FAILURE, SEC_E_CROSSREALM_DELEGATION_FAILURE); + cmp2(STATUS_REVOCATION_OFFLINE_KDC, SEC_E_REVOCATION_OFFLINE_KDC); + cmp2(STATUS_ISSUING_CA_UNTRUSTED_KDC, SEC_E_ISSUING_CA_UNTRUSTED_KDC); + cmp2(STATUS_KDC_CERT_EXPIRED, SEC_E_KDC_CERT_EXPIRED); + cmp2(STATUS_KDC_CERT_REVOKED, SEC_E_KDC_CERT_REVOKED); + cmp2(STATUS_PARAMETER_QUOTA_EXCEEDED, ERROR_PARAMETER_QUOTA_EXCEEDED); + cmp2(STATUS_HIBERNATION_FAILURE, ERROR_HIBERNATION_FAILURE); + cmp2(STATUS_DELAY_LOAD_FAILED, ERROR_DELAY_LOAD_FAILED); + cmp2(STATUS_AUTHENTICATION_FIREWALL_FAILED, ERROR_AUTHENTICATION_FIREWALL_FAILED); + cmp2(STATUS_VDM_DISALLOWED, ERROR_VDM_DISALLOWED); + cmp2(STATUS_HUNG_DISPLAY_DRIVER_THREAD, ERROR_HUNG_DISPLAY_DRIVER_THREAD); + cmp2(STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE, ERROR_NOT_ENOUGH_MEMORY); + cmp2(STATUS_INVALID_CRUNTIME_PARAMETER, ERROR_INVALID_CRUNTIME_PARAMETER); + cmp2(STATUS_NTLM_BLOCKED, ERROR_NTLM_BLOCKED); + cmp2(STATUS_DS_SRC_SID_EXISTS_IN_FOREST, ERROR_DS_SRC_SID_EXISTS_IN_FOREST); + cmp2(STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST, ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST); + cmp2(STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST, ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST); + cmp2(STATUS_INVALID_USER_PRINCIPAL_NAME, ERROR_INVALID_USER_PRINCIPAL_NAME); + cmp2(STATUS_FATAL_USER_CALLBACK_EXCEPTION, ERROR_FATAL_APP_EXIT); + cmp2(STATUS_ASSERTION_FAILURE, ERROR_ASSERTION_FAILURE); + cmp2(STATUS_VERIFIER_STOP, ERROR_VERIFIER_STOP); + cmp2(STATUS_CALLBACK_POP_STACK, ERROR_CALLBACK_POP_STACK); + cmp2(STATUS_INCOMPATIBLE_DRIVER_BLOCKED, ERROR_DRIVER_BLOCKED); + cmp2(STATUS_HIVE_UNLOADED, ERROR_KEY_DELETED); + cmp2(STATUS_COMPRESSION_DISABLED, ERROR_COMPRESSION_DISABLED); + cmp2(STATUS_FILE_SYSTEM_LIMITATION, ERROR_FILE_SYSTEM_LIMITATION); + cmp2(STATUS_INVALID_IMAGE_HASH, ERROR_INVALID_IMAGE_HASH); + cmp2(STATUS_NOT_CAPABLE, ERROR_NOT_CAPABLE); + cmp2(STATUS_REQUEST_OUT_OF_SEQUENCE, ERROR_REQUEST_OUT_OF_SEQUENCE); + cmp2(STATUS_IMPLEMENTATION_LIMIT, ERROR_IMPLEMENTATION_LIMIT); + cmp2(STATUS_NO_SECURITY_CONTEXT, SEC_E_NO_CONTEXT); + cmp2(STATUS_PKU2U_CERT_FAILURE, SEC_E_PKU2U_CERT_FAILURE); + cmp2(STATUS_BEYOND_VDL, ERROR_BEYOND_VDL); + cmp2(STATUS_ENCOUNTERED_WRITE_IN_PROGRESS, ERROR_BUSY); + cmp2(STATUS_PTE_CHANGED, ERROR_BUSY); + cmp2(STATUS_PURGE_FAILED, ERROR_USER_MAPPED_FILE); + cmp2(STATUS_CRED_REQUIRES_CONFIRMATION, ERROR_CRED_REQUIRES_CONFIRMATION); + cmp2(STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE, ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE); + cmp2(STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER, ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER); + cmp2(STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE, ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE); + cmp2(STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE, ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE); + cmp2(STATUS_CS_ENCRYPTION_FILE_NOT_CSE, ERROR_CS_ENCRYPTION_FILE_NOT_CSE); + cmp2(STATUS_INVALID_LABEL, ERROR_INVALID_LABEL); + cmp2(STATUS_DRIVER_PROCESS_TERMINATED, ERROR_DRIVER_PROCESS_TERMINATED); + cmp2(STATUS_AMBIGUOUS_SYSTEM_DEVICE, ERROR_AMBIGUOUS_SYSTEM_DEVICE); + cmp2(STATUS_SYSTEM_DEVICE_NOT_FOUND, ERROR_SYSTEM_DEVICE_NOT_FOUND); + cmp2(STATUS_RESTART_BOOT_APPLICATION, ERROR_RESTART_APPLICATION); + cmp2(STATUS_INSUFFICIENT_NVRAM_RESOURCES, ERROR_NO_NVRAM_RESOURCES); + cmp2(STATUS_INVALID_SESSION, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREAD_ALREADY_IN_SESSION, ERROR_INVALID_PARAMETER); + cmp2(STATUS_THREAD_NOT_IN_SESSION, ERROR_INVALID_PARAMETER); + cmp2(STATUS_INVALID_WEIGHT, ERROR_INVALID_PARAMETER); + cmp2(STATUS_REQUEST_PAUSED, ERROR_REQUEST_PAUSED); + cmp2(STATUS_NO_RANGES_PROCESSED, ERROR_NO_RANGES_PROCESSED); + cmp2(STATUS_DISK_RESOURCES_EXHAUSTED, ERROR_DISK_RESOURCES_EXHAUSTED); + cmp2(STATUS_NEEDS_REMEDIATION, ERROR_NEEDS_REMEDIATION); + cmp2(STATUS_DEVICE_FEATURE_NOT_SUPPORTED, ERROR_DEVICE_FEATURE_NOT_SUPPORTED); + cmp2(STATUS_DEVICE_UNREACHABLE, ERROR_DEVICE_UNREACHABLE); + cmp2(STATUS_INVALID_TOKEN, ERROR_INVALID_TOKEN); + cmp2(STATUS_SERVER_UNAVAILABLE, ERROR_NETNAME_DELETED); + cmp2(STATUS_FILE_NOT_AVAILABLE, ERROR_SHARING_VIOLATION); + cmp2(STATUS_DEVICE_INSUFFICIENT_RESOURCES, ERROR_DEVICE_NO_RESOURCES); + cmp2(STATUS_PACKAGE_UPDATING, ERROR_PACKAGE_UPDATING); + cmp2(STATUS_NOT_READ_FROM_COPY, ERROR_NOT_READ_FROM_COPY); + cmp2(STATUS_FT_WRITE_FAILURE, ERROR_FT_WRITE_FAILURE); + cmp2(STATUS_FT_DI_SCAN_REQUIRED, ERROR_FT_DI_SCAN_REQUIRED); + cmp2(STATUS_OBJECT_NOT_EXTERNALLY_BACKED, ERROR_OBJECT_NOT_EXTERNALLY_BACKED); + cmp2(STATUS_EXTERNAL_BACKING_PROVIDER_UNKNOWN, ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN); + cmp2(STATUS_COMPRESSION_NOT_BENEFICIAL, ERROR_COMPRESSION_NOT_BENEFICIAL); + cmp2(STATUS_DATA_CHECKSUM_ERROR, ERROR_DATA_CHECKSUM_ERROR); + cmp2(STATUS_INTERMIXED_KERNEL_EA_OPERATION, ERROR_INTERMIXED_KERNEL_EA_OPERATION); + cmp2(STATUS_TRIM_READ_ZERO_NOT_SUPPORTED, ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED); + cmp2(STATUS_TOO_MANY_SEGMENT_DESCRIPTORS, ERROR_TOO_MANY_DESCRIPTORS); + cmp2(STATUS_INVALID_OFFSET_ALIGNMENT, ERROR_OFFSET_ALIGNMENT_VIOLATION); + cmp2(STATUS_INVALID_FIELD_IN_PARAMETER_LIST, ERROR_INVALID_FIELD_IN_PARAMETER_LIST); + cmp2(STATUS_OPERATION_IN_PROGRESS, ERROR_OPERATION_IN_PROGRESS); + cmp2(STATUS_INVALID_INITIATOR_TARGET_PATH, ERROR_BAD_DEVICE_PATH); + cmp2(STATUS_SCRUB_DATA_DISABLED, ERROR_SCRUB_DATA_DISABLED); + cmp2(STATUS_NOT_REDUNDANT_STORAGE, ERROR_NOT_REDUNDANT_STORAGE); + cmp2(STATUS_RESIDENT_FILE_NOT_SUPPORTED, ERROR_RESIDENT_FILE_NOT_SUPPORTED); + cmp2(STATUS_COMPRESSED_FILE_NOT_SUPPORTED, ERROR_COMPRESSED_FILE_NOT_SUPPORTED); + cmp2(STATUS_DIRECTORY_NOT_SUPPORTED, ERROR_DIRECTORY_NOT_SUPPORTED); + cmp2(STATUS_IO_OPERATION_TIMEOUT, ERROR_TIMEOUT); + cmp2(STATUS_SYSTEM_NEEDS_REMEDIATION, ERROR_SYSTEM_NEEDS_REMEDIATION); + cmp2(STATUS_APPX_INTEGRITY_FAILURE_CLR_NGEN, ERROR_APPX_INTEGRITY_FAILURE_CLR_NGEN); + cmp2(STATUS_SHARE_UNAVAILABLE, ERROR_NETNAME_DELETED); + cmp2(STATUS_APISET_NOT_HOSTED, ERROR_MOD_NOT_FOUND); + cmp2(STATUS_APISET_NOT_PRESENT, ERROR_MOD_NOT_FOUND); + cmp2(STATUS_DEVICE_HARDWARE_ERROR, ERROR_DEVICE_HARDWARE_ERROR); + cmp2(STATUS_FIRMWARE_SLOT_INVALID, STG_E_FIRMWARE_SLOT_INVALID); + cmp2(STATUS_FIRMWARE_IMAGE_INVALID, STG_E_FIRMWARE_IMAGE_INVALID); + cmp2(STATUS_STORAGE_TOPOLOGY_ID_MISMATCH, ERROR_STORAGE_TOPOLOGY_ID_MISMATCH); + cmp2(STATUS_WIM_NOT_BOOTABLE, ERROR_GEN_FAILURE); + cmp2(STATUS_BLOCKED_BY_PARENTAL_CONTROLS, ERROR_BLOCKED_BY_PARENTAL_CONTROLS); + cmp2(STATUS_NEEDS_REGISTRATION, ERROR_NEEDS_REGISTRATION); + cmp2(STATUS_QUOTA_ACTIVITY, ERROR_QUOTA_ACTIVITY); + cmp2(STATUS_CALLBACK_INVOKE_INLINE, ERROR_CALLBACK_INVOKE_INLINE); + cmp2(STATUS_BLOCK_TOO_MANY_REFERENCES, ERROR_BLOCK_TOO_MANY_REFERENCES); + cmp2(STATUS_MARKED_TO_DISALLOW_WRITES, ERROR_MARKED_TO_DISALLOW_WRITES); + cmp2(STATUS_NETWORK_ACCESS_DENIED_EDP, ERROR_NETWORK_ACCESS_DENIED_EDP); + cmp2(STATUS_ENCLAVE_FAILURE, ERROR_ENCLAVE_FAILURE); + cmp2(STATUS_PNP_NO_COMPAT_DRIVERS, ERROR_NO_MATCH); + cmp2(STATUS_PNP_DRIVER_PACKAGE_NOT_FOUND, ERROR_FILE_NOT_FOUND); + cmp2(STATUS_PNP_DRIVER_CONFIGURATION_NOT_FOUND, ERROR_NOT_FOUND); + cmp2(STATUS_PNP_DRIVER_CONFIGURATION_INCOMPLETE, ERROR_SET_NOT_FOUND); + cmp2(STATUS_PNP_FUNCTION_DRIVER_REQUIRED, ERROR_NOT_CAPABLE); + cmp2(STATUS_PNP_DEVICE_CONFIGURATION_PENDING, ERROR_NOT_READY); + cmp2(STATUS_DEVICE_HINT_NAME_BUFFER_TOO_SMALL, ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL); + cmp2(STATUS_PACKAGE_NOT_AVAILABLE, APPMODEL_ERROR_PACKAGE_NOT_AVAILABLE); + cmp2(STATUS_DEVICE_IN_MAINTENANCE, ERROR_DEVICE_IN_MAINTENANCE); + cmp2(STATUS_NOT_SUPPORTED_ON_DAX, ERROR_NOT_SUPPORTED_ON_DAX); + cmp2(STATUS_FREE_SPACE_TOO_FRAGMENTED, ERROR_DISK_TOO_FRAGMENTED); + cmp2(STATUS_DAX_MAPPING_EXISTS, ERROR_DAX_MAPPING_EXISTS); + cmp2(STATUS_CHILD_PROCESS_BLOCKED, ERROR_CHILD_PROCESS_BLOCKED); + cmp2(STATUS_STORAGE_LOST_DATA_PERSISTENCE, ERROR_STORAGE_LOST_DATA_PERSISTENCE); + cmp2(STATUS_VRF_CFG_ENABLED, ERROR_VRF_CFG_ENABLED); + cmp2(STATUS_PARTITION_TERMINATING, ERROR_PARTITION_TERMINATING); + cmp2(STATUS_EXTERNAL_SYSKEY_NOT_SUPPORTED, ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED); + cmp2(STATUS_FAIL_FAST_EXCEPTION, ERROR_FAIL_FAST_EXCEPTION); + cmp2(STATUS_IMAGE_CERT_REVOKED, CERT_E_REVOKED); + cmp2(STATUS_DYNAMIC_CODE_BLOCKED, ERROR_DYNAMIC_CODE_BLOCKED); + cmp2(STATUS_IMAGE_CERT_EXPIRED, CERT_E_EXPIRED); + cmp2(STATUS_STRICT_CFG_VIOLATION, ERROR_STRICT_CFG_VIOLATION); + cmp2(STATUS_SET_CONTEXT_DENIED, ERROR_SET_CONTEXT_DENIED); + cmp2(STATUS_CROSS_PARTITION_VIOLATION, ERROR_CROSS_PARTITION_VIOLATION); + cmp2(STATUS_PORT_CLOSED, ERROR_INTERNAL_ERROR); + cmp2(STATUS_MESSAGE_LOST, ERROR_INTERNAL_ERROR); + cmp2(STATUS_INVALID_MESSAGE, ERROR_INVALID_PARAMETER); + cmp2(STATUS_REQUEST_CANCELED, ERROR_INTERNAL_ERROR); + cmp2(STATUS_RECURSIVE_DISPATCH, ERROR_NOT_SUPPORTED); + cmp2(STATUS_LPC_RECEIVE_BUFFER_EXPECTED, ERROR_INVALID_PARAMETER); + cmp2(STATUS_LPC_INVALID_CONNECTION_USAGE, ERROR_INVALID_PARAMETER); + cmp2(STATUS_LPC_REQUESTS_NOT_ALLOWED, ERROR_NOT_SUPPORTED); + cmp2(STATUS_RESOURCE_IN_USE, ERROR_INTERNAL_ERROR); + cmp2(STATUS_HARDWARE_MEMORY_ERROR, ERROR_MEMORY_HARDWARE); + cmp2(STATUS_THREADPOOL_HANDLE_EXCEPTION, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED, ERROR_INVALID_HANDLE); + cmp2(STATUS_THREADPOOL_RELEASED_DURING_OPERATION, ERROR_INVALID_HANDLE); + cmp2(STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING, ERROR_INVALID_FUNCTION); + cmp2(STATUS_APC_RETURNED_WHILE_IMPERSONATING, ERROR_INVALID_FUNCTION); + cmp2(STATUS_PROCESS_IS_PROTECTED, ERROR_PROCESS_IS_PROTECTED); + cmp2(STATUS_MCA_EXCEPTION, ERROR_MCA_EXCEPTION); + cmp2(STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE, ERROR_LOGON_FAILURE); + cmp2(STATUS_SYMLINK_CLASS_DISABLED, ERROR_SYMLINK_CLASS_DISABLED); + cmp2(STATUS_INVALID_IDN_NORMALIZATION, ERROR_INVALID_NAME); + cmp2(STATUS_NO_UNICODE_TRANSLATION, ERROR_NO_UNICODE_TRANSLATION); + cmp2(STATUS_ALREADY_REGISTERED, ERROR_INTERNAL_ERROR); + cmp2(STATUS_CONTEXT_MISMATCH, ERROR_INTERNAL_ERROR); + cmp2(STATUS_PORT_ALREADY_HAS_COMPLETION_LIST, ERROR_INTERNAL_ERROR); + cmp2(STATUS_CALLBACK_RETURNED_THREAD_PRIORITY, ERROR_INVALID_FUNCTION); + cmp2(STATUS_INVALID_THREAD, ERROR_INVALID_PARAMETER); + cmp2(STATUS_CALLBACK_RETURNED_TRANSACTION, ERROR_INVALID_FUNCTION); + cmp2(STATUS_CALLBACK_RETURNED_LDR_LOCK, ERROR_INVALID_FUNCTION); + cmp2(STATUS_CALLBACK_RETURNED_LANG, ERROR_INVALID_FUNCTION); + cmp2(STATUS_CALLBACK_RETURNED_PRI_BACK, ERROR_INVALID_FUNCTION); + cmp2(STATUS_CALLBACK_RETURNED_THREAD_AFFINITY, ERROR_INVALID_FUNCTION); + cmp2(STATUS_LPC_HANDLE_COUNT_EXCEEDED, RPC_S_SYSTEM_HANDLE_COUNT_EXCEEDED); + cmp2(STATUS_EXECUTABLE_MEMORY_WRITE, ERROR_GEN_FAILURE); + cmp2(STATUS_KERNEL_EXECUTABLE_MEMORY_WRITE, ERROR_GEN_FAILURE); + cmp2(STATUS_ATTACHED_EXECUTABLE_MEMORY_WRITE, ERROR_GEN_FAILURE); + cmp2(STATUS_TRIGGERED_EXECUTABLE_MEMORY_WRITE, ERROR_GEN_FAILURE); + cmp2(STATUS_DISK_REPAIR_DISABLED, ERROR_DISK_REPAIR_DISABLED); + cmp2(STATUS_DS_DOMAIN_RENAME_IN_PROGRESS, ERROR_DS_DOMAIN_RENAME_IN_PROGRESS); + cmp2(STATUS_DISK_QUOTA_EXCEEDED, ERROR_DISK_QUOTA_EXCEEDED); + cmp2(STATUS_CONTENT_BLOCKED, ERROR_CONTENT_BLOCKED); + cmp2(STATUS_BAD_CLUSTERS, ERROR_BAD_CLUSTERS); + cmp2(STATUS_VOLUME_DIRTY, ERROR_VOLUME_DIRTY); + cmp2(STATUS_DISK_REPAIR_UNSUCCESSFUL, ERROR_DISK_REPAIR_UNSUCCESSFUL); + cmp2(STATUS_CORRUPT_LOG_OVERFULL, ERROR_CORRUPT_LOG_OVERFULL); + cmp2(STATUS_CORRUPT_LOG_CORRUPTED, ERROR_CORRUPT_LOG_CORRUPTED); + cmp2(STATUS_CORRUPT_LOG_UNAVAILABLE, ERROR_CORRUPT_LOG_UNAVAILABLE); + cmp2(STATUS_CORRUPT_LOG_DELETED_FULL, ERROR_CORRUPT_LOG_DELETED_FULL); + cmp2(STATUS_CORRUPT_LOG_CLEARED, ERROR_CORRUPT_LOG_CLEARED); + cmp2(STATUS_ORPHAN_NAME_EXHAUSTED, ERROR_ORPHAN_NAME_EXHAUSTED); + cmp2(STATUS_PROACTIVE_SCAN_IN_PROGRESS, ERROR_RETRY); + cmp2(STATUS_ENCRYPTED_IO_NOT_POSSIBLE, ERROR_ENCRYPTED_IO_NOT_POSSIBLE); + cmp2(STATUS_CORRUPT_LOG_UPLEVEL_RECORDS, ERROR_INTERNAL_ERROR); + cmp2(STATUS_FILE_CHECKED_OUT, ERROR_FILE_CHECKED_OUT); + cmp2(STATUS_CHECKOUT_REQUIRED, ERROR_CHECKOUT_REQUIRED); + cmp2(STATUS_BAD_FILE_TYPE, ERROR_BAD_FILE_TYPE); + cmp2(STATUS_FILE_TOO_LARGE, ERROR_FILE_TOO_LARGE); + cmp2(STATUS_FORMS_AUTH_REQUIRED, ERROR_FORMS_AUTH_REQUIRED); + cmp2(STATUS_VIRUS_INFECTED, ERROR_VIRUS_INFECTED); + cmp2(STATUS_VIRUS_DELETED, ERROR_VIRUS_DELETED); + cmp2(STATUS_BAD_MCFG_TABLE, ERROR_BAD_MCFG_TABLE); + cmp2(STATUS_CANNOT_BREAK_OPLOCK, ERROR_CANNOT_BREAK_OPLOCK); + cmp2(STATUS_BAD_KEY, NTE_BAD_KEY); + cmp2(STATUS_BAD_DATA, NTE_BAD_DATA); + cmp2(STATUS_NO_KEY, NTE_NO_KEY); + cmp2(STATUS_FILE_HANDLE_REVOKED, ERROR_FILE_HANDLE_REVOKED); + cmp2(RPC_NT_PROXY_ACCESS_DENIED, RPC_S_PROXY_ACCESS_DENIED); + cmp2(RPC_NT_COOKIE_AUTH_FAILED, RPC_S_COOKIE_AUTH_FAILED); + cmp2(STATUS_RDP_PROTOCOL_ERROR, ERROR_RDP_PROTOCOL_ERROR); + cmp2(STATUS_CTX_LOGON_DISABLED, ERROR_CTX_LOGON_DISABLED); + cmp2(STATUS_CTX_SECURITY_LAYER_ERROR, ERROR_CTX_SECURITY_LAYER_ERROR); + cmp2(STATUS_TS_INCOMPATIBLE_SESSIONS, ERROR_TS_INCOMPATIBLE_SESSIONS); + cmp2(STATUS_TS_VIDEO_SUBSYSTEM_ERROR, ERROR_TS_VIDEO_SUBSYSTEM_ERROR); + cmp2(STATUS_MUI_FILE_NOT_FOUND, ERROR_MUI_FILE_NOT_FOUND); + cmp2(STATUS_MUI_INVALID_FILE, ERROR_MUI_INVALID_FILE); + cmp2(STATUS_MUI_INVALID_RC_CONFIG, ERROR_MUI_INVALID_RC_CONFIG); + cmp2(STATUS_MUI_INVALID_LOCALE_NAME, ERROR_MUI_INVALID_LOCALE_NAME); + cmp2(STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME, ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME); + cmp2(STATUS_MUI_FILE_NOT_LOADED, ERROR_MUI_FILE_NOT_LOADED); + cmp2(STATUS_RESOURCE_ENUM_USER_STOP, ERROR_RESOURCE_ENUM_USER_STOP); + cmp2(STATUS_CLUSTER_NO_NET_ADAPTERS, ERROR_CLUSTER_NO_NET_ADAPTERS); + cmp2(STATUS_CLUSTER_POISONED, ERROR_CLUSTER_POISONED); + cmp2(STATUS_CLUSTER_NON_CSV_PATH, ERROR_NON_CSV_PATH); + cmp2(STATUS_CLUSTER_CSV_VOLUME_NOT_LOCAL, ERROR_CSV_VOLUME_NOT_LOCAL); + cmp2(STATUS_CLUSTER_CSV_READ_OPLOCK_BREAK_IN_PROGRESS, ERROR_RETRY); + cmp2(STATUS_CLUSTER_CSV_AUTO_PAUSE_ERROR, ERROR_RETRY); + cmp2(STATUS_CLUSTER_CSV_REDIRECTED, ERROR_NOT_SUPPORTED); + cmp2(STATUS_CLUSTER_CSV_NOT_REDIRECTED, ERROR_NOT_SUPPORTED); + cmp2(STATUS_CLUSTER_CSV_VOLUME_DRAINING, ERROR_NOT_SUPPORTED); + cmp2(STATUS_CLUSTER_CSV_SNAPSHOT_CREATION_IN_PROGRESS, ERROR_RETRY); + cmp2(STATUS_CLUSTER_CSV_VOLUME_DRAINING_SUCCEEDED_DOWNLEVEL, ERROR_NOT_SUPPORTED); + cmp2(STATUS_CLUSTER_CSV_NO_SNAPSHOTS, ERROR_RETRY); + cmp2(STATUS_CSV_IO_PAUSE_TIMEOUT, ERROR_CLUSTER_CSV_IO_PAUSE_TIMEOUT); + cmp2(STATUS_CLUSTER_CSV_INVALID_HANDLE, ERROR_CLUSTER_CSV_INVALID_HANDLE); + cmp2(STATUS_CLUSTER_CSV_SUPPORTED_ONLY_ON_COORDINATOR, ERROR_CLUSTER_CSV_SUPPORTED_ONLY_ON_COORDINATOR); + cmp2(STATUS_CLUSTER_CAM_TICKET_REPLAY_DETECTED, ERROR_ACCESS_DENIED); + cmp2(STATUS_VOLMGR_DATABASE_FULL, ERROR_VOLMGR_DATABASE_FULL); + cmp2(STATUS_VOLMGR_DISK_CONFIGURATION_CORRUPTED, ERROR_VOLMGR_DISK_CONFIGURATION_CORRUPTED); + cmp2(STATUS_VOLMGR_DISK_CONFIGURATION_NOT_IN_SYNC, ERROR_VOLMGR_DISK_CONFIGURATION_NOT_IN_SYNC); + cmp2(STATUS_VOLMGR_PACK_CONFIG_UPDATE_FAILED, ERROR_VOLMGR_PACK_CONFIG_UPDATE_FAILED); + cmp2(STATUS_VOLMGR_DISK_CONTAINS_NON_SIMPLE_VOLUME, ERROR_VOLMGR_DISK_CONTAINS_NON_SIMPLE_VOLUME); + cmp2(STATUS_VOLMGR_DISK_DUPLICATE, ERROR_VOLMGR_DISK_DUPLICATE); + cmp2(STATUS_VOLMGR_DISK_DYNAMIC, ERROR_VOLMGR_DISK_DYNAMIC); + cmp2(STATUS_VOLMGR_DISK_ID_INVALID, ERROR_VOLMGR_DISK_ID_INVALID); + cmp2(STATUS_VOLMGR_DISK_INVALID, ERROR_VOLMGR_DISK_INVALID); + cmp2(STATUS_VOLMGR_DISK_LAST_VOTER, ERROR_VOLMGR_DISK_LAST_VOTER); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_INVALID, ERROR_VOLMGR_DISK_LAYOUT_INVALID); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_NON_BASIC_BETWEEN_BASIC_PARTITIONS, ERROR_VOLMGR_DISK_LAYOUT_NON_BASIC_BETWEEN_BASIC_PARTITIONS); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_NOT_CYLINDER_ALIGNED, ERROR_VOLMGR_DISK_LAYOUT_NOT_CYLINDER_ALIGNED); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_PARTITIONS_TOO_SMALL, ERROR_VOLMGR_DISK_LAYOUT_PARTITIONS_TOO_SMALL); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_PRIMARY_BETWEEN_LOGICAL_PARTITIONS, ERROR_VOLMGR_DISK_LAYOUT_PRIMARY_BETWEEN_LOGICAL_PARTITIONS); + cmp2(STATUS_VOLMGR_DISK_LAYOUT_TOO_MANY_PARTITIONS, ERROR_VOLMGR_DISK_LAYOUT_TOO_MANY_PARTITIONS); + cmp2(STATUS_VOLMGR_DISK_MISSING, ERROR_VOLMGR_DISK_MISSING); + cmp2(STATUS_VOLMGR_DISK_NOT_EMPTY, ERROR_VOLMGR_DISK_NOT_EMPTY); + cmp2(STATUS_VOLMGR_DISK_NOT_ENOUGH_SPACE, ERROR_VOLMGR_DISK_NOT_ENOUGH_SPACE); + cmp2(STATUS_VOLMGR_DISK_REVECTORING_FAILED, ERROR_VOLMGR_DISK_REVECTORING_FAILED); + cmp2(STATUS_VOLMGR_DISK_SECTOR_SIZE_INVALID, ERROR_VOLMGR_DISK_SECTOR_SIZE_INVALID); + cmp2(STATUS_VOLMGR_DISK_SET_NOT_CONTAINED, ERROR_VOLMGR_DISK_SET_NOT_CONTAINED); + cmp2(STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_MEMBERS, ERROR_VOLMGR_DISK_USED_BY_MULTIPLE_MEMBERS); + cmp2(STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_PLEXES, ERROR_VOLMGR_DISK_USED_BY_MULTIPLE_PLEXES); + cmp2(STATUS_VOLMGR_DYNAMIC_DISK_NOT_SUPPORTED, ERROR_VOLMGR_DYNAMIC_DISK_NOT_SUPPORTED); + cmp2(STATUS_VOLMGR_EXTENT_ALREADY_USED, ERROR_VOLMGR_EXTENT_ALREADY_USED); + cmp2(STATUS_VOLMGR_EXTENT_NOT_CONTIGUOUS, ERROR_VOLMGR_EXTENT_NOT_CONTIGUOUS); + cmp2(STATUS_VOLMGR_EXTENT_NOT_IN_PUBLIC_REGION, ERROR_VOLMGR_EXTENT_NOT_IN_PUBLIC_REGION); + cmp2(STATUS_VOLMGR_EXTENT_NOT_SECTOR_ALIGNED, ERROR_VOLMGR_EXTENT_NOT_SECTOR_ALIGNED); + cmp2(STATUS_VOLMGR_EXTENT_OVERLAPS_EBR_PARTITION, ERROR_VOLMGR_EXTENT_OVERLAPS_EBR_PARTITION); + cmp2(STATUS_VOLMGR_EXTENT_VOLUME_LENGTHS_DO_NOT_MATCH, ERROR_VOLMGR_EXTENT_VOLUME_LENGTHS_DO_NOT_MATCH); + cmp2(STATUS_VOLMGR_FAULT_TOLERANT_NOT_SUPPORTED, ERROR_VOLMGR_FAULT_TOLERANT_NOT_SUPPORTED); + cmp2(STATUS_VOLMGR_INTERLEAVE_LENGTH_INVALID, ERROR_VOLMGR_INTERLEAVE_LENGTH_INVALID); + cmp2(STATUS_VOLMGR_MAXIMUM_REGISTERED_USERS, ERROR_VOLMGR_MAXIMUM_REGISTERED_USERS); + cmp2(STATUS_VOLMGR_MEMBER_IN_SYNC, ERROR_VOLMGR_MEMBER_IN_SYNC); + cmp2(STATUS_VOLMGR_MEMBER_INDEX_DUPLICATE, ERROR_VOLMGR_MEMBER_INDEX_DUPLICATE); + cmp2(STATUS_VOLMGR_MEMBER_INDEX_INVALID, ERROR_VOLMGR_MEMBER_INDEX_INVALID); + cmp2(STATUS_VOLMGR_MEMBER_MISSING, ERROR_VOLMGR_MEMBER_MISSING); + cmp2(STATUS_VOLMGR_MEMBER_NOT_DETACHED, ERROR_VOLMGR_MEMBER_NOT_DETACHED); + cmp2(STATUS_VOLMGR_MEMBER_REGENERATING, ERROR_VOLMGR_MEMBER_REGENERATING); + cmp2(STATUS_VOLMGR_ALL_DISKS_FAILED, ERROR_VOLMGR_ALL_DISKS_FAILED); + cmp2(STATUS_VOLMGR_NO_REGISTERED_USERS, ERROR_VOLMGR_NO_REGISTERED_USERS); + cmp2(STATUS_VOLMGR_NO_SUCH_USER, ERROR_VOLMGR_NO_SUCH_USER); + cmp2(STATUS_VOLMGR_NOTIFICATION_RESET, ERROR_VOLMGR_NOTIFICATION_RESET); + cmp2(STATUS_VOLMGR_NUMBER_OF_MEMBERS_INVALID,ERROR_VOLMGR_NUMBER_OF_MEMBERS_INVALID); + cmp2(STATUS_VOLMGR_NUMBER_OF_PLEXES_INVALID, ERROR_VOLMGR_NUMBER_OF_PLEXES_INVALID); + cmp2(STATUS_VOLMGR_PACK_DUPLICATE, ERROR_VOLMGR_PACK_DUPLICATE); + cmp2(STATUS_VOLMGR_PACK_ID_INVALID, ERROR_VOLMGR_PACK_ID_INVALID); + cmp2(STATUS_VOLMGR_PACK_INVALID, ERROR_VOLMGR_PACK_INVALID); + cmp2(STATUS_VOLMGR_PACK_NAME_INVALID, ERROR_VOLMGR_PACK_NAME_INVALID); + cmp2(STATUS_VOLMGR_PACK_OFFLINE, ERROR_VOLMGR_PACK_OFFLINE); + cmp2(STATUS_VOLMGR_PACK_HAS_QUORUM, ERROR_VOLMGR_PACK_HAS_QUORUM); + cmp2(STATUS_VOLMGR_PACK_WITHOUT_QUORUM, ERROR_VOLMGR_PACK_WITHOUT_QUORUM); + cmp2(STATUS_VOLMGR_PARTITION_STYLE_INVALID, ERROR_VOLMGR_PARTITION_STYLE_INVALID); + cmp2(STATUS_VOLMGR_PARTITION_UPDATE_FAILED, ERROR_VOLMGR_PARTITION_UPDATE_FAILED); + cmp2(STATUS_VOLMGR_PLEX_IN_SYNC, ERROR_VOLMGR_PLEX_IN_SYNC); + cmp2(STATUS_VOLMGR_PLEX_INDEX_DUPLICATE, ERROR_VOLMGR_PLEX_INDEX_DUPLICATE); + cmp2(STATUS_VOLMGR_PLEX_INDEX_INVALID, ERROR_VOLMGR_PLEX_INDEX_INVALID); + cmp2(STATUS_VOLMGR_PLEX_LAST_ACTIVE, ERROR_VOLMGR_PLEX_LAST_ACTIVE); + cmp2(STATUS_VOLMGR_PLEX_MISSING, ERROR_VOLMGR_PLEX_MISSING); + cmp2(STATUS_VOLMGR_PLEX_REGENERATING, ERROR_VOLMGR_PLEX_REGENERATING); + cmp2(STATUS_VOLMGR_PLEX_TYPE_INVALID, ERROR_VOLMGR_PLEX_TYPE_INVALID); + cmp2(STATUS_VOLMGR_PLEX_NOT_RAID5, ERROR_VOLMGR_PLEX_NOT_RAID5); + cmp2(STATUS_VOLMGR_PLEX_NOT_SIMPLE, ERROR_VOLMGR_PLEX_NOT_SIMPLE); + cmp2(STATUS_VOLMGR_STRUCTURE_SIZE_INVALID, ERROR_VOLMGR_STRUCTURE_SIZE_INVALID); + cmp2(STATUS_VOLMGR_TOO_MANY_NOTIFICATION_REQUESTS, ERROR_VOLMGR_TOO_MANY_NOTIFICATION_REQUESTS); + cmp2(STATUS_VOLMGR_TRANSACTION_IN_PROGRESS, ERROR_VOLMGR_TRANSACTION_IN_PROGRESS); + cmp2(STATUS_VOLMGR_UNEXPECTED_DISK_LAYOUT_CHANGE, ERROR_VOLMGR_UNEXPECTED_DISK_LAYOUT_CHANGE); + cmp2(STATUS_VOLMGR_VOLUME_CONTAINS_MISSING_DISK, ERROR_VOLMGR_VOLUME_CONTAINS_MISSING_DISK); + cmp2(STATUS_VOLMGR_VOLUME_ID_INVALID, ERROR_VOLMGR_VOLUME_ID_INVALID); + cmp2(STATUS_VOLMGR_VOLUME_LENGTH_INVALID, ERROR_VOLMGR_VOLUME_LENGTH_INVALID); + cmp2(STATUS_VOLMGR_VOLUME_LENGTH_NOT_SECTOR_SIZE_MULTIPLE, ERROR_VOLMGR_VOLUME_LENGTH_NOT_SECTOR_SIZE_MULTIPLE); + cmp2(STATUS_VOLMGR_VOLUME_NOT_MIRRORED, ERROR_VOLMGR_VOLUME_NOT_MIRRORED); + cmp2(STATUS_VOLMGR_VOLUME_NOT_RETAINED, ERROR_VOLMGR_VOLUME_NOT_RETAINED); + cmp2(STATUS_VOLMGR_VOLUME_OFFLINE, ERROR_VOLMGR_VOLUME_OFFLINE); + cmp2(STATUS_VOLMGR_VOLUME_RETAINED, ERROR_VOLMGR_VOLUME_RETAINED); + cmp2(STATUS_VOLMGR_NUMBER_OF_EXTENTS_INVALID, ERROR_VOLMGR_NUMBER_OF_EXTENTS_INVALID); + cmp2(STATUS_VOLMGR_DIFFERENT_SECTOR_SIZE, ERROR_VOLMGR_DIFFERENT_SECTOR_SIZE); + cmp2(STATUS_VOLMGR_BAD_BOOT_DISK, ERROR_VOLMGR_BAD_BOOT_DISK); + cmp2(STATUS_VOLMGR_PACK_CONFIG_OFFLINE, ERROR_VOLMGR_PACK_CONFIG_OFFLINE); + cmp2(STATUS_VOLMGR_PACK_CONFIG_ONLINE, ERROR_VOLMGR_PACK_CONFIG_ONLINE); + cmp2(STATUS_VOLMGR_NOT_PRIMARY_PACK, ERROR_VOLMGR_NOT_PRIMARY_PACK); + cmp2(STATUS_VOLMGR_PACK_LOG_UPDATE_FAILED, ERROR_VOLMGR_PACK_LOG_UPDATE_FAILED); + cmp2(STATUS_VOLMGR_NUMBER_OF_DISKS_IN_PLEX_INVALID, ERROR_VOLMGR_NUMBER_OF_DISKS_IN_PLEX_INVALID); + cmp2(STATUS_VOLMGR_NUMBER_OF_DISKS_IN_MEMBER_INVALID, ERROR_VOLMGR_NUMBER_OF_DISKS_IN_MEMBER_INVALID); + cmp2(STATUS_VOLMGR_VOLUME_MIRRORED, ERROR_VOLMGR_VOLUME_MIRRORED); + cmp2(STATUS_VOLMGR_PLEX_NOT_SIMPLE_SPANNED, ERROR_VOLMGR_PLEX_NOT_SIMPLE_SPANNED); + cmp2(STATUS_VOLMGR_NO_VALID_LOG_COPIES, ERROR_VOLMGR_NO_VALID_LOG_COPIES); + cmp2(STATUS_VOLMGR_PRIMARY_PACK_PRESENT, ERROR_VOLMGR_PRIMARY_PACK_PRESENT); + cmp2(STATUS_VOLMGR_NUMBER_OF_DISKS_INVALID, ERROR_VOLMGR_NUMBER_OF_DISKS_INVALID); + cmp2(STATUS_VOLMGR_MIRROR_NOT_SUPPORTED, ERROR_VOLMGR_MIRROR_NOT_SUPPORTED); + cmp2(STATUS_VOLMGR_RAID5_NOT_SUPPORTED, ERROR_VOLMGR_RAID5_NOT_SUPPORTED); } START_TEST(error) diff --git a/modules/rostests/winetests/ntdll/exception.c b/modules/rostests/winetests/ntdll/exception.c index e13c9771aa5..58a6273aa53 100644 --- a/modules/rostests/winetests/ntdll/exception.c +++ b/modules/rostests/winetests/ntdll/exception.c @@ -20,26 +20,43 @@ #include #include +#include #include "ntstatus.h" #define WIN32_NO_STATUS -#define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winnt.h" #include "winreg.h" +#include "winuser.h" #include "winternl.h" +#include "rtlsupportapi.h" +#include "ddk/wdm.h" +#include "excpt.h" +#include "wine/test.h" +#include "intrin.h" #ifdef __REACTOS__ #include -#else -#include "excpt.h" -#endif -#include "wine/test.h" +#ifdef _M_AMD64 +USHORT __readsegds(void); +USHORT __readseges(void); +USHORT __readsegfs(void); +USHORT __readseggs(void); +USHORT __readsegss(void); +void __cld(void); +void Call_NtRaiseException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, BOOLEAN FirstChance, PVOID pNtRaiseException); +#endif // _M_AMD64 +#endif // __REACTOS__ static void *code_mem; +static HMODULE hntdll; +static BOOL is_arm64ec; static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*); static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*); +static NTSTATUS (WINAPI *pNtQueueApcThread)(HANDLE handle, PNTAPCFUNC func, + ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +static NTSTATUS (WINAPI *pNtContinueEx)(CONTEXT*,KCONTINUE_ARGUMENT*); static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec); static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID); static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); @@ -47,82 +64,258 @@ static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORE static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler); static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func); static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler); +static void (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER filter); +static ULONG64 (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64); +static NTSTATUS (WINAPI *pRtlGetExtendedContextLength)(ULONG context_flags, ULONG *length); +static NTSTATUS (WINAPI *pRtlGetExtendedContextLength2)(ULONG context_flags, ULONG *length, ULONG64 compaction_mask); +static NTSTATUS (WINAPI *pRtlInitializeExtendedContext)(void *context, ULONG context_flags, CONTEXT_EX **context_ex); +static NTSTATUS (WINAPI *pRtlInitializeExtendedContext2)(void *context, ULONG context_flags, CONTEXT_EX **context_ex, + ULONG64 compaction_mask); +static NTSTATUS (WINAPI *pRtlCopyContext)(CONTEXT *dst, DWORD context_flags, CONTEXT *src); +static NTSTATUS (WINAPI *pRtlCopyExtendedContext)(CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src); +static void * (WINAPI *pRtlLocateExtendedFeature)(CONTEXT_EX *context_ex, ULONG feature_id, ULONG *length); +static void * (WINAPI *pRtlLocateLegacyContext)(CONTEXT_EX *context_ex, ULONG *length); +static void (WINAPI *pRtlSetExtendedFeaturesMask)(CONTEXT_EX *context_ex, ULONG64 feature_mask); +static ULONG64 (WINAPI *pRtlGetExtendedFeaturesMask)(CONTEXT_EX *context_ex); +static void * (WINAPI *pRtlPcToFileHeader)(PVOID pc, PVOID *address); +static void (WINAPI *pRtlGetCallersAddress)(void**,void**); +static NTSTATUS (WINAPI *pNtRaiseException)(EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance); static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*); static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code); -static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG); static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); static NTSTATUS (WINAPI *pNtClose)(HANDLE); +static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process); +static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process); +static BOOL (WINAPI *pInitializeContext)(void *buffer, DWORD context_flags, CONTEXT **context, + DWORD *length); +static BOOL (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags, CONTEXT **context, + DWORD *length, ULONG64 compaction_mask); +static void * (WINAPI *pLocateXStateFeature)(CONTEXT *context, DWORD feature_id, DWORD *length); +static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feature_mask); +static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask); +static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD); +#ifndef __i386__ +static VOID (WINAPI *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*); +static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); +static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); +static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*); +static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*); +#endif + +static void *pKiUserApcDispatcher; +static void *pKiUserCallbackDispatcher; +static void *pKiUserExceptionDispatcher; + +#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 + +typedef struct _RTL_UNLOAD_EVENT_TRACE +{ + void *BaseAddress; + SIZE_T SizeOfImage; + ULONG Sequence; + ULONG TimeDateStamp; + ULONG CheckSum; + WCHAR ImageName[32]; +} RTL_UNLOAD_EVENT_TRACE, *PRTL_UNLOAD_EVENT_TRACE; + +static RTL_UNLOAD_EVENT_TRACE *(WINAPI *pRtlGetUnloadEventTrace)(void); +static void (WINAPI *pRtlGetUnloadEventTraceEx)(ULONG **element_size, ULONG **element_count, void **event_trace); #if defined(__x86_64__) -#ifndef __REACTOS__ -typedef struct + +typedef union _UNWIND_CODE { - ULONG Count; struct { - ULONG BeginAddress; - ULONG EndAddress; - ULONG HandlerAddress; - ULONG JumpTarget; - } ScopeRecord[1]; -} SCOPE_TABLE; + BYTE CodeOffset; + BYTE UnwindOp : 4; + BYTE OpInfo : 4; + } s; + USHORT FrameOffset; +} UNWIND_CODE; -typedef struct +typedef struct _UNWIND_INFO { - ULONG64 ControlPc; - ULONG64 ImageBase; - PRUNTIME_FUNCTION FunctionEntry; - ULONG64 EstablisherFrame; - ULONG64 TargetIp; - PCONTEXT ContextRecord; - void* /*PEXCEPTION_ROUTINE*/ LanguageHandler; - PVOID HandlerData; - PUNWIND_HISTORY_TABLE HistoryTable; - ULONG ScopeIndex; -} DISPATCHER_CONTEXT; + BYTE Version : 3; + BYTE Flags : 5; + BYTE SizeOfProlog; + BYTE CountOfCodes; + BYTE FrameRegister : 4; + BYTE FrameOffset : 4; + UNWIND_CODE UnwindCode[1]; /* actually CountOfCodes (aligned) */ +/* + * union + * { + * OPTIONAL ULONG ExceptionHandler; + * OPTIONAL ULONG FunctionEntry; + * }; + * OPTIONAL ULONG ExceptionData[]; + */ +} UNWIND_INFO; -typedef struct _SETJMP_FLOAT128 -{ - unsigned __int64 DECLSPEC_ALIGN(16) Part[2]; -} SETJMP_FLOAT128; - -typedef struct _JUMP_BUFFER -{ - unsigned __int64 Frame; - unsigned __int64 Rbx; - unsigned __int64 Rsp; - unsigned __int64 Rbp; - unsigned __int64 Rsi; - unsigned __int64 Rdi; - unsigned __int64 R12; - unsigned __int64 R13; - unsigned __int64 R14; - unsigned __int64 R15; - unsigned __int64 Rip; - unsigned __int64 Spare; - SETJMP_FLOAT128 Xmm6; - SETJMP_FLOAT128 Xmm7; - SETJMP_FLOAT128 Xmm8; - SETJMP_FLOAT128 Xmm9; - SETJMP_FLOAT128 Xmm10; - SETJMP_FLOAT128 Xmm11; - SETJMP_FLOAT128 Xmm12; - SETJMP_FLOAT128 Xmm13; - SETJMP_FLOAT128 Xmm14; - SETJMP_FLOAT128 Xmm15; -} _JUMP_BUFFER; -#endif // __REACTOS__ - -static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); -static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); -static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); -static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*); static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*); -static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*); -static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*); -static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*); -static int (CDECL *p_setjmp)(_JUMP_BUFFER*); +static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE, WOW64_CONTEXT *); +static NTSTATUS (WINAPI *pRtlWow64SetThreadContext)(HANDLE, const WOW64_CONTEXT *); +static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*); +#endif + +enum debugger_stages +{ + STAGE_RTLRAISE_NOT_HANDLED = 1, + STAGE_RTLRAISE_HANDLE_LAST_CHANCE, + STAGE_OUTPUTDEBUGSTRINGA_CONTINUE, + STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED, + STAGE_OUTPUTDEBUGSTRINGW_CONTINUE, + STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED, + STAGE_RIPEVENT_CONTINUE, + STAGE_RIPEVENT_NOT_HANDLED, + STAGE_SERVICE_CONTINUE, + STAGE_SERVICE_NOT_HANDLED, + STAGE_BREAKPOINT_CONTINUE, + STAGE_BREAKPOINT_NOT_HANDLED, + STAGE_EXCEPTION_INVHANDLE_CONTINUE, + STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED, + STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED, + STAGE_XSTATE, + STAGE_XSTATE_LEGACY_SSE, + STAGE_SEGMENTS, +}; + +static int my_argc; +static char** my_argv; +static BOOL is_wow64; +static BOOL old_wow64; /* Wine old-style wow64 */ +static UINT apc_count; +static BOOL have_vectored_api; +static enum debugger_stages test_stage; + +static void CALLBACK apc_func( ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 ); + ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 ); + ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 ); + apc_count++; +} + +#if defined(__i386__) || defined(__x86_64__) +static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, enum debugger_stages stage) +{ + char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 3072]; + CONTEXT_EX *c_ex; + NTSTATUS status; + YMMCONTEXT *ymm; + CONTEXT *xctx; + DWORD length; + XSTATE *xs; + M128A *xmm; + BOOL bret; + + if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX)) + return; + + if (stage == STAGE_XSTATE) + return; + + length = sizeof(context_buffer); + bret = pInitializeContext(context_buffer, ctx->ContextFlags | CONTEXT_XSTATE, &xctx, &length); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + ymm = pLocateXStateFeature(xctx, XSTATE_AVX, &length); + ok(!!ymm, "Got zero ymm.\n"); + memset(ymm, 0xcc, sizeof(*ymm)); + + xmm = pLocateXStateFeature(xctx, XSTATE_LEGACY_SSE, &length); + ok(length == sizeof(*xmm) * (sizeof(void *) == 8 ? 16 : 8), "Got unexpected length %#lx.\n", length); + ok(!!xmm, "Got zero xmm.\n"); + memset(xmm, 0xcc, length); + + status = pNtGetContextThread(thread, xctx); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); + + c_ex = (CONTEXT_EX *)(xctx + 1); + xs = (XSTATE *)((char *)c_ex + c_ex->XState.Offset); + ok((xs->Mask & 7) == 4 || broken(!xs->Mask) /* Win7 */, + "Got unexpected xs->Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + + ok(xmm[0].Low == 0x200000001, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].Low)); + ok(xmm[0].High == 0x400000003, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].High)); + + ok(ymm->Ymm0.Low == 0x600000005 || broken(!xs->Mask && ymm->Ymm0.Low == 0xcccccccccccccccc) /* Win7 */, + "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.Low)); + ok(ymm->Ymm0.High == 0x800000007 || broken(!xs->Mask && ymm->Ymm0.High == 0xcccccccccccccccc) /* Win7 */, + "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.High)); + + xmm = pLocateXStateFeature(ctx, XSTATE_LEGACY_SSE, &length); + ok(!!xmm, "Got zero xmm.\n"); + + xmm[0].Low = 0x2828282828282828; + xmm[0].High = xmm[0].Low; + ymm->Ymm0.Low = 0x4848484848484848; + ymm->Ymm0.High = ymm->Ymm0.Low; + + status = pNtSetContextThread(thread, xctx); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); +} + +#define check_context_exception_request( a, b ) check_context_exception_request_( a, b, __LINE__ ) +static void check_context_exception_request_( DWORD flags, BOOL hardware_exception, unsigned int line ) +{ + static const DWORD exception_reporting_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE | CONTEXT_SERVICE_ACTIVE; + DWORD expected_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return; + expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE; + ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n", + flags, expected_flags ); +} + +static BOOL test_hwbpt_in_syscall_trap; + +static LONG WINAPI test_hwbpt_in_syscall_handler( EXCEPTION_POINTERS *eptr ) +{ + EXCEPTION_RECORD *rec = eptr->ExceptionRecord; + + test_hwbpt_in_syscall_trap = TRUE; + ok(rec->ExceptionCode == EXCEPTION_SINGLE_STEP, "got %#lx.\n", rec->ExceptionCode); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void test_hwbpt_in_syscall(void) +{ + TEB *teb = NtCurrentTeb(); + NTSTATUS status; + void *handler; + CONTEXT c; + DWORD ind; + BOOL bret; + + ind = TlsAlloc(); + ok(ind < ARRAY_SIZE(teb->TlsSlots), "got %lu.\n", ind); + handler = AddVectoredExceptionHandler(TRUE, test_hwbpt_in_syscall_handler); + memset(&c, 0, sizeof(c)); + c.ContextFlags = CONTEXT_DEBUG_REGISTERS; + c.Dr0 = (ULONG_PTR)&teb->TlsSlots[ind]; + c.Dr7 = 3 | (3 << 16) | (3 << 18); /* read / write 4 byte breakpoint. */ + bret = SetThreadContext(GetCurrentThread(), &c); + ok(bret, "got error %lu.\n", GetLastError()); + test_hwbpt_in_syscall_trap = FALSE; + teb->TlsSlots[ind] = (void *)0xdeadbeef; + ok(test_hwbpt_in_syscall_trap, "expected trap.\n"); + + test_hwbpt_in_syscall_trap = FALSE; + status = NtSetInformationThread(GetCurrentThread(), ThreadZeroTlsCell, &ind, sizeof(ind)); + ok(!status, "got %#lx.\n", status); + ok(!test_hwbpt_in_syscall_trap, "got trap.\n"); + c.Dr7 = 0; + bret = SetThreadContext(GetCurrentThread(), &c); + ok(bret, "got error %lu.\n", GetLastError()); + ok(!teb->TlsSlots[ind], "got %p.\n", teb->TlsSlots[ind]); + RemoveVectoredExceptionHandler(handler); + TlsFree(ind); +} #endif #ifdef __i386__ @@ -131,15 +324,10 @@ static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #define ProcessExecuteFlags 0x22 #define MEM_EXECUTE_OPTION_DISABLE 0x01 #define MEM_EXECUTE_OPTION_ENABLE 0x02 +#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x04 #define MEM_EXECUTE_OPTION_PERMANENT 0x08 #endif -static int my_argc; -static char** my_argv; -static int test_stage; - -static BOOL is_wow64; - /* Test various instruction combinations that cause a protection fault on the i386, * and check what the resulting exception looks like. */ @@ -292,7 +480,6 @@ static const struct exception }; static int got_exception; -static BOOL have_vectored_api; static void run_exception_test(void *handler, const void* context, const void *code, unsigned int code_size, @@ -325,16 +512,15 @@ static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *Exce { PCONTEXT context = ExceptionInfo->ContextRecord; PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode, - rec->ExceptionAddress, context->Eip); - ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n", - rec->ExceptionAddress, (char *)code_mem + 0xb); + ok(rec->ExceptionAddress == (char *)code_mem + 0xb + || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb); if (NtCurrentTeb()->Peb->BeingDebugged) ok((void *)context->Eax == pRtlRaiseException || broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */ - "debugger managed to modify Eax to %x should be %p\n", + "debugger managed to modify Eax to %lx should be %p\n", context->Eax, pRtlRaiseException); /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT @@ -343,14 +529,16 @@ static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *Exce if(rec->ExceptionCode == EXCEPTION_BREAKPOINT) { ok(context->Eip == (DWORD)code_mem + 0xa || - broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */ - "Eip at %x instead of %x or %x\n", context->Eip, + (is_wow64 && context->Eip == (DWORD)code_mem + 0xb) || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %lx instead of %lx or %lx\n", context->Eip, (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb); } else { - ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n", - context->Eip, (DWORD)code_mem + 0xb); + ok(context->Eip == (DWORD)code_mem + 0xb || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 0xb); } /* test if context change is preserved from vectored handler to stack handlers */ @@ -361,11 +549,16 @@ static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *Exce static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { - trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n", + trace( "exception: %08lx flags:%lx addr:%p context: Eip:%lx\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip ); - ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n", - rec->ExceptionAddress, (char *)code_mem + 0xb); + ok(rec->ExceptionAddress == (char *)code_mem + 0xb + || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb); + + ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE) || + broken(context->ContextFlags == CONTEXT_FULL), /* win2003 */ + "wrong context flags %lx\n", context->ContextFlags ); /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT * even if raised by RtlRaiseException @@ -373,23 +566,25 @@ static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTR if(rec->ExceptionCode == EXCEPTION_BREAKPOINT) { ok(context->Eip == (DWORD)code_mem + 0xa || - broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */ - "Eip at %x instead of %x or %x\n", context->Eip, + (is_wow64 && context->Eip == (DWORD)code_mem + 0xb) || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %lx instead of %lx or %lx\n", context->Eip, (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb); } else { - ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n", - context->Eip, (DWORD)code_mem + 0xb); + ok(context->Eip == (DWORD)code_mem + 0xb || + broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */, + "Eip at %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 0xb); } if(have_vectored_api) - ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n", + ok(context->Eax == 0xf00f00f0, "Eax is %lx, should have been set to 0xf00f00f0 in vectored handler\n", context->Eax); /* give the debugger a chance to examine the state a second time */ /* without the exception handler changing Eip */ - if (test_stage == 2) + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return ExceptionContinueSearch; /* Eip in context is decreased by 1 @@ -467,14 +662,16 @@ static DWORD unwind_expected_eax; static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { - trace("exception: %08x flags:%x addr:%p context: Eip:%x\n", + trace("exception: %08lx flags:%lx addr:%p context: Eip:%lx\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip); - ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08x instead of %08x\n", + ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08lx instead of %08lx\n", rec->ExceptionCode, STATUS_UNWIND); - ok(rec->ExceptionAddress == (char *)code_mem + 0x22, "ExceptionAddress at %p instead of %p\n", - rec->ExceptionAddress, (char *)code_mem + 0x22); - ok(context->Eax == unwind_expected_eax, "context->Eax is %08x instead of %08x\n", + ok(rec->ExceptionAddress == (char *)code_mem + 0x22 || broken(TRUE) /* Win10 1709 */, + "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x22); + ok(context->Eip == (DWORD)code_mem + 0x22, "context->Eip is %08lx instead of %08lx\n", + context->Eip, (DWORD)code_mem + 0x22); + ok(context->Eax == unwind_expected_eax, "context->Eax is %08lx instead of %08lx\n", context->Eax, unwind_expected_eax); context->Eax += 1; @@ -524,14 +721,14 @@ static void test_unwind(void) /* test unwind to current frame */ unwind_expected_eax = 0xDEAD0000; retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000); - ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000); + ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08lx instead of %08x\n", retval, 0xDEAD0000); ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n", NtCurrentTeb()->Tib.ExceptionList, frame2); /* unwind to frame1 */ unwind_expected_eax = 0xDEAD0000; retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000); - ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001); + ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08lx instead of %08x\n", retval, 0xDEAD0001); ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n", NtCurrentTeb()->Tib.ExceptionList, frame1); @@ -539,22 +736,22 @@ static void test_unwind(void) NtCurrentTeb()->Tib.ExceptionList = frame1->Prev; } -static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, - CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) +static DWORD prot_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { const struct exception *except = *(const struct exception **)(frame + 1); unsigned int i, parameter_count, entry = except - exceptions; got_exception++; - trace( "exception %u: %x flags:%x addr:%p\n", + trace( "exception %u: %lx flags:%lx addr:%p\n", entry, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress ); ok( rec->ExceptionCode == except->status || (except->alt_status != 0 && rec->ExceptionCode == except->alt_status), - "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status ); - ok( context->Eip == (DWORD_PTR)code_mem + except->offset, - "%u: Unexpected eip %#x/%#lx\n", entry, - context->Eip, (DWORD_PTR)code_mem + except->offset ); + "%u: Wrong exception code %lx/%lx\n", entry, rec->ExceptionCode, except->status ); + ok( context->Eip == (DWORD)code_mem + except->offset, + "%u: Unexpected eip %#lx/%#lx\n", entry, + context->Eip, (DWORD)code_mem + except->offset ); ok( rec->ExceptionAddress == (char*)context->Eip || (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1), "%u: Unexpected exception address %p/%p\n", entry, @@ -568,7 +765,7 @@ static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *fram parameter_count = except->alt_nb_params; ok( rec->NumberParameters == parameter_count, - "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count ); + "%u: Unexpected parameter count %lu/%u\n", entry, rec->NumberParameters, parameter_count ); /* Most CPUs (except Intel Core apparently) report a segment limit violation */ /* instead of page faults for accesses beyond 0xffffffff */ @@ -590,22 +787,19 @@ static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *fram { for (i = 0; i < rec->NumberParameters; i++) ok( rec->ExceptionInformation[i] == except->params[i], - "%u: Wrong parameter %d: %lx/%x\n", + "%u: Wrong parameter %d: %Ix/%lx\n", entry, i, rec->ExceptionInformation[i], except->params[i] ); } else { for (i = 0; i < rec->NumberParameters; i++) ok( rec->ExceptionInformation[i] == except->alt_params[i], - "%u: Wrong parameter %d: %lx/%x\n", + "%u: Wrong parameter %d: %Ix/%lx\n", entry, i, rec->ExceptionInformation[i], except->alt_params[i] ); } skip_params: - /* don't handle exception if it's not the address we expected */ - if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch; - - context->Eip += except->length; + context->Eip = (DWORD_PTR)code_mem + except->offset + except->length; return ExceptionContinueExecution; } @@ -613,7 +807,7 @@ static void test_prot_fault(void) { unsigned int i; - for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++) + for (i = 0; i < ARRAY_SIZE(exceptions); i++) { if (is_wow64 && exceptions[i].wow64_broken && !strcmp( winetest_platform, "windows" )) { @@ -621,7 +815,7 @@ static void test_prot_fault(void) continue; } got_exception = 0; - run_exception_test(handler, &exceptions[i], &exceptions[i].code, + run_exception_test(prot_fault_handler, &exceptions[i], &exceptions[i].code, sizeof(exceptions[i].code), 0); if (!i && !got_exception) { @@ -654,7 +848,7 @@ static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD } #define CHECK_DEBUG_REG(n, m) \ - ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %x, got %x\n", \ + ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %lx, got %lx\n", \ test_num, test->dr##n, ctx.Dr##n) static void check_debug_registers(int test_num, const struct dbgreg_test *test) @@ -664,7 +858,7 @@ static void check_debug_registers(int test_num, const struct dbgreg_test *test) ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; status = pNtGetContextThread(GetCurrentThread(), &ctx); - ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status); CHECK_DEBUG_REG(0, ~0); CHECK_DEBUG_REG(1, ~0); @@ -693,7 +887,7 @@ static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_ /* show that the last single step exception on the popf instruction * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */ ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP, - "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode); + "exception is not EXCEPTION_SINGLE_STEP: %lx\n", rec->ExceptionCode); } return ExceptionContinueExecution; @@ -716,16 +910,13 @@ static const BYTE align_check_code[] = { 0x55, /* push %ebp */ 0x89,0xe5, /* mov %esp,%ebp */ 0x9c, /* pushf */ + 0x9c, /* pushf */ 0x58, /* pop %eax */ 0x0d,0,0,4,0, /* or $0x40000,%eax */ 0x50, /* push %eax */ 0x9d, /* popf */ 0x89,0xe0, /* mov %esp, %eax */ 0x8b,0x40,0x1, /* mov 0x1(%eax), %eax - cause exception */ - 0x9c, /* pushf */ - 0x58, /* pop %eax */ - 0x35,0,0,4,0, /* xor $0x40000,%eax */ - 0x50, /* push %eax */ 0x9d, /* popf */ 0x5d, /* pop %ebp */ 0xc3, /* ret */ @@ -771,20 +962,19 @@ static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD * { got_exception++; ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP, - "wrong exception code: %x\n", rec->ExceptionCode); + "wrong exception code: %lx\n", rec->ExceptionCode); if(got_exception == 1) { /* hw bp exception on first nop */ - ok( context->Eip == (DWORD)code_mem, "eip is wrong: %x instead of %x\n", + ok( context->Eip == (DWORD)code_mem, "eip is wrong: %lx instead of %lx\n", context->Eip, (DWORD)code_mem); ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n"); - ros_skip_flaky ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n"); context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */ context->EFlags |= 0x100; /* enable single stepping */ } else if( got_exception == 2) { /* single step exception on second nop */ - ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n", + ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 1); ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n"); /* depending on the win version the B0 bit is already set here as well @@ -792,7 +982,7 @@ static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD * context->EFlags |= 0x100; } else if( got_exception == 3) { /* hw bp exception on second nop */ - ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n", + ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 1); ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n"); ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n"); @@ -800,7 +990,7 @@ static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD * context->EFlags |= 0x100; } else { /* single step exception on ret */ - ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n", + ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 2); ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n"); @@ -818,7 +1008,7 @@ static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD { ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n", code_mem, rec->ExceptionAddress); - ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip); + ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#lx\n", code_mem, context->Eip); if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */ return ExceptionContinueExecution; @@ -886,7 +1076,7 @@ static void test_exceptions(void) ctx.Dr7 = 3; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; res = pNtSetContextThread( GetCurrentThread(), &ctx); - ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res); + ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res); got_exception = 0; run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0); @@ -897,7 +1087,7 @@ static void test_exceptions(void) /* test that hardware breakpoints are not inherited by created threads */ res = pNtSetContextThread( GetCurrentThread(), &ctx ); - ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res ); + ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res ); h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL ); WaitForSingleObject( h, 10000 ); @@ -906,13 +1096,13 @@ static void test_exceptions(void) h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL ); ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; res = pNtGetContextThread( h, &ctx ); - ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", res ); - ok( ctx.Dr0 == 0, "dr0 %x\n", ctx.Dr0 ); - ok( ctx.Dr7 == 0, "dr7 %x\n", ctx.Dr7 ); + ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res ); + ok( ctx.Dr0 == 0, "dr0 %lx\n", ctx.Dr0 ); + ok( ctx.Dr7 == 0, "dr7 %lx\n", ctx.Dr7 ); ctx.Dr0 = (DWORD)code_mem; ctx.Dr7 = 3; res = pNtSetContextThread( h, &ctx ); - ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res ); + ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res ); ResumeThread( h ); WaitForSingleObject( h, 10000 ); CloseHandle( h ); @@ -920,10 +1110,10 @@ static void test_exceptions(void) ctx.Dr0 = 0; ctx.Dr7 = 0; res = pNtSetContextThread( GetCurrentThread(), &ctx ); - ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res ); + ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res ); } -static void test_debugger(void) +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) { char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; @@ -943,21 +1133,32 @@ static void test_debugger(void) return; } + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); - ok(ret, "could not create child process error: %u\n", GetLastError()); + ok(ret, "could not create child process error: %lu\n", GetLastError()); if (!ret) return; do { - continuestatus = DBG_CONTINUE; - ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + continuestatus = cont_status; + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); + + ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); + ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError()); if (de.dwThreadId != pi.dwThreadId) { - trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode); - ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); + trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode); + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status); continue; } @@ -972,59 +1173,69 @@ static void test_debugger(void) else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { CONTEXT ctx; - int stage; + enum debugger_stages stage; counter++; status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, sizeof(code_mem_address), &size_read); - ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); - ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ctx.ContextFlags = CONTEXT_FULL; + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS | CONTEXT_EXCEPTION_REQUEST; status = pNtGetContextThread(pi.hThread, &ctx); - ok(!status, "NtGetContextThread failed with 0x%x\n", status); + ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING + || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */, + "got %#lx.\n", ctx.ContextFlags); - trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n", + trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx ctx.ContextFlags %#lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, - de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax); + de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax, ctx.ContextFlags); if (counter > 100) { ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n"); pNtTerminateProcess(pi.hProcess, 1); } - else if (counter >= 2) /* skip startup breakpoint */ + else if (counter < 2) /* startup breakpoint */ { - if (stage == 1) - { - ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n", - ctx.Eip, (char *)code_mem_address + 0xb); - /* setting the context from debugger does not affect the context, the exception handlers gets */ - /* uncomment once wine is fixed */ - /* ctx.Eip = 0x12345; */ - ctx.Eax = 0xf00f00f1; + /* breakpoint is inside ntdll */ + void *ntdll = GetModuleHandleA( "ntdll.dll" ); + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll ); + ok( (char *)ctx.Eip >= (char *)ntdll && + (char *)ctx.Eip < (char *)ntdll + nt->OptionalHeader.SizeOfImage, + "wrong eip %p ntdll %p-%p\n", (void *)ctx.Eip, ntdll, + (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); + } + else + { + if (stage == STAGE_RTLRAISE_NOT_HANDLED) + { + ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %lx instead of %p\n", + ctx.Eip, (char *)code_mem_address + 0xb); + /* setting the context from debugger does not affect the context that the + * exception handler gets, except on w2008 */ + ctx.Eip = (UINT_PTR)code_mem_address + 0xd; + ctx.Eax = 0xf00f00f1; /* let the debuggee handle the exception */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } - else if (stage == 2) + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) { if (de.u.Exception.dwFirstChance) { /* debugger gets first chance exception with unmodified ctx.Eip */ - ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n", - ctx.Eip, (char *)code_mem_address + 0xb); - - /* setting the context from debugger does not affect the context, the exception handlers gets */ - /* uncomment once wine is fixed */ - /* ctx.Eip = 0x12345; */ + ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%lx instead of %p\n", + ctx.Eip, (char *)code_mem_address + 0xb); + ctx.Eip = (UINT_PTR)code_mem_address + 0xd; ctx.Eax = 0xf00f00f1; - /* pass exception to debuggee - * exception will not be handled and - * a second chance exception will be raised */ + * exception will not be handled and a second chance exception will be raised */ continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else @@ -1034,117 +1245,169 @@ static void test_debugger(void) if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { ok((char *)ctx.Eip == (char *)code_mem_address + 0xa || - broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb), - "Eip at 0x%x instead of %p\n", + (is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb) || + broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */, + "Eip at 0x%lx instead of %p\n", ctx.Eip, (char *)code_mem_address + 0xa); /* need to fixup Eip for debuggee */ if ((char *)ctx.Eip == (char *)code_mem_address + 0xa) ctx.Eip += 1; } else - ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n", - ctx.Eip, (char *)code_mem_address + 0xb); + ok((char *)ctx.Eip == (char *)code_mem_address + 0xb || + broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */, + "Eip at 0x%lx instead of %p\n", + ctx.Eip, (char *)code_mem_address + 0xb); /* here we handle exception */ } + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); } - else if (stage == 7 || stage == 8) + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, - "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode); + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d, - "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip); + "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Eip); - if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } - else if (stage == 9 || stage == 10) + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, - "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode); + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); ok((char *)ctx.Eip == (char *)code_mem_address + 2, - "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip); + "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 2, ctx.Eip); - if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); } - else if (stage == 11 || stage == 12) + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) { ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, - "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode, + "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, EXCEPTION_INVALID_HANDLE); ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, - "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); + "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); - if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); + } + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, !is_wow64 ); + } + else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) + { + check_context_exception_request( ctx.ContextFlags, TRUE ); + test_debugger_xstate(pi.hThread, &ctx, stage); + } + else if (stage == STAGE_SEGMENTS) + { + USHORT ss; +#if defined(__REACTOS__) && defined(_MSC_VER) + USHORT segss; + __asm mov [segss], ss + ss = segss; +#else + __asm__( "movw %%ss,%0" : "=r" (ss) ); +#endif + ok( ctx.SegSs == ss, "wrong ss %04lx / %04x\n", ctx.SegSs, ss ); + ok( ctx.SegFs != ctx.SegSs, "wrong fs %04lx / %04lx\n", ctx.SegFs, ctx.SegSs ); + if (is_wow64) todo_wine_if( !ctx.SegDs ) /* old wow64 */ + { + ok( ctx.SegDs == ctx.SegSs, "wrong ds %04lx / %04lx\n", ctx.SegDs, ctx.SegSs ); + ok( ctx.SegEs == ctx.SegSs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs ); + ok( ctx.SegGs == ctx.SegSs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs ); + } + else + { + ok( !ctx.SegDs, "wrong ds %04lx / %04lx\n", ctx.SegDs, ctx.SegSs ); + ok( !ctx.SegEs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs ); + ok( !ctx.SegGs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs ); + } + check_context_exception_request( ctx.ContextFlags, TRUE ); } else - ok(FALSE, "unexpected stage %x\n", stage); + ok(FALSE, "unexpected stage %u\n", stage); status = pNtSetContextThread(pi.hThread, &ctx); - ok(!status, "NtSetContextThread failed with 0x%x\n", status); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); } } else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - int stage; -#ifdef __REACTOS__ - /* This will catch our DPRINTs, such as - * "WARNING: RtlpDphTargetDllsLogicInitialize at ..\..\lib\rtl\heappage.c:1283 is UNIMPLEMENTED!" - * so we need a full-size buffer to avoid a stack overflow - */ - char buffer[513]; -#else - char buffer[64]; -#endif + enum debugger_stages stage; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); - ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n"); - ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n", - de.u.DebugString.nDebugStringLength); + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); memset(buffer, 0, sizeof(buffer)); status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, - de.u.DebugString.nDebugStringLength, &size_read); - ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status); + de.u.DebugString.nDebugStringLength * char_size, &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 3 || stage == 4) - ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); - if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; } else if (de.dwDebugEventCode == RIP_EVENT) { - int stage; + enum debugger_stages stage; status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, sizeof(stage), &size_read); - ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); - if (stage == 5 || stage == 6) + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) { - ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n", + ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", de.u.RipInfo.dwError, 0x11223344); - ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n", + ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n", de.u.RipInfo.dwType, 0x55667788); } else ok(FALSE, "unexpected stage %x\n", stage); - if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); - winetest_wait_child_process( pi.hProcess ); + wait_child_process( pi.hProcess ); ret = CloseHandle(pi.hThread); - ok(ret, "error %u\n", GetLastError()); + ok(ret, "error %lu\n", GetLastError()); ret = CloseHandle(pi.hProcess); - ok(ret, "error %u\n", GetLastError()); + ok(ret, "error %lu\n", GetLastError()); return; } @@ -1168,13 +1431,14 @@ static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_R skip("system doesn't support SIMD exceptions\n"); else { ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS, - "exception code: %#x, should be %#x\n", + "exception code: %#lx, should be %#lx\n", rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS); - ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2), - "# of params: %i, should be 1\n", - rec->NumberParameters); - if( rec->NumberParameters == 1 ) - ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]); + ok( rec->NumberParameters == is_wow64 ? 2 : 1, "# of params: %li\n", rec->NumberParameters); + ok( rec->ExceptionInformation[0] == 0, "param #1: %Ix, should be 0\n", rec->ExceptionInformation[0]); + if (rec->NumberParameters == 2) + ok( rec->ExceptionInformation[1] == ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr, + "param #1: %Ix / %lx\n", rec->ExceptionInformation[1], + ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr); } context->Eip += 3; /* skip divps */ } @@ -1316,20 +1580,18 @@ static void test_fpu_exceptions(void) memset(&info, 0, sizeof(info)); run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0); ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK, - "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code); - ok(info.exception_offset == 0x19 || - broken( info.exception_offset == info.eip_offset ), - "Got exception offset %#x, expected 0x19\n", info.exception_offset); - ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset); + "Got exception code %#lx, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code); + ok(info.exception_offset == 0x19 || info.exception_offset == info.eip_offset, + "Got exception offset %#lx, expected 0x19\n", info.exception_offset); + ok(info.eip_offset == 0x1b, "Got EIP offset %#lx, expected 0x1b\n", info.eip_offset); memset(&info, 0, sizeof(info)); run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0); ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO, - "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code); - ok(info.exception_offset == 0x17 || - broken( info.exception_offset == info.eip_offset ), - "Got exception offset %#x, expected 0x17\n", info.exception_offset); - ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset); + "Got exception code %#lx, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code); + ok(info.exception_offset == 0x17 || info.exception_offset == info.eip_offset, + "Got exception offset %#lx, expected 0x17\n", info.exception_offset); + ok(info.eip_offset == 0x19, "Got EIP offset %#lx, expected 0x19\n", info.eip_offset); } struct dpe_exception_info { @@ -1344,9 +1606,9 @@ static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1); ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, - "Exception code %08x\n", rec->ExceptionCode); + "Exception code %08lx\n", rec->ExceptionCode); ok(rec->NumberParameters == 2, - "Parameter count: %d\n", rec->NumberParameters); + "Parameter count: %ld\n", rec->NumberParameters); ok((LPVOID)rec->ExceptionInformation[1] == code_mem, "Exception address: %p, expected %p\n", (LPVOID)rec->ExceptionInformation[1], code_mem); @@ -1369,20 +1631,20 @@ static void test_dpe_exceptions(void) ULONG len; /* Query DEP with len too small */ - stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len); + stat = NtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len); if(stat == STATUS_INVALID_INFO_CLASS) { skip("This software platform does not support DEP\n"); return; } - ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat); + ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08lx\n", stat); /* Query DEP */ - stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len); - ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat); + stat = NtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len); + ok(stat == STATUS_SUCCESS, "querying DEP: status %08lx\n", stat); if(stat == STATUS_SUCCESS) { - ok(len == sizeof val, "returned length: %d\n", len); + ok(len == sizeof val, "returned length: %ld\n", len); if(val & MEM_EXECUTE_OPTION_PERMANENT) { skip("toggling DEP impossible - status locked\n"); @@ -1399,7 +1661,7 @@ static void test_dpe_exceptions(void) /* Enable DEP */ val = MEM_EXECUTE_OPTION_DISABLE; stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val); - ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat); + ok(stat == STATUS_SUCCESS, "enabling DEP: status %08lx\n", stat); } if(can_test_with) @@ -1434,7 +1696,7 @@ static void test_dpe_exceptions(void) /* Disable DEP */ val = MEM_EXECUTE_OPTION_ENABLE; stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val); - ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat); + ok(stat == STATUS_SUCCESS, "disabling DEP: status %08lx\n", stat); } /* page is read without exec here */ @@ -1461,18 +1723,18 @@ static void test_dpe_exceptions(void) /* Turn off DEP permanently */ val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT; stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val); - ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat); + ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08lx\n", stat); } /* Try to turn off DEP */ val = MEM_EXECUTE_OPTION_ENABLE; stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val); - ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat); + ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08lx\n", stat); /* Try to turn on DEP */ val = MEM_EXECUTE_OPTION_DISABLE; stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val); - ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat); + ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08lx\n", stat); } static void test_thread_context(void) @@ -1482,9 +1744,12 @@ static void test_thread_context(void) struct expected { DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip, - SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame; + SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame, + x87_control; } expect; - NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2 ) = (void *)code_mem; + NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2, + DWORD *new_x87_control ) = code_mem; + DWORD new_x87_control; static const BYTE call_func[] = { @@ -1515,10 +1780,20 @@ static void test_thread_context(void) 0x8f, 0x40, 0x3c, /* popl 0x3c(%eax) */ 0xff, 0x75, 0x00, /* pushl 0x0(%ebp) ; previous stack frame */ 0x8f, 0x40, 0x40, /* popl 0x40(%eax) */ + /* pushl $0x47f */ + 0x68, 0x7f, 0x04, 0x00, 0x00, + 0x8f, 0x40, 0x44, /* popl 0x44(%eax) */ + 0xd9, 0x68, 0x44, /* fldcw 0x44(%eax) */ + 0x8b, 0x00, /* mov (%eax),%eax */ 0xff, 0x75, 0x14, /* pushl 0x14(%ebp) */ 0xff, 0x75, 0x10, /* pushl 0x10(%ebp) */ 0xff, 0x55, 0x0c, /* call *0xc(%ebp) */ + + 0x8b, 0x55, 0x18, /* mov 0x18(%ebp),%edx */ + 0x9b, 0xd9, 0x3a, /* fstcw (%edx) */ + 0xdb, 0xe3, /* fninit */ + 0xc9, /* leave */ 0xc3, /* ret */ }; @@ -1526,25 +1801,25 @@ static void test_thread_context(void) memcpy( func_ptr, call_func, sizeof(call_func) ); #define COMPARE(reg) \ - ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg ) + ok( context.reg == expect.reg, "wrong " #reg " %08lx/%08lx\n", context.reg, expect.reg ) memset( &context, 0xcc, sizeof(context) ); memset( &expect, 0xcc, sizeof(expect) ); - func_ptr( &expect, pRtlCaptureContext, &context, 0 ); - trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x " - "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n", + func_ptr( &expect, pRtlCaptureContext, &context, 0, &new_x87_control ); + trace( "expect: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx " + "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx prev=%08lx\n", expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi, expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs, expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame ); - trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x " - "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n", + trace( "actual: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx " + "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n", context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi, context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs, context.SegFs, context.SegGs, context.SegSs, context.EFlags ); ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) || broken( context.ContextFlags == 0xcccccccc ), /* <= vista */ - "wrong flags %08x\n", context.ContextFlags ); + "wrong flags %08lx\n", context.ContextFlags ); COMPARE( Eax ); COMPARE( Ebx ); COMPARE( Ecx ); @@ -1560,22 +1835,23 @@ static void test_thread_context(void) COMPARE( SegSs ); COMPARE( EFlags ); /* Ebp is from the previous stackframe */ - ok( context.Ebp == expect.prev_frame, "wrong Ebp %08x/%08x\n", context.Ebp, expect.prev_frame ); + ok( context.Ebp == expect.prev_frame, "wrong Ebp %08lx/%08lx\n", context.Ebp, expect.prev_frame ); /* Esp is the value on entry to the previous stackframe */ - ok( context.Esp == expect.Ebp + 8, "wrong Esp %08x/%08x\n", context.Esp, expect.Ebp + 8 ); + ok( context.Esp == expect.Ebp + 8, "wrong Esp %08lx/%08lx\n", context.Esp, expect.Ebp + 8 ); memset( &context, 0xcc, sizeof(context) ); memset( &expect, 0xcc, sizeof(expect) ); - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; - status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context ); - ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status ); - trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x " - "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n", + context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; + + status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context, &new_x87_control ); + ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status ); + trace( "expect: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx " + "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx prev=%08lx\n", expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi, expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs, expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame ); - trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x " - "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n", + trace( "actual: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx " + "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n", context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi, context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs, context.SegFs, context.SegGs, context.SegSs, context.EFlags ); @@ -1586,309 +1862,491 @@ static void test_thread_context(void) COMPARE( Ebp ); /* Esp is the stack upon entry to NtGetContextThread */ ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16, - "wrong Esp %08x/%08x\n", context.Esp, expect.Esp ); + "wrong Esp %08lx/%08lx\n", context.Esp, expect.Esp ); /* Eip is somewhere close to the NtGetContextThread implementation */ - ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x10000 && - (char *)context.Eip <= (char *)pNtGetContextThread + 0x10000, - "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread ); - ok( *(WORD *)context.Eip == 0xc483 || *(WORD *)context.Eip == 0x08c2 || *(WORD *)context.Eip == 0x8dc3, - "expected 0xc483 or 0x08c2 or 0x8dc3, got %04x\n", *(WORD *)context.Eip ); + ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x40000 && + (char *)context.Eip <= (char *)pNtGetContextThread + 0x40000, + "wrong Eip %08lx/%08lx\n", context.Eip, (DWORD)pNtGetContextThread ); /* segment registers clear the high word */ - ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs ); - ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs ); - ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08x/%08x\n", context.SegEs, expect.SegEs ); - ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08x/%08x\n", context.SegFs, expect.SegFs ); - ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08x/%08x\n", context.SegGs, expect.SegGs ); - ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08x/%08x\n", context.SegSs, expect.SegGs ); + ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08lx/%08lx\n", context.SegCs, expect.SegCs ); + ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08lx/%08lx\n", context.SegDs, expect.SegDs ); + ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08lx/%08lx\n", context.SegEs, expect.SegEs ); + ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08lx/%08lx\n", context.SegFs, expect.SegFs ); + if (LOWORD(expect.SegGs)) ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08lx/%08lx\n", context.SegGs, expect.SegGs ); + ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08lx/%08lx\n", context.SegSs, expect.SegSs ); + + ok( LOWORD(context.FloatSave.ControlWord) == LOWORD(expect.x87_control), + "wrong x87 control word %#lx/%#lx.\n", context.FloatSave.ControlWord, expect.x87_control ); + ok( LOWORD(expect.x87_control) == LOWORD(new_x87_control), + "x87 control word changed in NtGetContextThread() %#x/%#x.\n", + LOWORD(expect.x87_control), LOWORD(new_x87_control) ); + #undef COMPARE } +static BYTE saved_KiUserExceptionDispatcher_bytes[7]; +static BOOL hook_called; +static void *hook_KiUserExceptionDispatcher_eip; +static void *dbg_except_continue_handler_eip; +static void *hook_exception_address; + +static struct +{ + DWORD old_eax; + DWORD old_edx; + DWORD old_esi; + DWORD old_edi; + DWORD old_ebp; + DWORD old_esp; + DWORD new_eax; + DWORD new_edx; + DWORD new_esi; + DWORD new_edi; + DWORD new_ebp; + DWORD new_esp; +} +test_kiuserexceptiondispatcher_regs; + +static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + ok(hook_called, "Hook was not called.\n"); + + ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); + + got_exception = 1; + dbg_except_continue_handler_eip = (void *)context->Eip; + ++context->Eip; + + context->Eip = (DWORD)code_mem + 0x1c; + context->Eax = 0xdeadbeef; + context->Esi = 0xdeadbeef; + pRtlUnwind(NtCurrentTeb()->Tib.ExceptionList, (void *)context->Eip, rec, (void *)0xdeadbeef); + return ExceptionContinueExecution; +} + +static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e) +{ + EXCEPTION_RECORD *rec = e->ExceptionRecord; + CONTEXT *context = e->ContextRecord; + + trace("dbg_except_continue_vectored_handler, code %#lx, eip %#lx, ExceptionAddress %p.\n", + rec->ExceptionCode, context->Eip, rec->ExceptionAddress); + + ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); + + got_exception = 1; + + if ((ULONG_PTR)rec->ExceptionAddress == context->Eip + 1) + { + /* XP and Vista+ have ExceptionAddress == Eip + 1, Eip is adjusted even + * for software raised breakpoint exception. + * Win2003 has Eip not adjusted and matching ExceptionAddress. + * Win2008 has Eip not adjusted and ExceptionAddress not filled for + * software raised exception. */ + context->Eip = (ULONG_PTR)rec->ExceptionAddress; + } + + return EXCEPTION_CONTINUE_EXECUTION; +} + +/* Use CDECL to leave arguments on stack. */ +static void * CDECL hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context) +{ + CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); + + trace( "rec %p context %p context->Eip %#lx, context->Esp %#lx (%x), ContextFlags %#lx.\n", + rec, context, context->Eip, context->Esp, + (char *)context->Esp - (char *)&rec, context->ContextFlags); + trace( "xstate %lx = %p (%x) %lx\n", xctx->XState.Offset, (char *)xctx + xctx->XState.Offset, + (char *)xctx + xctx->XState.Offset - (char *)&rec, xctx->XState.Length ); + + ok( (char *)rec->ExceptionInformation <= (char *)context && + (char *)(rec + 1) >= (char *)context, "wrong ptrs %p / %p\n", rec, context ); + ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset ); + ok( xctx->All.Length >= sizeof(CONTEXT) + sizeof(CONTEXT_EX), "wrong All.Length %lx\n", xctx->All.Length ); + ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset ); + ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length ); + + hook_called = TRUE; + hook_KiUserExceptionDispatcher_eip = (void *)context->Eip; + hook_exception_address = rec->ExceptionAddress; + memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + return pKiUserExceptionDispatcher; +} + +static void test_KiUserExceptionDispatcher(void) +{ + PVOID vectored_handler; + static BYTE except_code[] = + { + 0xb9, /* mov imm32, %ecx */ + /* offset: 0x1 */ + 0x00, 0x00, 0x00, 0x00, + + 0x89, 0x01, /* mov %eax, (%ecx) */ + 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */ + 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */ + 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */ + 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */ + 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */ + 0x83, 0xc1, 0x18, /* add $0x18, %ecx */ + + /* offset: 0x19 */ + 0xcc, /* int3 */ + + 0x0f, 0x0b, /* ud2, illegal instruction */ + + /* offset: 0x1c */ + 0xb9, /* mov imm32, %ecx */ + /* offset: 0x1d */ + 0x00, 0x00, 0x00, 0x00, + + 0x89, 0x01, /* mov %eax, (%ecx) */ + 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */ + 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */ + 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */ + 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */ + 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */ + 0x8b, 0x71, 0xf0, /* mov -0x10(%ecx),%esi */ + + 0xc3, /* ret */ + }; + static BYTE hook_trampoline[] = + { + 0xff, 0x15, + /* offset: 2 bytes */ + 0x00, 0x00, 0x00, 0x00, /* call *addr */ /* call hook implementation. */ + 0xff, 0xe0, /* jmp *%eax */ + }; + void *phook_KiUserExceptionDispatcher = hook_KiUserExceptionDispatcher; + BYTE patched_KiUserExceptionDispatcher_bytes[7]; + DWORD old_protect1, old_protect2; + EXCEPTION_RECORD record; + void *bpt_address; + BYTE *ptr; + BOOL ret; + + if (!pRtlUnwind) + { + win_skip("RtlUnwind is not available.\n"); + return; + } + + *(DWORD *)(except_code + 1) = (DWORD)&test_kiuserexceptiondispatcher_regs; + *(DWORD *)(except_code + 0x1d) = (DWORD)&test_kiuserexceptiondispatcher_regs.new_eax; + + *(unsigned int *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserExceptionDispatcher; + + ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), PAGE_EXECUTE_READWRITE, &old_protect1); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + + ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes), + PAGE_EXECUTE_READWRITE, &old_protect2); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + + memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + + ptr = patched_KiUserExceptionDispatcher_bytes; + /* mov $hook_trampoline, %eax */ + *ptr++ = 0xb8; + *(void **)ptr = hook_trampoline; + ptr += sizeof(void *); + /* jmp *eax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code), + PAGE_EXECUTE_READ); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called, "Hook was not called.\n"); + + ok(test_kiuserexceptiondispatcher_regs.new_eax == 0xdeadbeef, "Got unexpected eax %#lx.\n", + test_kiuserexceptiondispatcher_regs.new_eax); + ok(test_kiuserexceptiondispatcher_regs.new_esi == 0xdeadbeef, "Got unexpected esi %#lx.\n", + test_kiuserexceptiondispatcher_regs.new_esi); + ok(test_kiuserexceptiondispatcher_regs.old_edi + == test_kiuserexceptiondispatcher_regs.new_edi, "edi does not match.\n"); + ok(test_kiuserexceptiondispatcher_regs.old_ebp + == test_kiuserexceptiondispatcher_regs.new_ebp, "ebp does not match.\n"); + + bpt_address = (BYTE *)code_mem + 0x19; + + ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */, + "Got unexpected exception address %p, expected %p.\n", + hook_exception_address, bpt_address); + ok(hook_KiUserExceptionDispatcher_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + hook_KiUserExceptionDispatcher_eip, bpt_address); + ok(dbg_except_continue_handler_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + dbg_except_continue_handler_eip, bpt_address); + + record.ExceptionCode = 0x80000003; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; /* does not matter, copied return address */ + record.NumberParameters = 0; + + vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler); + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + hook_called = FALSE; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called || broken(!hook_called) /* 2003 */, "Hook was not called.\n"); + + memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + + RemoveVectoredExceptionHandler(vectored_handler); + ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes), + old_protect2, &old_protect2); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), old_protect1, &old_protect1); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); +} + +static BYTE saved_KiUserApcDispatcher[7]; + +static void * CDECL hook_KiUserApcDispatcher( void *func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 ) +{ + CONTEXT *context = (CONTEXT *)((ULONG_PTR)&arg3 + sizeof(ULONG)); + + ok( func == apc_func, "wrong function %p / %p\n", func, apc_func ); + ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 ); + ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 ); + ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 ); + + if (context->ContextFlags != 1) + { + trace( "context %p eip %lx ebp %lx esp %lx (%x)\n", + context, context->Eip, context->Ebp, context->Esp, (char *)context->Esp - (char *)&func ); + } + else /* new style with alertable arg and CONTEXT_EX */ + { + CONTEXT_EX *xctx; + ULONG *alertable = (ULONG *)context; + + context = (CONTEXT *)(alertable + 1); + xctx = (CONTEXT_EX *)(context + 1); + + trace( "alertable %lx context %p eip %lx ebp %lx esp %lx (%x)\n", *alertable, + context, context->Eip, context->Ebp, context->Esp, (char *)context->Esp - (char *)&func ); + if ((void *)(xctx + 1) < (void *)context->Esp) + { + ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset ); + ok( xctx->All.Length >= sizeof(CONTEXT) + sizeof(CONTEXT_EX), "wrong All.Length %lx\n", xctx->All.Length ); + ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset ); + ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length ); + } + + if (apc_count) *alertable = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef ); + } + + hook_called = TRUE; + memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + BYTE hook_trampoline[] = + { + 0xff, 0x15, + /* offset: 2 bytes */ + 0x00, 0x00, 0x00, 0x00, /* call *addr */ /* call hook implementation. */ + 0xff, 0xe0, /* jmp *%eax */ + }; + + BYTE patched_KiUserApcDispatcher[7]; + void *phook_KiUserApcDispatcher = hook_KiUserApcDispatcher; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + *(ULONG_PTR *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) ); + ptr = patched_KiUserApcDispatcher; + /* mov $hook_trampoline, %eax */ + *ptr++ = 0xb8; + *(void **)ptr = code_mem; + ptr += sizeof(void *); + /* jmp *eax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + + apc_count = 0; + hook_called = FALSE; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 1 || apc_count == 2, "APC count %u\n", apc_count ); + ok( hook_called, "hook was not called\n" ); + + if (apc_count == 2) + { + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 3, "APC count %u\n", apc_count ); + SleepEx( 0, TRUE ); + ok( apc_count == 4, "APC count %u\n", apc_count ); + } + VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect ); +} + +static void CDECL hook_KiUserCallbackDispatcher( void *eip, ULONG id, ULONG *args, ULONG len, + ULONG unk1, ULONG unk2, ULONG arg0, ULONG arg1 ) +{ + KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[id]; + + trace( "eip %p id %lx args %p (%x) len %lx unk1 %lx unk2 %lx args %lx,%lx\n", + eip, id, args, (char *)args - (char *)&eip, len, unk1, unk2, arg0, arg1 ); + + if (args[0] != arg0) /* new style with extra esp */ + { + void *esp = (void *)arg0; + + ok( args[0] == arg1, "wrong arg1 %lx / %lx\n", args[0], arg1 ); + ok( (char *)esp - ((char *)args + len) < 0x10, "wrong esp offset %p / %p\n", esp, args ); + } + + if (eip && pRtlPcToFileHeader) + { + void *mod, *win32u = GetModuleHandleA("win32u.dll"); + + pRtlPcToFileHeader( eip, &mod ); + if (win32u) ok( mod == win32u, "ret address %p not in win32u %p\n", eip, win32u ); + else trace( "ret address %p in %p\n", eip, mod ); + } + NtCallbackReturn( NULL, 0, func( args, len )); +} + +static void test_KiUserCallbackDispatcher(void) +{ + BYTE saved_code[7], patched_code[7]; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code) ); + ptr = patched_code; + /* mov $hook_trampoline, %eax */ + *ptr++ = 0xb8; + *(void **)ptr = hook_KiUserCallbackDispatcher; + ptr += sizeof(void *); + /* call *eax */ + *ptr++ = 0xff; + *ptr++ = 0xd0; + memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code) ); + + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + + memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code)); + VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect ); +} + +static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0xff, 0x05, /* inc instrumentation_call_count */ + /* &instrumentation_call_count, offset 2 */ 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe1, /* jmp *ecx */ + }; + + unsigned int instrumentation_call_count; + NTSTATUS status; + + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(volatile void **)((char *)code_mem + 2) = &instrumentation_call_count; + + memset(&info, 0, sizeof(info)); + /* On 32 bit the structure is never used and just a callback pointer is expected. */ + info.Version = (ULONG_PTR)code_mem; + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_NOT_SUPPORTED + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + todo_wine ok( instrumentation_call_count, "got %u.\n", instrumentation_call_count ); + + memset(&info, 0, sizeof(info)); + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !instrumentation_call_count, "got %u.\n", instrumentation_call_count ); +} + #elif defined(__x86_64__) -#define is_wow64 0 - -#ifndef __REACTOS__ -#define UNW_FLAG_NHANDLER 0 -#define UNW_FLAG_EHANDLER 1 -#define UNW_FLAG_UHANDLER 2 -#define UNW_FLAG_CHAININFO 4 -#endif // __REACTOS__ - -#define UWOP_PUSH_NONVOL 0 -#define UWOP_ALLOC_LARGE 1 -#define UWOP_ALLOC_SMALL 2 -#define UWOP_SET_FPREG 3 -#define UWOP_SAVE_NONVOL 4 -#define UWOP_SAVE_NONVOL_FAR 5 -#define UWOP_SAVE_XMM128 8 -#define UWOP_SAVE_XMM128_FAR 9 -#define UWOP_PUSH_MACHFRAME 10 - -struct results -{ - int rip_offset; /* rip offset from code start */ - int rbp_offset; /* rbp offset from stack pointer */ - int handler; /* expect handler to be set? */ - int rip; /* expected final rip value */ - int frame; /* expected frame return value */ - int regs[8][2]; /* expected values for registers */ -}; - -struct unwind_test -{ - const BYTE *function; - size_t function_size; - const BYTE *unwind_info; - const struct results *results; - unsigned int nb_results; -}; - -enum regs -{ - rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, - r8, r9, r10, r11, r12, r13, r14, r15 -}; - -static const char * const reg_names[16] = -{ - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" -}; - -#define UWOP(code,info) (UWOP_##code | ((info) << 4)) - -static void call_virtual_unwind( int testnum, const struct unwind_test *test ) -{ - static const int code_offset = 1024; - static const int unwind_offset = 2048; - void *handler, *data; - CONTEXT context; - RUNTIME_FUNCTION runtime_func; - KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; - UINT i, j, k; - ULONG64 fake_stack[256]; - ULONG64 frame, orig_rip, orig_rbp, unset_reg; - UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8; - - memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); - memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size ); - - runtime_func.BeginAddress = code_offset; - runtime_func.EndAddress = code_offset + test->function_size; - runtime_func.UnwindData = unwind_offset; - - trace( "code: %p stack: %p\n", code_mem, fake_stack ); - - for (i = 0; i < test->nb_results; i++) - { - memset( &ctx_ptr, 0, sizeof(ctx_ptr) ); - memset( &context, 0x55, sizeof(context) ); - memset( &unset_reg, 0x55, sizeof(unset_reg) ); - for (j = 0; j < 256; j++) fake_stack[j] = j * 8; - - context.Rsp = (ULONG_PTR)fake_stack; - context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset; - orig_rbp = context.Rbp; - orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset; - - trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i, - (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp ); - - data = (void *)0xdeadbeef; - handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip, - &runtime_func, &context, &data, &frame, &ctx_ptr ); - if (test->results[i].handler) - { - ok( (char *)handler == (char *)code_mem + 0x200, - "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 ); - if (handler) ok( *(DWORD *)data == 0x08070605, - "%u/%u: wrong handler data %p\n", testnum, i, data ); - } - else - { - ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler ); - ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data ); - } - - ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n", - testnum, i, (void *)context.Rip, test->results[i].rip ); - ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n", - testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame ); - - for (j = 0; j < 16; j++) - { - static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]); - - for (k = 0; k < nb_regs; k++) - { - if (test->results[i].regs[k][0] == -1) - { - k = nb_regs; - break; - } - if (test->results[i].regs[k][0] == j) break; - } - - if (j == rsp) /* rsp is special */ - { - ok( !ctx_ptr.u2.IntegerContext[j], - "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i ); - - ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1], - "%u/%u: register rsp wrong %p/%p\n", - testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] ); - continue; - } - - if (ctx_ptr.u2.IntegerContext[j]) - { - ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", - testnum, i, reg_names[j], *(&context.Rax + j) ); - if (k < nb_regs) - ok( *(&context.Rax + j) == test->results[i].regs[k][1], - "%u/%u: register %s wrong %p/%x\n", - testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] ); - } - else - { - ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] ); - if (j == rbp) - ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n", - testnum, i, (void *)context.Rbp ); - else - ok( *(&context.Rax + j) == unset_reg, - "%u/%u: register %s wrong %p/unset\n", - testnum, i, reg_names[j], (void *)*(&context.Rax + j)); - } - } - } -} - -static void test_virtual_unwind(void) -{ - static const BYTE function_0[] = - { - 0xff, 0xf5, /* 00: push %rbp */ - 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */ - 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */ - 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */ - 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */ - 0x90, /* 1c: nop */ - 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */ - 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */ - 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */ - 0x5d, /* 32: pop %rbp */ - 0xc3 /* 33: ret */ - }; - - static const BYTE unwind_info_0[] = - { - 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ - 0x1c, /* prolog size */ - 8, /* opcode count */ - (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */ - - 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */ - 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */ - 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */ - 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */ - 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ - - 0x00, 0x02, 0x00, 0x00, /* handler */ - 0x05, 0x06, 0x07, 0x08, /* data */ - }; - - static const struct results results_0[] = - { - /* offset rbp handler rip frame registers */ - { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, - { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, - { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }}, - { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }}, - { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }}, - { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, - { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, - { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, - { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}}, - { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}}, - { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}}, - }; - - - static const BYTE function_1[] = - { - 0x53, /* 00: push %rbx */ - 0x55, /* 01: push %rbp */ - 0x56, /* 02: push %rsi */ - 0x57, /* 03: push %rdi */ - 0x41, 0x54, /* 04: push %r12 */ - 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */ - 0x90, 0x90, /* 0a: nop; nop */ - 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */ - 0x41, 0x5c, /* 10: pop %r12 */ - 0x5f, /* 12: pop %rdi */ - 0x5e, /* 13: pop %rsi */ - 0x5d, /* 14: pop %rbp */ - 0x5b, /* 15: pop %rbx */ - 0xc3 /* 16: ret */ - }; - - static const BYTE unwind_info_1[] = - { - 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ - 0x0a, /* prolog size */ - 6, /* opcode count */ - 0, /* frame reg */ - - 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */ - 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */ - 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */ - 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */ - 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ - 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */ - - 0x00, 0x02, 0x00, 0x00, /* handler */ - 0x05, 0x06, 0x07, 0x08, /* data */ - }; - - static const struct results results_1[] = - { - /* offset rbp handler rip frame registers */ - { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, - { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, - { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, - { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, - { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, - { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, - { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, - { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, - { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, - { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, - { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, - { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, - { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, - { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, - }; - - static const struct unwind_test tests[] = - { - { function_0, sizeof(function_0), unwind_info_0, - results_0, sizeof(results_0)/sizeof(results_0[0]) }, - { function_1, sizeof(function_1), unwind_info_1, - results_1, sizeof(results_1)/sizeof(results_1[0]) } - }; - unsigned int i; - - for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) - call_virtual_unwind( i, &tests[i] ); -} - -static int consolidate_dummy_called; +static LONG consolidate_dummy_called; static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec) { CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1]; - consolidate_dummy_called = 1; - ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip); + + switch (InterlockedIncrement(&consolidate_dummy_called)) + { + case 1: /* RtlRestoreContext */ + ok(ctx->Rip == 0xdeadbeef, "RtlRestoreContext wrong Rip, expected: 0xdeadbeef, got: %Ix\n", ctx->Rip); + ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + break; + case 2: /* RtlUnwindEx */ + ok(ctx->Rip != 0xdeadbeef, "RtlUnwindEx wrong Rip, got: %Ix\n", ctx->Rip ); + if (is_arm64ec) + { + DISPATCHER_CONTEXT_NONVOLREG_ARM64 *regs = (void *)rec->ExceptionInformation[10]; + _JUMP_BUFFER *buf = (void *)rec->ExceptionInformation[3]; + ARM64EC_NT_CONTEXT *ec_ctx = (ARM64EC_NT_CONTEXT *)ctx; + int i; + + ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + ok( regs->GpNvRegs[0] == buf->R12, "wrong reg X19, %Ix / %Ix\n", regs->GpNvRegs[0], buf->R12 ); + ok( regs->GpNvRegs[1] == buf->R13, "wrong reg X20, %Ix / %Ix\n", regs->GpNvRegs[1], buf->R13 ); + ok( regs->GpNvRegs[2] == buf->R14, "wrong reg X21, %Ix / %Ix\n", regs->GpNvRegs[2], buf->R14 ); + ok( regs->GpNvRegs[3] == buf->R15, "wrong reg X22, %Ix / %Ix\n", regs->GpNvRegs[3], buf->R15 ); + ok( regs->GpNvRegs[4] == 0, "wrong reg X23, %Ix / 0\n", regs->GpNvRegs[4] ); + ok( regs->GpNvRegs[5] == 0, "wrong reg X24, %Ix / 0\n", regs->GpNvRegs[5] ); + ok( regs->GpNvRegs[6] == buf->Rsi, "wrong reg X25, %Ix / %Ix\n", regs->GpNvRegs[6], buf->Rsi ); + ok( regs->GpNvRegs[7] == buf->Rdi, "wrong reg X26, %Ix / %Ix\n", regs->GpNvRegs[7], buf->Rdi ); + ok( regs->GpNvRegs[8] == buf->Rbx, "wrong reg X27, %Ix / %Ix\n", regs->GpNvRegs[8], buf->Rbx ); + ok( regs->GpNvRegs[9] == 0, "wrong reg X28, %Ix / 0\n", regs->GpNvRegs[9] ); + ok( regs->GpNvRegs[10] == buf->Rbp,"wrong reg X29, %Ix / %Ix\n", regs->GpNvRegs[10], buf->Rbp ); + for (i = 0; i < 8; i++) + ok(regs->FpNvRegs[i] == ec_ctx->V[i + 8].D[0], "wrong reg D%u, expected: %g, got: %g\n", + i + 8, regs->FpNvRegs[i], ec_ctx->V[i + 8].D[0] ); + } + else ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + break; + } return (PVOID)rec->ExceptionInformation[2]; } @@ -1898,16 +2356,12 @@ static void test_restore_context(void) EXCEPTION_RECORD rec; _JUMP_BUFFER buf; CONTEXT ctx; -#ifdef __REACTOS__ int i; LONG pass; -#else - int i, pass; -#endif - if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp) + if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext) { - skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n"); + skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n"); return; } @@ -1923,14 +2377,14 @@ static void test_restore_context(void) ok(0, "shouldn't be reached\n"); } else - ok(pass < 4, "unexpected pass %d\n", pass); + ok(pass < 4, "unexpected pass %ld\n", pass); - /* test with jmp using RltRestoreContext */ + /* test with jmp using RtlRestoreContext */ pass = 0; InterlockedIncrement(&pass); RtlCaptureContext(&ctx); InterlockedIncrement(&pass); /* only called once */ - p_setjmp(&buf); + setjmp((_JBTYPE *)&buf); InterlockedIncrement(&pass); if (pass == 3) { @@ -1938,43 +2392,58 @@ static void test_restore_context(void) rec.NumberParameters = 1; rec.ExceptionInformation[0] = (DWORD64)&buf; + ok(buf.FpCsr == 0x27f, "Got unexpected FpCsr %#x.\n", buf.FpCsr); + buf.FpCsr = 0x7f; + buf.MxCsr = 0x3f80; /* uses buf.Rip instead of ctx.Rip */ pRtlRestoreContext(&ctx, &rec); ok(0, "shouldn't be reached\n"); } else if (pass == 4) { - ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx); - ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp); - ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp); - ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi); - ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi); - ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12); - ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13); - ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14); - ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15); + ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %Ix, got: %Ix\n", buf.Rbx, ctx.Rbx); + ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %Ix, got: %Ix\n", buf.Rsp, ctx.Rsp); + ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %Ix, got: %Ix\n", buf.Rbp, ctx.Rbp); + ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %Ix, got: %Ix\n", buf.Rsi, ctx.Rsi); + ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %Ix, got: %Ix\n", buf.Rdi, ctx.Rdi); + ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %Ix, got: %Ix\n", buf.R12, ctx.R12); + ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %Ix, got: %Ix\n", buf.R13, ctx.R13); + ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %Ix, got: %Ix\n", buf.R14, ctx.R14); + ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %Ix, got: %Ix\n", buf.R15, ctx.R15); fltsave = &buf.Xmm6; for (i = 0; i < 10; i++) { - ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low, - "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, - fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low); + ok(fltsave[i].Part[0] == ctx.FltSave.XmmRegisters[i + 6].Low, + "longjmp failed for Xmm%d, expected %Ix, got %Ix\n", i + 6, + fltsave[i].Part[0], ctx.FltSave.XmmRegisters[i + 6].Low); - ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High, - "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6, - fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High); + ok(fltsave[i].Part[1] == ctx.FltSave.XmmRegisters[i + 6].High, + "longjmp failed for Xmm%d, expected %Ix, got %Ix\n", i + 6, + fltsave[i].Part[1], ctx.FltSave.XmmRegisters[i + 6].High); } + ok(ctx.FltSave.ControlWord == 0x7f, "Got unexpected float control word %#x.\n", ctx.FltSave.ControlWord); + ok(ctx.MxCsr == 0x3f80, "Got unexpected MxCsr %#lx.\n", ctx.MxCsr); + ok(ctx.FltSave.MxCsr == 0x3f80, "Got unexpected MxCsr %#lx.\n", ctx.FltSave.MxCsr); + buf.FpCsr = 0x27f; + buf.MxCsr = 0x1f80; + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 5) + { + ok(ctx.FltSave.ControlWord == 0x27f, "Got unexpected float control word %#x.\n", ctx.FltSave.ControlWord); + ok(ctx.FltSave.MxCsr == 0x1f80, "Got unexpected MxCsr %#lx.\n", ctx.MxCsr); } else - ok(0, "unexpected pass %d\n", pass); + ok(0, "unexpected pass %ld\n", pass); /* test with jmp through RtlUnwindEx */ pass = 0; InterlockedIncrement(&pass); pRtlCaptureContext(&ctx); InterlockedIncrement(&pass); /* only called once */ - p_setjmp(&buf); + setjmp((_JBTYPE *)&buf); InterlockedIncrement(&pass); if (pass == 3) { @@ -1983,11 +2452,11 @@ static void test_restore_context(void) rec.ExceptionInformation[0] = (DWORD64)&buf; /* uses buf.Rip instead of bogus 0xdeadbeef */ - pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + pRtlUnwindEx((void*)buf.Frame, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); ok(0, "shouldn't be reached\n"); } else - ok(pass == 4, "unexpected pass %d\n", pass); + ok(pass == 4, "unexpected pass %ld\n", pass); /* test with consolidate */ @@ -2002,131 +2471,41 @@ static void test_restore_context(void) rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; rec.ExceptionInformation[1] = (DWORD64)&ctx; rec.ExceptionInformation[2] = ctx.Rip; + rec.ExceptionInformation[10] = -1; ctx.Rip = 0xdeadbeef; pRtlRestoreContext(&ctx, &rec); ok(0, "shouldn't be reached\n"); } else if (pass == 3) - ok(consolidate_dummy_called, "test_consolidate_dummy not called\n"); + ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n"); else - ok(0, "unexpected pass %d\n", pass); -} - -static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context ) -{ - static const int code_offset = 1024; - static RUNTIME_FUNCTION runtime_func; - (*(DWORD *)context)++; - - runtime_func.BeginAddress = code_offset + 16; - runtime_func.EndAddress = code_offset + 32; - runtime_func.UnwindData = 0; - return &runtime_func; -} - -static void test_dynamic_unwind(void) -{ - static const int code_offset = 1024; - char buf[sizeof(RUNTIME_FUNCTION) + 4]; - RUNTIME_FUNCTION *runtime_func, *func; - ULONG_PTR table, base; - DWORD count; - - /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */ - runtime_func = (RUNTIME_FUNCTION *)buf; - runtime_func->BeginAddress = code_offset; - runtime_func->EndAddress = code_offset + 16; - runtime_func->UnwindData = 0; - ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), - "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); - - /* Lookup function outside of any function table */ - base = 0xdeadbeef; - func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); - ok( func == NULL, - "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); - ok( !base || broken(base == 0xdeadbeef), - "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base ); - - /* Test with pointer inside of our function */ - base = 0xdeadbeef; - func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); - ok( func == runtime_func, - "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); - ok( base == (ULONG_PTR)code_mem, - "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base ); - - /* Test RtlDeleteFunctionTable */ - ok( pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); - ok( !pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); - - /* Unaligned RUNTIME_FUNCTION pointer */ - runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3); - runtime_func->BeginAddress = code_offset; - runtime_func->EndAddress = code_offset + 16; - runtime_func->UnwindData = 0; - ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), - "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); - ok( pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); - - /* Attempt to insert the same entry twice */ - runtime_func = (RUNTIME_FUNCTION *)buf; - runtime_func->BeginAddress = code_offset; - runtime_func->EndAddress = code_offset + 16; - runtime_func->UnwindData = 0; - ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), - "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); - ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), - "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); - ok( pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); - ok( pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); - ok( !pRtlDeleteFunctionTable( runtime_func ), - "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); - - /* Test RtlInstallFunctionTableCallback with both low bits unset */ - table = (ULONG_PTR)code_mem; - ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), - "RtlInstallFunctionTableCallback returned success for table = %lx\n", table ); - - /* Test RtlInstallFunctionTableCallback with both low bits set */ - table = (ULONG_PTR)code_mem | 0x3; - ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), - "RtlInstallFunctionTableCallback failed for table = %lx\n", table ); - - /* Lookup function outside of any function table */ - count = 0; - base = 0xdeadbeef; - func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL ); - ok( func == NULL, - "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); - ok( !base || broken(base == 0xdeadbeef), - "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base ); - ok( !count, - "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count ); - - /* Test with pointer inside of our function */ - count = 0; - base = 0xdeadbeef; - func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL ); - ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32, - "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func ); - ok( base == (ULONG_PTR)code_mem, - "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base ); - ok( count == 1, - "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count ); - - /* Clean up again */ - ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), - "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table ); - ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), - "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table ); + ok(0, "unexpected pass %ld\n", pass); + /* test with consolidate through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 4; + rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD64)&ctx; + rec.ExceptionInformation[2] = ctx.Rip; + rec.ExceptionInformation[3] = (DWORD64)&buf; + rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */ + ctx.Rip = 0xdeadbeef; + /* uses consolidate callback Rip instead of bogus 0xdeadbeef */ + setjmp((_JBTYPE *)&buf); + pRtlUnwindEx((void*)buf.Frame, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %ld\n", pass); } static int termination_handler_called; @@ -2134,7 +2513,7 @@ static void WINAPI termination_handler(ULONG flags, ULONG64 frame) { termination_handler_called++; - ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags); + ok(flags == 1 || broken(flags == 0x401), "flags = %lx\n", flags); ok(frame == 0x1234, "frame = %p\n", (void*)frame); } @@ -2154,7 +2533,7 @@ static void test___C_specific_handler(void) } memset(&rec, 0, sizeof(rec)); - rec.ExceptionFlags = 2; /* EH_UNWINDING */ + rec.ExceptionFlags = EXCEPTION_UNWINDING; frame = 0x1234; memset(&dispatch, 0, sizeof(dispatch)); dispatch.ImageBase = (ULONG_PTR)GetModuleHandleA(NULL); @@ -2164,11 +2543,7 @@ static void test___C_specific_handler(void) scope_table.Count = 1; scope_table.ScopeRecord[0].BeginAddress = 0x200; scope_table.ScopeRecord[0].EndAddress = 0x400; -#ifndef __REACTOS__ scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase; -#else - scope_table.ScopeRecord[0].HandlerAddress = ((ULONG_PTR)termination_handler - (ULONG_PTR)dispatch.ImageBase); -#endif scope_table.ScopeRecord[0].JumpTarget = 0; memset(&context, 0, sizeof(context)); @@ -2177,16 +2552,5822 @@ static void test___C_specific_handler(void) ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret); ok(termination_handler_called == 1, "termination_handler_called = %d\n", termination_handler_called); - ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex); + ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %ld\n", dispatch.ScopeIndex); ret = p__C_specific_handler(&rec, frame, &context, &dispatch); ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret); ok(termination_handler_called == 1, "termination_handler_called = %d\n", termination_handler_called); - ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex); + ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %ld\n", dispatch.ScopeIndex); } -#endif /* __x86_64__ */ +/* This is heavily based on the i386 exception tests. */ +static const struct exception +{ + BYTE code[40]; /* asm code */ + BYTE offset; /* offset of faulting instruction */ + BYTE length; /* length of faulting instruction */ + NTSTATUS status; /* expected status code */ + DWORD nb_params; /* expected number of parameters */ + ULONG64 params[4]; /* expected parameters */ + NTSTATUS alt_status; /* alternative status code */ + DWORD alt_nb_params; /* alternative number of parameters */ + ULONG64 alt_params[4]; /* alternative parameters */ +} exceptions[] = +{ +/* 0 */ + /* test some privileged instructions */ + { { 0xfb, 0xc3 }, /* 0: sti; ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, +/* 5 */ + { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */ + 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */ + 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */ + 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */ + 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, +/* 10 */ + { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xf4, 0xc3 }, /* 12: hlt; ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0xfa, 0xc3 }, /* 13: cli; ret */ + 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + + /* test iret to invalid selector */ + { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x48, 0x83, 0xc4, 0x18, 0xc3 }, + /* 15: pushq $0; pushq $0; pushq $0; iret; addq $24,%rsp; ret */ + 6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } }, +/* 15 */ + /* test loading an invalid selector */ + { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */ + 5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */ + + /* test overlong instruction (limit is 15 bytes) */ + { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 }, + 0, 16, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 }, + STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } }, + + /* test invalid interrupt */ + { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */ + 0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } }, + + /* test moves to/from Crx */ + { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, +/* 20 */ + { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + + /* test moves to/from Drx */ + { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, +/* 25 */ + { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */ + 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + + /* test memory reads */ + { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffc,%eax; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffc } }, + { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffd,%eax; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffd } }, +/* 30 */ + { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffe,%eax; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffe } }, + { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffffffffffff,%eax; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } }, + + /* test memory writes */ + { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffc; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffc } }, + { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffd; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffd } }, + { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffe; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffe } }, +/* 35 */ + { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffffffffffff; ret */ + 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffffffffffff } }, + + /* test exception with cleared segment registers */ + { { + 0x8c, 0xc0, /* mov %es,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xd8, /* mov %ds,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xe0, /* mov %fs,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xe8, /* mov %gs,%eax */ + 0x50, /* push %rax */ + 0x31, 0xc0, /* xor %eax,%eax */ + 0x8e, 0xc0, /* mov %eax,%es */ + 0x8e, 0xd8, /* mov %eax,%ds */ +#if 0 + /* It is observed that fs/gs base is reset + on some CPUs when setting the segment value + even to 0 (regardless of CPU spec + saying otherwise) and it is not currently + handled in Wine. + Disable this part to avoid crashing the test. */ + 0x8e, 0xe0, /* mov %eax,%fs */ + 0x8e, 0xe8, /* mov %eax,%gs */ +#else + 0x90, 0x90, /* nop */ + 0x90, 0x90, /* nop */ +#endif + 0xfa, /* cli */ + 0x58, /* pop %rax */ +#if 0 + 0x8e, 0xe8, /* mov %eax,%gs */ + 0x58, /* pop %rax */ + 0x8e, 0xe0, /* mov %eax,%fs */ +#else + 0x58, /* pop %rax */ + 0x90, 0x90, /* nop */ + 0x90, 0x90, /* nop */ +#endif + 0x58, /* pop %rax */ + 0x8e, 0xd8, /* mov %eax,%ds */ + 0x58, /* pop %rax */ + 0x8e, 0xc0, /* mov %eax,%es */ + 0xc3, /* retq */ + }, 22, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 }, + + { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */ + 1, 1, STATUS_SINGLE_STEP, 0 }, + { { 0xcd, 0x2c, 0xc3 }, + 0, 2, STATUS_ASSERTION_FAILURE, 0 }, + { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */ + 0xcd, 0x2d, 0xfa, 0xc3 }, /* int $0x2d; cli; ret */ + 7, 1, STATUS_BREAKPOINT, 1, { 0xb8b8b8b8 } }, +/* 40 */ + { { 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x01, %eax */ + 0xcd, 0x2d, 0xfa, 0xc3 }, /* int $0x2d; cli; ret */ + 8, 0, STATUS_SUCCESS, 0 }, +}; + +static int got_exception; + +static void run_exception_test_flags(void *handler, const void* context, + const void *code, unsigned int code_size, + DWORD access, DWORD handler_flags) +{ + unsigned char buf[2 + 8 + 2 + 8 + 8]; + RUNTIME_FUNCTION runtime_func; + UNWIND_INFO *unwind = (UNWIND_INFO *)buf; + void (*func)(void) = code_mem; + DWORD oldaccess, oldaccess2; + + runtime_func.BeginAddress = 0; + runtime_func.EndAddress = code_size; + runtime_func.UnwindData = 0x1000; + + unwind->Version = 1; + unwind->Flags = handler_flags; + unwind->SizeOfProlog = 0; + unwind->CountOfCodes = 0; + unwind->FrameRegister = 0; + unwind->FrameOffset = 0; + *(ULONG *)&buf[4] = 0x1010; + *(const void **)&buf[8] = context; + + /* movabs $, %rax */ + buf[16] = 0x48; + buf[17] = 0xb8; + *(void **)&buf[18] = handler; + /* jmp *%rax */ + buf[26] = 0xff; + buf[27] = 0xe0; + + memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf)); + memcpy(code_mem, code, code_size); + if(access) + VirtualProtect(code_mem, code_size, access, &oldaccess); + + pRtlAddFunctionTable(&runtime_func, 1, (ULONG_PTR)code_mem); + func(); + pRtlDeleteFunctionTable(&runtime_func); + + if(access) + VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2); +} + +static void run_exception_test(void *handler, const void* context, + const void *code, unsigned int code_size, + DWORD access) +{ + run_exception_test_flags(handler, context, code, code_size, access, UNW_FLAG_EHANDLER); +} + +static DWORD WINAPI prot_fault_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + const struct exception *except = *(const struct exception **)(dispatcher->HandlerData); + unsigned int i, parameter_count, entry = except - exceptions; + + got_exception++; + winetest_push_context( "%u: %lx", entry, rec->ExceptionCode ); + + ok( rec->ExceptionCode == except->status || + (except->alt_status != 0 && rec->ExceptionCode == except->alt_status), + "Wrong exception code %lx/%lx\n", rec->ExceptionCode, except->status ); + ok( context->Rip == (DWORD_PTR)code_mem + except->offset, + "Unexpected eip %#Ix/%#Ix\n", context->Rip, (DWORD_PTR)code_mem + except->offset ); + ok( rec->ExceptionAddress == (char*)context->Rip || + (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Rip + 1), + "Unexpected exception address %p/%p\n", rec->ExceptionAddress, (char*)context->Rip ); + +#ifndef __arm64ec__ + if (!is_arm64ec) + { + USHORT ds, es, fs, gs, ss; +#if defined(__REACTOS__) && defined(_MSC_VER) + ds = __readsegds(); + es = __readseges(); + fs = __readsegfs(); + gs = __readseggs(); + ss = __readsegss(); +#else + __asm__ volatile( "movw %%ds,%0" : "=g" (ds) ); + __asm__ volatile( "movw %%es,%0" : "=g" (es) ); + __asm__ volatile( "movw %%fs,%0" : "=g" (fs) ); + __asm__ volatile( "movw %%gs,%0" : "=g" (gs) ); + __asm__ volatile( "movw %%ss,%0" : "=g" (ss) ); +#endif + ok( context->SegDs == ds || !ds, "ds %#x does not match %#x\n", context->SegDs, ds ); + ok( context->SegEs == es || !es, "es %#x does not match %#x\n", context->SegEs, es ); + ok( context->SegFs == fs || !fs, "fs %#x does not match %#x\n", context->SegFs, fs ); + ok( context->SegGs == gs || !gs, "gs %#x does not match %#x\n", context->SegGs, gs ); + ok( context->SegSs == ss, "ss %#x does not match %#x\n", context->SegSs, ss ); + ok( context->SegDs == context->SegSs, + "ds %#x does not match ss %#x\n", context->SegDs, context->SegSs ); + ok( context->SegEs == context->SegSs, + "es %#x does not match ss %#x\n", context->SegEs, context->SegSs ); + ok( context->SegGs == context->SegSs, + "gs %#x does not match ss %#x\n", context->SegGs, context->SegSs ); + todo_wine ok( context->SegFs && context->SegFs != context->SegSs, + "got fs %#x\n", context->SegFs ); + } +#endif + + if (except->status == STATUS_BREAKPOINT && is_wow64) + parameter_count = 1; + else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status) + parameter_count = except->nb_params; + else + parameter_count = except->alt_nb_params; + + ok( rec->NumberParameters == parameter_count, + "Unexpected parameter count %lu/%u\n", rec->NumberParameters, parameter_count ); + + /* Most CPUs (except Intel Core apparently) report a segment limit violation */ + /* instead of page faults for accesses beyond 0xffffffffffffffff */ + if (except->nb_params == 2 && except->params[1] >= 0xfffffffffffffffd) + { + if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffffffffffff) + goto skip_params; + } + + /* Seems that both 0xbee8 and 0xfffffffffffffffff can be returned in windows */ + if (except->nb_params == 2 && rec->NumberParameters == 2 + && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffffffffffff + && except->params[0] == rec->ExceptionInformation[0]) + { + goto skip_params; + } + + if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status) + { + for (i = 0; i < rec->NumberParameters; i++) + ok( rec->ExceptionInformation[i] == except->params[i], + "Wrong parameter %d: %Ix/%Ix\n", + i, rec->ExceptionInformation[i], except->params[i] ); + } + else + { + for (i = 0; i < rec->NumberParameters; i++) + ok( rec->ExceptionInformation[i] == except->alt_params[i], + "Wrong parameter %d: %Ix/%Ix\n", + i, rec->ExceptionInformation[i], except->alt_params[i] ); + } + +skip_params: + winetest_pop_context(); + + context->Rip = (DWORD_PTR)code_mem + except->offset + except->length; + return ExceptionContinueExecution; +} + +static const BYTE segfault_code[5] = +{ + 0x31, 0xc0, /* xor %eax,%eax */ + 0x8f, 0x00, /* popq (%rax) - cause exception */ + 0xc3 /* ret */ +}; + +struct dbgreg_test +{ + ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7; +}; +/* test handling of debug registers */ +static DWORD WINAPI dreg_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + const struct dbgreg_test *test = *(const struct dbgreg_test **)dispatcher->HandlerData; + + context->Rip += 2; /* Skips the popq (%rax) */ + context->Dr0 = test->dr0; + context->Dr1 = test->dr1; + context->Dr2 = test->dr2; + context->Dr3 = test->dr3; + context->Dr6 = test->dr6; + context->Dr7 = test->dr7; + return ExceptionContinueExecution; +} + +#define CHECK_DEBUG_REG(n, m) \ + ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %p, got %p\n", \ + test_num, (void *)test->dr##n, (void *)ctx.Dr##n) + +static int check_debug_registers(int test_num, const struct dbgreg_test *test) +{ + CONTEXT ctx; + NTSTATUS status; + + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + status = pNtGetContextThread(GetCurrentThread(), &ctx); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status); + + if (!ctx.Dr0 && !ctx.Dr1 && !ctx.Dr2 && !ctx.Dr3 && !ctx.Dr6 && !ctx.Dr7) + { + win_skip( "debug registers broken\n" ); + return 0; + } + CHECK_DEBUG_REG(0, ~0); + CHECK_DEBUG_REG(1, ~0); + CHECK_DEBUG_REG(2, ~0); + CHECK_DEBUG_REG(3, ~0); + CHECK_DEBUG_REG(6, 0x0f); + CHECK_DEBUG_REG(7, ~0xdc00); + return 1; +} + +static const BYTE single_stepcode[] = +{ + 0x9c, /* pushf */ + 0x58, /* pop %rax */ + 0x0d,0,1,0,0, /* or $0x100,%eax */ + 0x50, /* push %rax */ + 0x9d, /* popf */ + 0x35,0,1,0,0, /* xor $0x100,%eax */ + 0x50, /* push %rax */ + 0x9d, /* popf */ + 0x90, + 0xc3 +}; + +/* test the single step exception behaviour */ +static DWORD WINAPI single_step_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + got_exception++; + ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n"); + + if( got_exception < 3) + context->EFlags |= 0x100; /* single step until popf instruction */ + else { + /* show that the last single step exception on the popf instruction + * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */ + ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP, + "exception is not EXCEPTION_SINGLE_STEP: %lx\n", rec->ExceptionCode); + } + return ExceptionContinueExecution; +} + +/* Test the alignment check (AC) flag handling. */ +static const BYTE align_check_code[] = +{ + 0x55, /* push %rbp */ + 0x48,0x89,0xe5, /* mov %rsp,%rbp */ + 0x9c, /* pushf */ + 0x9c, /* pushf */ + 0x58, /* pop %rax */ + 0x0d,0,0,4,0, /* or $0x40000,%eax */ + 0x50, /* push %rax */ + 0x9d, /* popf */ + 0x48,0x89,0xe0, /* mov %rsp, %rax */ + 0x8b,0x40,0x1, /* mov 0x1(%rax), %eax - cause exception */ + 0x9d, /* popf */ + 0x5d, /* pop %rbp */ + 0xc3, /* ret */ +}; + +static DWORD WINAPI align_check_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ +#ifdef __GNUC__ + __asm__ volatile( "pushfq; andl $~0x40000,(%rsp); popfq" ); +#endif + ok (context->EFlags & 0x40000, "eflags has AC bit unset\n"); + got_exception++; + if (got_exception != 1) + { + ok(broken(1) /* win7 */, "exception should occur only once"); + context->EFlags &= ~0x40000; + } + return ExceptionContinueExecution; +} + +/* Test the direction flag handling. */ +static const BYTE direction_flag_code[] = +{ + 0xfd, /* std */ + 0xfa, /* cli - cause exception */ + 0xc3, /* ret */ +}; + +static DWORD WINAPI direction_flag_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ +#ifdef __GNUC__ + ULONG_PTR flags; + __asm__("pushfq; popq %0; cld" : "=r" (flags) ); + /* older windows versions don't clear DF properly so don't test */ + if (flags & 0x400) trace( "eflags has DF bit set\n" ); +#endif + ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" ); + got_exception++; + context->Rip++; /* skip cli */ + context->EFlags &= ~0x400; /* make sure it is cleared on return */ + return ExceptionContinueExecution; +} + +/* test single stepping over hardware breakpoint */ +static const BYTE dummy_code[] = { 0x90, 0x90, 0x90, 0xc3 }; /* nop, nop, nop, ret */ + +static DWORD WINAPI bpx_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + got_exception++; + ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP, + "wrong exception code: %lx\n", rec->ExceptionCode); + + if(got_exception == 1) { + /* hw bp exception on first nop */ + ok( (void *)context->Rip == code_mem, "rip is wrong: %p instead of %p\n", + (void *)context->Rip, code_mem ); + ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n"); + ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n"); + context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */ + context->EFlags |= 0x100; /* enable single stepping */ + } else if (got_exception == 2) { + /* single step exception on second nop */ + ok( (char *)context->Rip == (char *)code_mem + 1, "rip is wrong: %p instead of %p\n", + (void *)context->Rip, (char *)code_mem + 1); + ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n"); + context->EFlags |= 0x100; + } else if( got_exception == 3) { + /* hw bp exception on second nop */ + ok( (void *)context->Rip == (char *)code_mem + 1, "rip is wrong: %p instead of %p\n", + (void *)context->Rip, (char *)code_mem + 1); + ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n"); + ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n"); + context->Dr0 = 0; /* clear breakpoint */ + context->EFlags |= 0x100; + } else { + /* single step exception on third nop */ + ok( (void *)context->Rip == (char *)code_mem + 2, "rip is wrong: %p instead of %p\n", + (void *)context->Rip, (char *)code_mem + 2); + ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); + ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n"); + } + + context->Dr6 = 0; /* clear status register */ + return ExceptionContinueExecution; +} + +/* test int3 handling */ +static const BYTE int3_code[] = { 0xcc, 0xc3 }; /* int 3, ret */ + +static DWORD WINAPI int3_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n", + code_mem, rec->ExceptionAddress); + ok( (void *)context->Rip == code_mem, "rip not at: %p, but at %p\n", code_mem, (void *)context->Rip); + if ((void *)context->Rip == code_mem) context->Rip++; /* skip breakpoint */ + + return ExceptionContinueExecution; +} + +/* trap unhandled exceptions */ +static LONG CALLBACK exc_filter( EXCEPTION_POINTERS *ptrs ) +{ + printf( "%04lx unhandled exception %08lx at %p rip %p eflags %lx\n", + GetCurrentProcessId(), + ptrs->ExceptionRecord->ExceptionCode, ptrs->ExceptionRecord->ExceptionAddress, + (void *)ptrs->ContextRecord->Rip, ptrs->ContextRecord->EFlags ); + fflush( stdout ); + return EXCEPTION_EXECUTE_HANDLER; +} + +static void test_exceptions(void) +{ + CONTEXT ctx; + NTSTATUS res; + struct dbgreg_test dreg_test; + + /* test handling of debug registers */ + memset(&dreg_test, 0, sizeof(dreg_test)); + + dreg_test.dr0 = 0x42424240; + dreg_test.dr2 = 0x126bb070; + dreg_test.dr3 = 0x0badbad0; + dreg_test.dr7 = 0xffff0115; + run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0); + if (check_debug_registers(1, &dreg_test)) + { + dreg_test.dr0 = 0x42424242; + dreg_test.dr2 = 0x100f0fe7; + dreg_test.dr3 = 0x0abebabe; + dreg_test.dr7 = 0x115; + run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0); + check_debug_registers(2, &dreg_test); + + /* test single stepping over hardware breakpoint */ + memset(&ctx, 0, sizeof(ctx)); + ctx.Dr0 = (ULONG_PTR)code_mem; /* set hw bp on first nop */ + ctx.Dr7 = 1; + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + res = pNtSetContextThread( GetCurrentThread(), &ctx ); + ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res); + + got_exception = 0; + run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0); + ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception); + } + + /* test single stepping behavior */ + SetUnhandledExceptionFilter( exc_filter ); + got_exception = 0; + run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0); + ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception); + + /* test alignment exceptions */ + got_exception = 0; + run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0); + todo_wine + ok(got_exception == 1 || broken(got_exception == 2) /* win7 */, "got %d alignment faults, expected 1\n", got_exception); + + /* test direction flag */ + got_exception = 0; + run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0); + ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception); +#ifndef __arm64ec__ +#if defined(__REACTOS__ ) && defined(_MSC_VER) + if (is_arm64ec) __cld(); +#else + if (is_arm64ec) __asm__ volatile( "cld" ); /* needed on Windows */ +#endif +#endif + + /* test int3 handling */ + run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0); + +#ifndef __arm64ec__ + if (!is_arm64ec) + { + USHORT ds, es, fs, gs, ss; + /* test segment registers */ + ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS; + res = pNtGetContextThread( GetCurrentThread(), &ctx ); + ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res ); +#if defined(__REACTOS__) && defined(_MSC_VER) + ds = __readsegds(); + es = __readseges(); + fs = __readsegfs(); + gs = __readseggs(); + ss = __readsegss(); +#else + __asm__ volatile( "movw %%ds,%0" : "=g" (ds) ); + __asm__ volatile( "movw %%es,%0" : "=g" (es) ); + __asm__ volatile( "movw %%fs,%0" : "=g" (fs) ); + __asm__ volatile( "movw %%gs,%0" : "=g" (gs) ); + __asm__ volatile( "movw %%ss,%0" : "=g" (ss) ); +#endif + ok( ctx.SegDs == ds, "wrong ds %04x / %04x\n", ctx.SegDs, ds ); + ok( ctx.SegEs == es, "wrong es %04x / %04x\n", ctx.SegEs, es ); + ok( ctx.SegFs == fs, "wrong fs %04x / %04x\n", ctx.SegFs, fs ); + ok( ctx.SegGs == gs || !gs, "wrong gs %04x / %04x\n", ctx.SegGs, gs ); + ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss ); + ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs ); + ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); + ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); + ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + ctx.SegDs = 0; + ctx.SegEs = ctx.SegFs; + ctx.SegFs = ctx.SegSs; + res = pNtSetContextThread( GetCurrentThread(), &ctx ); + ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res ); +#if defined(__REACTOS__) && defined(_MSC_VER) + ds = __readsegds(); + es = __readseges(); + fs = __readsegfs(); + gs = __readseggs(); + ss = __readsegss(); +#else + __asm__ volatile( "movw %%ds,%0" : "=g" (ds) ); + __asm__ volatile( "movw %%es,%0" : "=g" (es) ); + __asm__ volatile( "movw %%fs,%0" : "=g" (fs) ); + __asm__ volatile( "movw %%gs,%0" : "=g" (gs) ); + __asm__ volatile( "movw %%ss,%0" : "=g" (ss) ); +#endif + res = pNtGetContextThread( GetCurrentThread(), &ctx ); + ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res ); + ok( ctx.SegDs == ds, "wrong ds %04x / %04x\n", ctx.SegDs, ds ); + ok( ctx.SegEs == es, "wrong es %04x / %04x\n", ctx.SegEs, es ); + ok( ctx.SegFs == fs, "wrong fs %04x / %04x\n", ctx.SegFs, fs ); + ok( ctx.SegGs == gs || !gs, "wrong gs %04x / %04x\n", ctx.SegGs, gs ); + ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss ); + ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs ); + ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); + ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); + ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + } +#endif +} + +static DWORD WINAPI simd_fault_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + int *stage = *(int **)dispatcher->HandlerData; + + got_exception++; + + if (*stage == 1) + { + /* fault while executing sse instruction */ + context->Rip += 3; /* skip addps */ + return ExceptionContinueExecution; + } + else if (*stage == 2 || *stage == 3 ) + { + /* stage 2 - divide by zero fault */ + /* stage 3 - invalid operation fault */ + if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) + skip("system doesn't support SIMD exceptions\n"); + else + { + ULONG expect = *stage == 2 ? EXCEPTION_FLT_DIVIDE_BY_ZERO : EXCEPTION_FLT_INVALID_OPERATION; + ok( rec->ExceptionCode == expect, "exception code: %#lx, should be %#lx\n", + rec->ExceptionCode, expect ); + ok( rec->NumberParameters == 2, "# of params: %li, should be 2\n", rec->NumberParameters); + ok( rec->ExceptionInformation[0] == 0, "param #0: %Ix\n", rec->ExceptionInformation[0]); + ok( rec->ExceptionInformation[1] == context->MxCsr, "param #1: %Ix / %lx\n", + rec->ExceptionInformation[1], context->MxCsr); + } + context->Rip += 3; /* skip divps */ + } + else + ok(FALSE, "unexpected stage %x\n", *stage); + + return ExceptionContinueExecution; +} + +static const BYTE simd_exception_test[] = +{ + 0x48, 0x83, 0xec, 0x8, /* sub $0x8, %rsp */ + 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%rsp) */ + 0x8b, 0x04, 0x24, /* mov (%rsp),%eax * store mxcsr */ + 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%rsp) * enable divide by */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) * zero exceptions */ + 0xb9, 0x01, 0x00, 0x00, 0x00, /* movl $0x1,%ecx */ + 0x66, 0x48, 0x0f, 0x6e, 0xc9, /* movq %rcx,%xmm1 * fill dividend */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */ + 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */ + 0x89, 0x04, 0x24, /* mov %eax,(%rsp) * restore to old mxcsr */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) */ + 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */ + 0xc3, /* ret */ +}; + +static const BYTE simd_exception_test2[] = +{ + 0x48, 0x83, 0xec, 0x8, /* sub $0x8, %rsp */ + 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%rsp) */ + 0x8b, 0x04, 0x24, /* mov (%rsp),%eax * store mxcsr */ + 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%rsp) * enable invalid */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) * operation exceptions */ + 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */ + 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */ + 0x89, 0x04, 0x24, /* mov %eax,(%rsp) * restore to old mxcsr */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) */ + 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */ + 0xc3, /* ret */ +}; + +static const BYTE sse_check[] = +{ + 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */ + 0xc3, /* ret */ +}; + +static void test_simd_exceptions(void) +{ + int stage; + + /* test if CPU & OS can do sse */ + stage = 1; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0); + if(got_exception) { + skip("system doesn't support SSE\n"); + return; + } + + /* generate a SIMD exception */ + stage = 2; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, simd_exception_test, + sizeof(simd_exception_test), 0); + ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); + + /* generate a SIMD exception, test FPE_FLTINV */ + stage = 3; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, simd_exception_test2, + sizeof(simd_exception_test2), 0); + ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); +} + +static void test_prot_fault(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(exceptions); i++) + { + got_exception = 0; + run_exception_test(prot_fault_handler, &exceptions[i], &exceptions[i].code, + sizeof(exceptions[i].code), 0); + ok( got_exception == (exceptions[i].status != 0), + "%u: bad exception count %d\n", i, got_exception ); + } +} + +static LONG CALLBACK dpe_handler(EXCEPTION_POINTERS *info) +{ + EXCEPTION_RECORD *rec = info->ExceptionRecord; + DWORD old_prot; + + got_exception++; + + ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, + "got %#lx\n", rec->ExceptionCode); + ok(rec->NumberParameters == 2, "got %lu params\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT, + "got %#Ix\n", rec->ExceptionInformation[0]); + ok((void *)rec->ExceptionInformation[1] == code_mem, + "got %p\n", (void *)rec->ExceptionInformation[1]); + + VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot); + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void test_dpe_exceptions(void) +{ + static const BYTE ret[] = {0xc3}; + DWORD (CDECL *func)(void) = code_mem; + DWORD old_prot, val = 0, len = 0xdeadbeef; + NTSTATUS status; + void *handler; + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len ); + ok( status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, "got status %08lx\n", status ); + if (!status) + { + ok( len == sizeof(val), "wrong len %lu\n", len ); + ok( val == (MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_PERMANENT | + MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION), + "wrong val %08lx\n", val ); + } + else ok( len == 0xdeadbeef, "wrong len %lu\n", len ); + + val = MEM_EXECUTE_OPTION_DISABLE; + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val ); + ok( status == STATUS_INVALID_PARAMETER, "got status %08lx\n", status ); + + memcpy(code_mem, ret, sizeof(ret)); + + handler = pRtlAddVectoredExceptionHandler(TRUE, &dpe_handler); + ok(!!handler, "RtlAddVectoredExceptionHandler failed\n"); + + VirtualProtect(code_mem, 1, PAGE_NOACCESS, &old_prot); + + got_exception = 0; + func(); + ok(got_exception == 1, "got %u exceptions\n", got_exception); + + VirtualProtect(code_mem, 1, old_prot, &old_prot); + + VirtualProtect(code_mem, 1, PAGE_READWRITE, &old_prot); + + got_exception = 0; + func(); + ok(got_exception == 1, "got %u exceptions\n", got_exception); + + VirtualProtect(code_mem, 1, old_prot, &old_prot); + + pRtlRemoveVectoredExceptionHandler(handler); +} + +static const BYTE call_one_arg_code[] = { + 0x48, 0x83, 0xec, 0x28, /* sub $0x28,%rsp */ + 0x48, 0x89, 0xc8, /* mov %rcx,%rax */ + 0x48, 0x89, 0xd1, /* mov %rdx,%rcx */ + 0xff, 0xd0, /* callq *%rax */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x90, /* nop */ + 0x48, 0x83, 0xc4, 0x28, /* add $0x28,%rsp */ + 0xc3, /* retq */ +}; + +static int rtlraiseexception_unhandled_handler_called; +static int rtlraiseexception_teb_handler_called; +static int rtlraiseexception_handler_called; + +static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, + void *dispatcher, BOOL unhandled_handler ) +{ + void *addr = rec->ExceptionAddress; + + trace( "exception: %08lx flags:%lx addr:%p context: Rip:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Rip ); + + if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */ + { + ok( ((ULONG *)addr)[-1] == 0xd63f0120 /* blr x9 */, + "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n", + addr, (char *)addr - (char *)hntdll ); + ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_UNWOUND_TO_CALL), + "wrong context flags %lx\n", context->ContextFlags ); + } + else + { + ok( addr == (char *)code_mem + 0x0c || broken( addr == code_mem || !addr ) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0x0c ); + ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE) + || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS) + || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_XSTATE), + "wrong context flags %lx\n", context->ContextFlags ); + } + + /* check that pc is fixed up only for EXCEPTION_BREAKPOINT + * even if raised by RtlRaiseException + */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage && !is_arm64ec) + ok( context->Rip == (UINT_PTR)addr - 1, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr - 1 ); + else + ok( context->Rip == (UINT_PTR)addr, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr ); + + if (have_vectored_api) ok( context->Rax == 0xf00f00f0, "context->Rax is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->Rax ); +} + +static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + rtlraiseexception_unhandled_handler_called = 1; + rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE); + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return EXCEPTION_CONTINUE_SEARCH; + + /* pc in context is decreased by 1 + * Increase it again, else execution will continue in the middle of an instruction */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Rip == (UINT_PTR)rec->ExceptionAddress - 1)) + context->Rip++; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec, + EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, + EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + rtlraiseexception_teb_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + if (is_arm64ec) + { + ARM64EC_NT_CONTEXT *ec_ctx = (ARM64EC_NT_CONTEXT *)context; + DISPATCHER_CONTEXT_NONVOLREG_ARM64 *nonvol_regs; + int i; + + nonvol_regs = (void *)((DISPATCHER_CONTEXT_ARM64 *)dispatcher)->NonVolatileRegisters; + ok( nonvol_regs->GpNvRegs[0] == ec_ctx->X19, + "wrong non volatile reg x19 %I64x / %I64x\n", nonvol_regs->GpNvRegs[0], ec_ctx->X19 ); + ok( nonvol_regs->GpNvRegs[1] == ec_ctx->X20, + "wrong non volatile reg x20 %I64x / %I64x\n", nonvol_regs->GpNvRegs[1], ec_ctx->X20 ); + ok( nonvol_regs->GpNvRegs[2] == ec_ctx->X21, + "wrong non volatile reg x21 %I64x / %I64x\n", nonvol_regs->GpNvRegs[2], ec_ctx->X21 ); + ok( nonvol_regs->GpNvRegs[3] == ec_ctx->X22, + "wrong non volatile reg x22 %I64x / %I64x\n", nonvol_regs->GpNvRegs[3], ec_ctx->X22 ); + ok( nonvol_regs->GpNvRegs[4] == 0, "wrong non volatile reg x23 %I64x\n", nonvol_regs->GpNvRegs[4] ); + ok( nonvol_regs->GpNvRegs[5] == 0, "wrong non volatile reg x24 %I64x\n", nonvol_regs->GpNvRegs[5] ); + ok( nonvol_regs->GpNvRegs[6] == ec_ctx->X25, + "wrong non volatile reg x25 %I64x / %I64x\n", nonvol_regs->GpNvRegs[6], ec_ctx->X25 ); + ok( nonvol_regs->GpNvRegs[7] == ec_ctx->X26, + "wrong non volatile reg x26 %I64x / %I64x\n", nonvol_regs->GpNvRegs[7], ec_ctx->X26 ); + ok( nonvol_regs->GpNvRegs[8] == ec_ctx->X27, + "wrong non volatile reg x27 %I64x / %I64x\n", nonvol_regs->GpNvRegs[8], ec_ctx->X27 ); + ok( nonvol_regs->GpNvRegs[9] == 0, "wrong non volatile reg x28 %I64x\n", nonvol_regs->GpNvRegs[9] ); + ok( nonvol_regs->GpNvRegs[10] > ec_ctx->Fp, /* previous frame */ + "wrong non volatile reg x29 %I64x / %I64x\n", nonvol_regs->GpNvRegs[10], ec_ctx->Fp ); + + for (i = 0; i < NONVOL_FP_NUMREG_ARM64; i++) + ok( nonvol_regs->FpNvRegs[i] == ec_ctx->V[i + 8].D[0], + "wrong non volatile reg d%u %g / %g\n", i + 8, + nonvol_regs->FpNvRegs[i] , ec_ctx->V[i + 8].D[0] ); + } + rtlraiseexception_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + void *addr = rec->ExceptionAddress; + + if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */ + ok( ((ULONG *)addr)[-1] == 0xd63f0120 /* blr x9 */, + "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n", + addr, (char *)addr - (char *)hntdll ); + else + ok( addr == (char *)code_mem + 0xc || broken(addr == code_mem || !addr ) /* 2008 */, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0xc ); + + /* check that Rip is fixed up only for EXCEPTION_BREAKPOINT + * even if raised by RtlRaiseException + */ + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage && !is_arm64ec) + ok( context->Rip == (UINT_PTR)addr - 1, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr - 1 ); + else + ok( context->Rip == (UINT_PTR)addr, + "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr ); + + /* test if context change is preserved from vectored handler to stack handlers */ + context->Rax = 0xf00f00f0; + + return EXCEPTION_CONTINUE_SEARCH; +} + +static void run_rtlraiseexception_test(DWORD exceptioncode) +{ + unsigned char buf[4 + 4 + 4 + 8 + 2 + 8 + 2]; + RUNTIME_FUNCTION runtime_func; + UNWIND_INFO *unwind = (UNWIND_INFO *)buf; + EXCEPTION_REGISTRATION_RECORD frame; + EXCEPTION_RECORD record; + PVOID vectored_handler = NULL; + + void (CDECL *func)(void* function, EXCEPTION_RECORD* record) = code_mem; + + record.ExceptionCode = exceptioncode; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; /* does not matter, copied return address */ + record.NumberParameters = 0; + + runtime_func.BeginAddress = 0; + runtime_func.EndAddress = sizeof(call_one_arg_code); + runtime_func.UnwindData = 0x1000; + + unwind->Version = 1; + unwind->Flags = UNW_FLAG_EHANDLER; + unwind->SizeOfProlog = 4; + unwind->CountOfCodes = 1; + unwind->FrameRegister = 0; + unwind->FrameOffset = 0; + *(WORD *)&buf[4] = 0x4204; /* sub $0x28,%rsp */ + *(ULONG *)&buf[8] = 0x1014; + *(const void **)&buf[12] = NULL; + /* movabs $, %rax */ + buf[20] = 0x48; + buf[21] = 0xb8; + *(void **)&buf[22] = rtlraiseexception_handler; + /* jmp *%rax */ + buf[30] = 0xff; + buf[31] = 0xe0; + + memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf)); + pRtlAddFunctionTable( &runtime_func, 1, (ULONG_PTR)code_mem ); + + frame.Handler = rtlraiseexception_teb_handler; + frame.Prev = NtCurrentTeb()->Tib.ExceptionList; + + memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code)); + + NtCurrentTeb()->Tib.ExceptionList = &frame; + if (have_vectored_api) + { + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + } + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler); + + rtlraiseexception_handler_called = 0; + rtlraiseexception_teb_handler_called = 0; + rtlraiseexception_unhandled_handler_called = 0; + func(pRtlRaiseException, &record); + if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */ + ok( ((ULONG *)record.ExceptionAddress)[-1] == 0xd63f0120 /* blr x9 */, + "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n", + record.ExceptionAddress, (char *)record.ExceptionAddress - (char *)hntdll ); + else + ok( record.ExceptionAddress == (char *)code_mem + 0x0c, + "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0c ); + + todo_wine + ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" ); + ok( rtlraiseexception_handler_called, "Frame handler called\n" ); + ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" ); + + if (have_vectored_api) + pRtlRemoveVectoredExceptionHandler(vectored_handler); + + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL); + pRtlDeleteFunctionTable( &runtime_func ); + NtCurrentTeb()->Tib.ExceptionList = frame.Prev; +} + +static void test_rtlraiseexception(void) +{ + if (!pRtlRaiseException) + { + skip("RtlRaiseException not found\n"); + return; + } + + /* test without debugger */ + run_rtlraiseexception_test(0x12345); + run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); + run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); +} + +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) +{ + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + DEBUG_EVENT de; + DWORD continuestatus; + PVOID code_mem_address = NULL; + NTSTATUS status; + SIZE_T size_read; + BOOL ret; + int counter = 0; + si.cb = sizeof(si); + + if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess) + { + skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n"); + return; + } + + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "could not create child process error: %lu\n", GetLastError()); + if (!ret) + return; + + do + { + continuestatus = cont_status; + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); + + ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); + ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError()); + + if (de.dwThreadId != pi.dwThreadId) + { + trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode); + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status); + continue; + } + + if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) + { + if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress) + { + skip("child process loaded at different address, terminating it\n"); + pNtTerminateProcess(pi.hProcess, 0); + } + } + else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) + { + CONTEXT ctx; + enum debugger_stages stage; + + counter++; + status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, + sizeof(code_mem_address), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + ctx.ContextFlags = CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_EXCEPTION_REQUEST; + status = pNtGetContextThread(pi.hThread, &ctx); + ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags); + + trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n", + de.u.Exception.ExceptionRecord.ExceptionCode, + de.u.Exception.ExceptionRecord.ExceptionAddress, + de.u.Exception.dwFirstChance, (char *)ctx.Rip, (char *)ctx.Rax); + + if (counter > 100) + { + ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n"); + pNtTerminateProcess(pi.hProcess, 1); + } + else if (counter < 2) /* startup breakpoint */ + { + /* breakpoint is inside ntdll */ + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( hntdll ); + + ok( (char *)ctx.Rip >= (char *)hntdll && + (char *)ctx.Rip < (char *)hntdll + nt->OptionalHeader.SizeOfImage, + "wrong rip %p ntdll %p-%p\n", (void *)ctx.Rip, hntdll, + (char *)hntdll + nt->OptionalHeader.SizeOfImage ); + check_context_exception_request( ctx.ContextFlags, TRUE ); + } + else + { + if (stage == STAGE_RTLRAISE_NOT_HANDLED) + { + if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */ + ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */, + "Rip not in entry thunk %p (ntdll+%Ix)\n", + (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll ); + else + ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c, "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0x0c); + /* setting the context from debugger does not affect the context that the + * exception handler gets, except on w2008 */ + ctx.Rip = (UINT_PTR)code_mem_address + 0x0e; + ctx.Rax = 0xf00f00f1; + /* let the debuggee handle the exception */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); + } + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) + { + if (de.u.Exception.dwFirstChance) + { + if (is_arm64ec) + ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */, + "Rip not in entry thunk %p (ntdll+%Ix)\n", + (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll ); + else + ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c, + "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0x0c); + /* setting the context from debugger does not affect the context that the + * exception handler gets, except on w2008 */ + ctx.Rip = (UINT_PTR)code_mem_address + 0x0e; + ctx.Rax = 0xf00f00f1; + /* pass exception to debuggee + * exception will not be handled and a second chance exception will be raised */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + { + /* debugger gets context after exception handler has played with it */ + if (is_arm64ec) + ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */, + "Rip not in entry thunk %p (ntdll+%Ix)\n", + (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll ); + else if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n", + (char *)ctx.Rip, (char *)code_mem_address + 0xb); + ctx.Rip += 1; + } + else ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c, "Rip at 0x%I64x instead of %p\n", + ctx.Rip, (char *)code_mem_address + 0x0c); + /* here we handle exception */ + } + check_context_exception_request( ctx.ContextFlags, FALSE ); + } + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Rip == (char *)code_mem_address + 0x30, + "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip); + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); + } + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Rip == (char *)code_mem_address + 2, + "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip); + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, TRUE ); + } + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, + "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, + EXCEPTION_INVALID_HANDLE); + ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, + "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); + + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); + } + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + check_context_exception_request( ctx.ContextFlags, FALSE ); + } + else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE) + { + check_context_exception_request( ctx.ContextFlags, TRUE ); + test_debugger_xstate(pi.hThread, &ctx, stage); + } + else if (stage == STAGE_SEGMENTS) + { +#ifdef __arm64ec__ + USHORT ss = 0x2b; +#elif defined(__REACTOS__) && defined(_MSC_VER) + USHORT ss = __readsegss(); +#else + USHORT ss; + __asm__( "movw %%ss,%0" : "=r" (ss) ); +#endif + ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss ); + ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs ); + ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); + ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); + ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + ctx.SegSs = 0; + ctx.SegDs = 0; + ctx.SegEs = ctx.SegFs; + ctx.SegGs = 0; + status = pNtSetContextThread( pi.hThread, &ctx ); + ok( status == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", status ); + status = pNtGetContextThread( pi.hThread, &ctx ); + ok( status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status ); + todo_wine ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss ); + ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs ); + ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs ); + todo_wine ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs ); + ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs ); + check_context_exception_request( ctx.ContextFlags, TRUE ); + } + else + ok(FALSE, "unexpected stage %u\n", stage); + + status = pNtSetContextThread(pi.hThread, &ctx); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); + } + } + else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) + { + enum debugger_stages stage; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); + + memset(buffer, 0, sizeof(buffer)); + status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, + de.u.DebugString.nDebugStringLength * char_size, &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } + else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ + ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (de.dwDebugEventCode == RIP_EVENT) + { + enum debugger_stages stage; + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) + { + ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", + de.u.RipInfo.dwError, 0x11223344); + ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n", + de.u.RipInfo.dwType, 0x55667788); + } + else + ok(FALSE, "unexpected stage %x\n", stage); + + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); + + } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + wait_child_process( pi.hProcess ); + ret = CloseHandle(pi.hThread); + ok(ret, "error %lu\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "error %lu\n", GetLastError()); +} + +static void test_thread_context(void) +{ + CONTEXT context; + NTSTATUS status; + int i; + struct expected + { + ULONG64 Rax, Rbx, Rcx, Rdx, Rsi, Rdi, R8, R9, R10, R11, + R12, R13, R14, R15, Rbp, Rsp, Rip, prev_frame, EFlags; + ULONG MxCsr; + XMM_SAVE_AREA32 FltSave; + WORD SegCs, SegDs, SegEs, SegFs, SegGs, SegSs; + } expect; + NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = code_mem; + + static const BYTE call_func[] = + { + 0x55, /* push %rbp */ + 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */ + 0x48, 0x8d, 0x64, 0x24, 0xd0, /* lea -0x30(%rsp),%rsp */ + 0x49, 0x89, 0x00, /* mov %rax,(%r8) */ + 0x49, 0x89, 0x58, 0x08, /* mov %rbx,0x8(%r8) */ + 0x49, 0x89, 0x48, 0x10, /* mov %rcx,0x10(%r8) */ + 0x49, 0x89, 0x50, 0x18, /* mov %rdx,0x18(%r8) */ + 0x49, 0x89, 0x70, 0x20, /* mov %rsi,0x20(%r8) */ + 0x49, 0x89, 0x78, 0x28, /* mov %rdi,0x28(%r8) */ + 0x4d, 0x89, 0x40, 0x30, /* mov %r8,0x30(%r8) */ + 0x4d, 0x89, 0x48, 0x38, /* mov %r9,0x38(%r8) */ + 0x4d, 0x89, 0x50, 0x40, /* mov %r10,0x40(%r8) */ + 0x4d, 0x89, 0x58, 0x48, /* mov %r11,0x48(%r8) */ + 0x4d, 0x89, 0x60, 0x50, /* mov %r12,0x50(%r8) */ + 0x4d, 0x89, 0x68, 0x58, /* mov %r13,0x58(%r8) */ + 0x4d, 0x89, 0x70, 0x60, /* mov %r14,0x60(%r8) */ + 0x4d, 0x89, 0x78, 0x68, /* mov %r15,0x68(%r8) */ + 0x49, 0x89, 0x68, 0x70, /* mov %rbp,0x70(%r8) */ + 0x49, 0x89, 0x60, 0x78, /* mov %rsp,0x78(%r8) */ + 0xff, 0x75, 0x08, /* pushq 0x8(%rbp) */ + 0x41, 0x8f, 0x80, 0x80, 0x00, 0x00, 0x00, /* popq 0x80(%r8) */ + 0xff, 0x75, 0x00, /* pushq 0x0(%rbp) */ + 0x41, 0x8f, 0x80, 0x88, 0x00, 0x00, 0x00, /* popq 0x88(%r8) */ + 0x9c, /* pushfq */ + 0x41, 0x8f, 0x80, 0x90, 0x00, 0x00, 0x00, /* popq 0x90(%r8) */ + 0x41, 0x0f, 0xae, 0x98, 0x98, 0x00, 0x00, 0x00, /* stmxcsr 0x98(%r8) */ + 0x41, 0x0f, 0xae, 0x80, 0xa0, 0x00, 0x00, 0x00, /* fxsave 0xa0(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0x80, 0x40, 0x01, 0x00, 0x00, /* movdqa %xmm0,0x140(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0x88, 0x50, 0x01, 0x00, 0x00, /* movdqa %xmm1,0x150(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0x90, 0x60, 0x01, 0x00, 0x00, /* movdqa %xmm2,0x160(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0x98, 0x70, 0x01, 0x00, 0x00, /* movdqa %xmm3,0x170(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0xa0, 0x80, 0x01, 0x00, 0x00, /* movdqa %xmm4,0x180(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0xa8, 0x90, 0x01, 0x00, 0x00, /* movdqa %xmm5,0x190(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0xb0, 0xa0, 0x01, 0x00, 0x00, /* movdqa %xmm6,0x1a0(%r8) */ + 0x66, 0x41, 0x0f, 0x7f, 0xb8, 0xb0, 0x01, 0x00, 0x00, /* movdqa %xmm7,0x1b0(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0x80, 0xc0, 0x01, 0x00, 0x00, /* movdqa %xmm8,0x1c0(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0x88, 0xd0, 0x01, 0x00, 0x00, /* movdqa %xmm9,0x1d0(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0x90, 0xe0, 0x01, 0x00, 0x00, /* movdqa %xmm10,0x1e0(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0x98, 0xf0, 0x01, 0x00, 0x00, /* movdqa %xmm11,0x1f0(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0xa0, 0x00, 0x02, 0x00, 0x00, /* movdqa %xmm12,0x200(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0xa8, 0x10, 0x02, 0x00, 0x00, /* movdqa %xmm13,0x210(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0xb0, 0x20, 0x02, 0x00, 0x00, /* movdqa %xmm14,0x220(%r8) */ + 0x66, 0x45, 0x0f, 0x7f, 0xb8, 0x30, 0x02, 0x00, 0x00, /* movdqa %xmm15,0x230(%r8) */ + 0x41, 0x8c, 0x88, 0xa0, 0x02, 0x00, 0x00, /* mov %cs,0x2a0(%r8) */ + 0x41, 0x8c, 0x98, 0xa2, 0x02, 0x00, 0x00, /* mov %ds,0x2a2(%r8) */ + 0x41, 0x8c, 0x80, 0xa4, 0x02, 0x00, 0x00, /* mov %es,0x2a4(%r8) */ + 0x41, 0x8c, 0xa0, 0xa6, 0x02, 0x00, 0x00, /* mov %fs,0x2a6(%r8) */ + 0x41, 0x8c, 0xa8, 0xa8, 0x02, 0x00, 0x00, /* mov %gs,0x2a8(%r8) */ + 0x41, 0x8c, 0x90, 0xaa, 0x02, 0x00, 0x00, /* mov %ss,0x2aa(%r8) */ + 0x41, 0xff, 0xd1, /* callq *%r9 */ + 0xc9, /* leaveq */ + 0xc3, /* retq */ + }; + + memcpy( func_ptr, call_func, sizeof(call_func) ); + +#define COMPARE(reg) \ + ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg ) + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + func_ptr( &context, 0, &expect, pRtlCaptureContext ); + + if (is_arm64ec) + { + ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT), + "wrong flags %08lx\n", context.ContextFlags ); + ok( (context.EFlags & ~0xd5) == (expect.EFlags & ~0xd5), "wrong EFlags %lx / %I64x\n", + context.EFlags, expect.EFlags ); + ok( context.SegCs == 0xcccc, "wrong SegCs %x\n", context.SegCs); + ok( context.SegDs == 0xcccc, "wrong SegDs %x\n", context.SegDs); + ok( context.SegEs == 0xcccc, "wrong SegEs %x\n", context.SegEs); + ok( context.SegFs == 0xcccc, "wrong SegFs %x\n", context.SegFs); + ok( context.SegGs == 0xcccc, "wrong SegGs %x\n", context.SegGs); + ok( context.SegSs == 0xcccc, "wrong SegSs %x\n", context.SegSs); + } + else + { + ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT), + "wrong flags %08lx\n", context.ContextFlags ); + COMPARE( Rax ); + COMPARE( Rcx ); + COMPARE( Rdx ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + COMPARE( EFlags ); + COMPARE( SegCs ); + COMPARE( SegDs ); + COMPARE( SegEs ); + COMPARE( SegFs ); + COMPARE( SegGs ); + COMPARE( SegSs ); + ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )), + "wrong FltSave\n" ); + } + COMPARE( Rbx ); + COMPARE( Rsi ); + COMPARE( Rdi ); + COMPARE( R12 ); + COMPARE( R13 ); + COMPARE( R14 ); + COMPARE( R15 ); + COMPARE( Rbp ); + COMPARE( Rsp ); + COMPARE( MxCsr ); + COMPARE( FltSave.MxCsr ); + COMPARE( FltSave.ControlWord ); + COMPARE( FltSave.StatusWord ); + for (i = 0; i < 16; i++) + ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ), + "wrong xmm%u\n", i ); + /* Rip is return address from RtlCaptureContext */ + ok( context.Rip == (ULONG64)func_ptr + sizeof(call_func) - 2, + "wrong Rip %p/%p\n", (void *)context.Rip, (char *)func_ptr + sizeof(call_func) - 2 ); + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; + + status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread ); + ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status ); + + if (is_arm64ec) + { + /* Rsp is the stack upon entry to the ARM64 NtGetContextThread syscall */ + ok( context.Rsp <= expect.Rsp - sizeof(ARM64_NT_CONTEXT) && context.Rsp >= expect.Rsp - 0x1000, + "wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp ); + } + else + { + /* other registers are not preserved */ + COMPARE( Rbx ); + COMPARE( Rsi ); + COMPARE( Rdi ); + COMPARE( R12 ); + COMPARE( R13 ); + COMPARE( R14 ); + COMPARE( R15 ); + COMPARE( Rbp ); + /* Rsp is the stack upon entry to NtGetContextThread */ + ok( context.Rsp == expect.Rsp - 8, + "wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp ); + /* Rip is somewhere close to the NtGetContextThread implementation */ + ok( (char *)context.Rip >= (char *)pNtGetContextThread - 0x40000 && + (char *)context.Rip <= (char *)pNtGetContextThread + 0x40000, + "wrong Rip %p/%p\n", (void *)context.Rip, (void *)pNtGetContextThread ); + } + COMPARE( MxCsr ); + COMPARE( SegCs ); + COMPARE( SegDs ); + COMPARE( SegEs ); + COMPARE( SegFs ); + if (expect.SegGs) COMPARE( SegGs ); + COMPARE( SegSs ); + + /* AMD CPUs don't save the opcode or data pointer if no exception is + * pending; see the AMD64 Architecture Programmer's Manual Volume 5 s.v. + * FXSAVE */ + memcpy( &expect.FltSave, &context.FltSave, 0x12 ); + + ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, ErrorOffset )), "wrong FltSave\n" ); + for (i = 6; i < 16; i++) + ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ), + "wrong xmm%u\n", i ); +#undef COMPARE +} + +static void test_continue(void) +{ + struct context_pair { + CONTEXT before; + CONTEXT after; + } contexts; + NTSTATUS (*func_ptr)( struct context_pair *, void *arg, void *continue_func, void *capture_func ) = code_mem; + KCONTINUE_ARGUMENT args = { .ContinueType = KCONTINUE_UNWIND }; + int i; + + static const BYTE call_func[] = + { + /* ret at 8*13(rsp) */ + + /* need to preserve these */ + 0x53, /* push %rbx; 8*12(rsp) */ + 0x55, /* push %rbp; 8*11(rsp) */ + 0x56, /* push %rsi; 8*10(rsp) */ + 0x57, /* push %rdi; 8*9(rsp) */ + 0x41, 0x54, /* push %r12; 8*8(rsp) */ + 0x41, 0x55, /* push %r13; 8*7(rsp) */ + 0x41, 0x56, /* push %r14; 8*6(rsp) */ + 0x41, 0x57, /* push %r15; 8*5(rsp) */ + + 0x48, 0x83, 0xec, 0x28, /* sub $0x28, %rsp; reserve space for rsp and outgoing reg params */ + 0x48, 0x89, 0x64, 0x24, 0x20, /* mov %rsp, 8*4(%rsp); for stack validation */ + + /* save args */ + 0x48, 0x89, 0x4c, 0x24, 0x70, /* mov %rcx, 8*14(%rsp) */ + 0x48, 0x89, 0x54, 0x24, 0x78, /* mov %rdx, 8*15(%rsp) */ + 0x4c, 0x89, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, /* mov %r8, 8*16(%rsp) */ + 0x4c, 0x89, 0x8c, 0x24, 0x88, 0x00, 0x00, 0x00, /* mov %r9, 8*17(%rsp) */ + + /* invoke capture context */ + 0x41, 0xff, 0xd1, /* call *%r9 */ + + /* overwrite general registers */ + 0x48, 0xb8, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, /* movabs $0xdeadbeefdeadbeef, %rax */ + 0x48, 0x89, 0xc1, /* mov %rax, %rcx */ + 0x48, 0x89, 0xc2, /* mov %rax, %rdx */ + 0x48, 0x89, 0xc3, /* mov %rax, %rbx */ + 0x48, 0x89, 0xc5, /* mov %rax, %rbp */ + 0x48, 0x89, 0xc6, /* mov %rax, %rsi */ + 0x48, 0x89, 0xc7, /* mov %rax, %rdi */ + 0x49, 0x89, 0xc0, /* mov %rax, %r8 */ + 0x49, 0x89, 0xc1, /* mov %rax, %r9 */ + 0x49, 0x89, 0xc2, /* mov %rax, %r10 */ + 0x49, 0x89, 0xc3, /* mov %rax, %r11 */ + 0x49, 0x89, 0xc4, /* mov %rax, %r12 */ + 0x49, 0x89, 0xc5, /* mov %rax, %r13 */ + 0x49, 0x89, 0xc6, /* mov %rax, %r14 */ + 0x49, 0x89, 0xc7, /* mov %rax, %r15 */ + + /* overwrite SSE registers */ + 0x66, 0x48, 0x0f, 0x6e, 0xc0, /* movq %rax, %xmm0 */ + 0x66, 0x0f, 0x6c, 0xc0, /* punpcklqdq %xmm0, %xmm0; extend to high quadword */ + 0x0f, 0x28, 0xc8, /* movaps %xmm0, %xmm1 */ + 0x0f, 0x28, 0xd0, /* movaps %xmm0, %xmm2 */ + 0x0f, 0x28, 0xd8, /* movaps %xmm0, %xmm3 */ + 0x0f, 0x28, 0xe0, /* movaps %xmm0, %xmm4 */ + 0x0f, 0x28, 0xe8, /* movaps %xmm0, %xmm5 */ + 0x0f, 0x28, 0xf0, /* movaps %xmm0, %xmm6 */ + 0x0f, 0x28, 0xf8, /* movaps %xmm0, %xmm7 */ + 0x44, 0x0f, 0x28, 0xc0, /* movaps %xmm0, %xmm8 */ + 0x44, 0x0f, 0x28, 0xc8, /* movaps %xmm0, %xmm9 */ + 0x44, 0x0f, 0x28, 0xd0, /* movaps %xmm0, %xmm10 */ + 0x44, 0x0f, 0x28, 0xd8, /* movaps %xmm0, %xmm11 */ + 0x44, 0x0f, 0x28, 0xe0, /* movaps %xmm0, %xmm12 */ + 0x44, 0x0f, 0x28, 0xe8, /* movaps %xmm0, %xmm13 */ + 0x44, 0x0f, 0x28, 0xf0, /* movaps %xmm0, %xmm14 */ + 0x44, 0x0f, 0x28, 0xf8, /* movaps %xmm0, %xmm15 */ + + /* FIXME: overwrite debug, x87 FPU and AVX registers to test those */ + + /* load args */ + 0x48, 0x8b, 0x4c, 0x24, 0x70, /* mov 8*14(%rsp), %rcx; context */ + 0x48, 0x8b, 0x54, 0x24, 0x78, /* mov 8*15(%rsp), %rdx; arg */ + 0x48, 0x83, 0xec, 0x70, /* sub $0x70, %rsp; change stack */ + + /* setup context to return to label 1 */ + 0x48, 0x8d, 0x05, 0x18, 0x00, 0x00, 0x00, /* lea 1f(%rip), %rax */ + 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax, 0xf8(%rcx); context.Rip */ + + /* flip some EFLAGS */ + 0x9c, /* pushf */ + /* + 0x0001 Carry flag + 0x0004 Parity flag + 0x0010 Auxiliary Carry flag + 0x0040 Zero flag + 0x0080 Sign flag + FIXME: 0x0400 Direction flag - not changing as it breaks Wine + 0x0800 Overflow flag + ~0x4000~ Nested task flag - not changing - breaks Wine + = 0x8d5 + */ + 0x48, 0x81, 0x34, 0x24, 0xd5, 0x08, 0x00, 0x00, /* xorq $0x8d5, (%rsp) */ + 0x9d, /* popf */ + + /* invoke NtContinue... */ + 0xff, 0x94, 0x24, 0xf0, 0x00, 0x00, 0x00, /* call *8*16+0x70(%rsp) */ + + /* validate stack pointer */ + 0x48, 0x3b, 0x64, 0x24, 0x20, /* 1: cmp 0x20(%rsp), %rsp */ + 0x74, 0x02, /* je 2f; jump over ud2 */ + 0x0f, 0x0b, /* ud2; stack pointer invalid, let's crash */ + + /* invoke capture context */ + 0x48, 0x8b, 0x4c, 0x24, 0x70, /* 2: mov 8*14(%rsp), %rcx; context */ + 0x48, 0x81, 0xc1, 0xd0, 0x04, 0x00, 0x00, /* add $0x4d0, %rcx; +sizeof(CONTEXT) to get context->after */ + 0xff, 0x94, 0x24, 0x88, 0x00, 0x00, 0x00, /* call *8*17(%rsp) */ + + /* free stack */ + 0x48, 0x83, 0xc4, 0x28, /* add $0x28, %rsp */ + + /* restore back */ + 0x41, 0x5f, /* pop %r15 */ + 0x41, 0x5e, /* pop %r14 */ + 0x41, 0x5d, /* pop %r13 */ + 0x41, 0x5c, /* pop %r12 */ + 0x5f, /* pop %rdi */ + 0x5e, /* pop %rsi */ + 0x5d, /* pop %rbp */ + 0x5b, /* pop %rbx */ + 0xc3 /* ret */ + }; + + if (!pRtlCaptureContext) + { + win_skip("RtlCaptureContext is not available.\n"); + return; + } + + memcpy( func_ptr, call_func, sizeof(call_func) ); + FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) ); + + func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext ); + +#define COMPARE(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) + + COMPARE( Rax ); + COMPARE( Rdx ); + COMPARE( Rbx ); + COMPARE( Rbp ); + COMPARE( Rsi ); + COMPARE( Rdi ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + COMPARE( R12 ); + COMPARE( R13 ); + COMPARE( R14 ); + COMPARE( R15 ); + + for (i = 0; i < 16; i++) + ok( !memcmp( &contexts.before.Xmm0 + i, &contexts.after.Xmm0 + i, sizeof(contexts.before.Xmm0) ), + "wrong xmm%u %08I64x%08I64x/%08I64x%08I64x\n", i, *(&contexts.before.Xmm0.High + i*2), *(&contexts.before.Xmm0.Low + i*2), + *(&contexts.after.Xmm0.High + i*2), *(&contexts.after.Xmm0.Low + i*2) ); + + apc_count = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext ); + ok( apc_count == 0, "apc called\n" ); + func_ptr( &contexts, (void *)1, NtContinue, pRtlCaptureContext ); + ok( apc_count == 1, "apc not called\n" ); + + if (!pNtContinueEx) + { + win_skip( "NtContinueEx not supported\n" ); + return; + } + + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + +#define COMPARE(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) + + COMPARE( Rax ); + COMPARE( Rdx ); + COMPARE( Rbx ); + COMPARE( Rbp ); + COMPARE( Rsi ); + COMPARE( Rdi ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + COMPARE( R12 ); + COMPARE( R13 ); + COMPARE( R14 ); + COMPARE( R15 ); + + for (i = 0; i < 16; i++) + ok( !memcmp( &contexts.before.Xmm0 + i, &contexts.after.Xmm0 + i, sizeof(contexts.before.Xmm0) ), + "wrong xmm%u %08I64x%08I64x/%08I64x%08I64x\n", i, *(&contexts.before.Xmm0.High + i*2), *(&contexts.before.Xmm0.Low + i*2), + *(&contexts.after.Xmm0.High + i*2), *(&contexts.after.Xmm0.Low + i*2) ); + + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + ok( apc_count == 1, "apc called\n" ); + args.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT; + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + ok( apc_count == 2, "apc not called\n" ); + +#undef COMPARE +} + +static void test_wow64_context(void) +{ + const char appname[] = "C:\\windows\\syswow64\\cmd.exe"; + char cmdline[256]; + THREAD_BASIC_INFORMATION info; + PROCESS_INFORMATION pi; + STARTUPINFOA si = {0}; + WOW64_CONTEXT ctx, *ctx_ptr = NULL; + CONTEXT context; + NTSTATUS ret; + TEB teb; + TEB32 teb32; + SIZE_T res, cpu_size = 0; + WOW64_CPURESERVED *cpu = NULL; + WOW64_CPU_AREA_INFO cpu_info; + BOOL r, got32, got64; + unsigned int i, cs32, cs64; + ULONG_PTR ecx, rcx; + + memset(&ctx, 0x55, sizeof(ctx)); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + ret = pRtlWow64GetThreadContext( GetCurrentThread(), &ctx ); + ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_PARTIAL_COPY), "got %#lx\n", ret); + + sprintf( cmdline, "\"%s\" /c for /l %%n in () do @echo >nul", appname ); + r = CreateProcessA( appname, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); + ok( r, "failed to start %s err %lu\n", appname, GetLastError() ); + + ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + ok(ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags); + ok(!ctx.Ebp, "got ebp %08lx\n", ctx.Ebp); + ok(!ctx.Ecx, "got ecx %08lx\n", ctx.Ecx); + ok(!ctx.Edx, "got edx %08lx\n", ctx.Edx); + ok(!ctx.Esi, "got esi %08lx\n", ctx.Esi); + ok(!ctx.Edi, "got edi %08lx\n", ctx.Edi); + ok((ctx.EFlags & ~2) == 0x200, "got eflags %08lx\n", ctx.EFlags); + ok((WORD) ctx.FloatSave.ControlWord == 0x27f, "got control word %08lx\n", + ctx.FloatSave.ControlWord); + ok(*(WORD *)ctx.ExtendedRegisters == 0x27f, "got SSE control word %04x\n", + *(WORD *)ctx.ExtendedRegisters); + + ret = pRtlWow64SetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + + pNtQueryInformationThread( pi.hThread, ThreadBasicInformation, &info, sizeof(info), NULL ); + if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0; + ok( res == sizeof(teb), "wrong len %Ix\n", res ); + + memset( &teb32, 0, sizeof(teb32) ); + if (teb.WowTebOffset > 1) + { + if (!ReadProcessMemory( pi.hProcess, (char *)info.TebBaseAddress + teb.WowTebOffset, + &teb32, sizeof(teb32), &res )) res = 0; + ok( res == sizeof(teb32), "wrong len %Ix\n", res ); + + ok( ((ctx.Esp + 0xfff) & ~0xfff) == teb32.Tib.StackBase, + "esp is not at top of stack: %08lx / %08lx\n", ctx.Esp, teb32.Tib.StackBase ); + ok( ULongToPtr( teb32.Tib.StackBase ) <= teb.DeallocationStack || + ULongToPtr( teb32.DeallocationStack ) >= teb.Tib.StackBase, + "stacks overlap %08lx-%08lx / %p-%p\n", teb32.DeallocationStack, teb32.Tib.StackBase, + teb.DeallocationStack, teb.Tib.StackBase ); + } + + if (pRtlWow64GetCpuAreaInfo) + { + ok( teb.TlsSlots[WOW64_TLS_CPURESERVED] == teb.Tib.StackBase, "wrong cpu reserved %p / %p\n", + teb.TlsSlots[WOW64_TLS_CPURESERVED], teb.Tib.StackBase ); + cpu_size = 0x1000 - ((ULONG_PTR)teb.TlsSlots[WOW64_TLS_CPURESERVED] & 0xfff); + cpu = malloc( cpu_size ); + if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res )) res = 0; + ok( res == cpu_size, "wrong len %Ix\n", res ); + ok( cpu->Machine == IMAGE_FILE_MACHINE_I386, "wrong machine %04x\n", cpu->Machine ); + ret = pRtlWow64GetCpuAreaInfo( cpu, 0, &cpu_info ); + ok( !ret, "RtlWow64GetCpuAreaInfo failed %lx\n", ret ); + /* work around pointer truncation bug on win10 <= 1709 */ + if (!((ULONG_PTR)cpu_info.Context >> 32)) + { + cpu_info.Context = (char *)cpu + (ULONG)((char *)cpu_info.Context - (char *)cpu); + cpu_info.ContextEx = (char *)cpu + (ULONG)((char *)cpu_info.ContextEx - (char *)cpu); + } + ctx_ptr = (WOW64_CONTEXT *)cpu_info.Context; + ok(!*(void **)cpu_info.ContextEx, "got context_ex %p\n", *(void **)cpu_info.ContextEx); + ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx_ptr->ContextFlags); + ok(ctx_ptr->Eax == ctx.Eax, "got eax %08lx / %08lx\n", ctx_ptr->Eax, ctx.Eax); + ok(ctx_ptr->Ebx == ctx.Ebx, "got ebx %08lx / %08lx\n", ctx_ptr->Ebx, ctx.Ebx); + ok(ctx_ptr->Ecx == ctx.Ecx, "got ecx %08lx / %08lx\n", ctx_ptr->Ecx, ctx.Ecx); + ok(ctx_ptr->Edx == ctx.Edx, "got edx %08lx / %08lx\n", ctx_ptr->Edx, ctx.Edx); + ok(ctx_ptr->Ebp == ctx.Ebp, "got ebp %08lx / %08lx\n", ctx_ptr->Ebp, ctx.Ebp); + ok(ctx_ptr->Esi == ctx.Esi, "got esi %08lx / %08lx\n", ctx_ptr->Esi, ctx.Esi); + ok(ctx_ptr->Edi == ctx.Edi, "got edi %08lx / %08lx\n", ctx_ptr->Edi, ctx.Edi); + ok(ctx_ptr->SegCs == ctx.SegCs, "got cs %04lx / %04lx\n", ctx_ptr->SegCs, ctx.SegCs); + ok(ctx_ptr->SegDs == ctx.SegDs, "got ds %04lx / %04lx\n", ctx_ptr->SegDs, ctx.SegDs); + ok(ctx_ptr->SegEs == ctx.SegEs, "got es %04lx / %04lx\n", ctx_ptr->SegEs, ctx.SegEs); + ok(ctx_ptr->SegFs == ctx.SegFs, "got fs %04lx / %04lx\n", ctx_ptr->SegFs, ctx.SegFs); + ok(ctx_ptr->SegGs == ctx.SegGs, "got gs %04lx / %04lx\n", ctx_ptr->SegGs, ctx.SegGs); + ok(ctx_ptr->SegSs == ctx.SegSs, "got ss %04lx / %04lx\n", ctx_ptr->SegSs, ctx.SegSs); + ok(ctx_ptr->EFlags == ctx.EFlags, "got eflags %08lx / %08lx\n", ctx_ptr->EFlags, ctx.EFlags); + ok((WORD)ctx_ptr->FloatSave.ControlWord == ctx.FloatSave.ControlWord, + "got control word %08lx / %08lx\n", ctx_ptr->FloatSave.ControlWord, ctx.FloatSave.ControlWord); + ok(*(WORD *)ctx_ptr->ExtendedRegisters == *(WORD *)ctx.ExtendedRegisters, + "got SSE control word %04x / %04x\n", *(WORD *)ctx_ptr->ExtendedRegisters, + *(WORD *)ctx.ExtendedRegisters); + + ecx = ctx.Ecx; + ctx.Ecx = 0x12345678; + ret = pRtlWow64SetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res )) res = 0; + ok( res == cpu_size, "wrong len %Ix\n", res ); + todo_wine + ok( ctx_ptr->Ecx == 0x12345678, "got ecx %08lx\n", ctx_ptr->Ecx ); + ctx.Ecx = ecx; + pRtlWow64SetThreadContext( pi.hThread, &ctx ); + } + else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" ); + + memset( &context, 0x55, sizeof(context) ); + context.ContextFlags = CONTEXT_ALL; + ret = pNtGetContextThread( pi.hThread, &context ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + ok( context.ContextFlags == is_arm64ec ? CONTEXT_FULL : CONTEXT_ALL, + "got context flags %#lx\n", context.ContextFlags ); + ok( !context.Rsi, "rsi is not zero %Ix\n", context.Rsi ); + ok( !context.Rdi, "rdi is not zero %Ix\n", context.Rdi ); + ok( !context.Rbp, "rbp is not zero %Ix\n", context.Rbp ); + ok( !context.R8, "r8 is not zero %Ix\n", context.R8 ); + ok( !context.R9, "r9 is not zero %Ix\n", context.R9 ); + ok( !context.R10, "r10 is not zero %Ix\n", context.R10 ); + ok( !context.R11, "r11 is not zero %Ix\n", context.R11 ); + ok( !context.R12, "r12 is not zero %Ix\n", context.R12 ); + ok( !context.R13, "r13 is not zero %Ix\n", context.R13 ); + ok( !context.R14, "r14 is not zero %Ix\n", context.R14 ); + ok( !context.R15, "r15 is not zero %Ix\n", context.R15 ); + ok( context.MxCsr == 0x1f80, "wrong mxcsr %08lx\n", context.MxCsr ); + ok( context.FltSave.ControlWord == 0x27f, "wrong control %08x\n", context.FltSave.ControlWord ); + if (LOWORD(context.ContextFlags) & CONTEXT_SEGMENTS) + { + ok( context.SegDs == ctx.SegDs, "wrong ds %04x / %04lx\n", context.SegDs, ctx.SegDs ); + ok( context.SegEs == ctx.SegEs, "wrong es %04x / %04lx\n", context.SegEs, ctx.SegEs ); + ok( context.SegFs == ctx.SegFs, "wrong fs %04x / %04lx\n", context.SegFs, ctx.SegFs ); + ok( context.SegGs == ctx.SegGs, "wrong gs %04x / %04lx\n", context.SegGs, ctx.SegGs ); + ok( context.SegSs == ctx.SegSs, "wrong ss %04x / %04lx\n", context.SegSs, ctx.SegSs ); + } + cs32 = ctx.SegCs; + cs64 = context.SegCs; + if (cs32 == cs64) + { + todo_wine win_skip( "no wow64 support\n" ); + goto done; + } + + ok( !context.Rax, "rax is not zero %Ix\n", context.Rax ); + ok( !context.Rbx, "rbx is not zero %Ix\n", context.Rbx ); + ok( ((ULONG_PTR)context.Rsp & ~0xfff) == ((ULONG_PTR)teb.Tib.StackBase & ~0xfff), + "rsp is not at top of stack %p / %p\n", (void *)context.Rsp, teb.Tib.StackBase ); + ok( context.EFlags == 0x200 || context.EFlags == 0x202, "wrong flags %08lx\n", context.EFlags ); + + for (i = 0, got32 = got64 = FALSE; i < 10000 && !(got32 && got64); i++) + { + ResumeThread( pi.hThread ); + Sleep( 1 ); + SuspendThread( pi.hThread ); + memset( &context, 0x55, sizeof(context) ); + context.ContextFlags = CONTEXT_ALL; + ret = pNtGetContextThread( pi.hThread, &context ); + ok( ret == STATUS_SUCCESS, "got %#lx\n", ret ); + if (ret) break; + if (context.SegCs == cs32 && got32) continue; + if (context.SegCs == cs64 && got64) continue; + if (context.SegCs != cs32 && context.SegCs != cs64) + { + ok( 0, "unexpected cs %04x\n", context.SegCs ); + break; + } + + memset( &ctx, 0x55, sizeof(ctx) ); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + ok( ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags ); + + ctx.ContextFlags = WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST; + ret = pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/, + "got context flags %#lx\n", ctx.ContextFlags ); + + if (context.SegCs == cs32) + { + trace( "in 32-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING + | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); + ok( ctx.Eip == context.Rip, "cs32: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip ); + ok( ctx.Ebp == context.Rbp, "cs32: ebp %08lx / %p\n", ctx.Ebp, (void *)context.Rbp ); + ok( ctx.Esp == context.Rsp, "cs32: esp %08lx / %p\n", ctx.Esp, (void *)context.Rsp ); + ok( ctx.Eax == context.Rax, "cs32: eax %08lx / %p\n", ctx.Eax, (void *)context.Rax ); + ok( ctx.Ebx == context.Rbx, "cs32: ebx %08lx / %p\n", ctx.Ebx, (void *)context.Rbx ); + ok( ctx.Ecx == context.Rcx || broken(ctx.Ecx == (ULONG)context.Rcx), + "cs32: ecx %08lx / %p\n", ctx.Ecx, (void *)context.Rcx ); + ok( ctx.Edx == context.Rdx, "cs32: edx %08lx / %p\n", ctx.Edx, (void *)context.Rdx ); + ok( ctx.Esi == context.Rsi, "cs32: esi %08lx / %p\n", ctx.Esi, (void *)context.Rsi ); + ok( ctx.Edi == context.Rdi, "cs32: edi %08lx / %p\n", ctx.Edi, (void *)context.Rdi ); + ok( ctx.SegCs == cs32, "cs32: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 ); + ok( ctx.SegDs == context.SegDs, "cs32: wrong ds %04lx / %04x\n", ctx.SegDs, context.SegDs ); + ok( ctx.SegEs == context.SegEs, "cs32: wrong es %04lx / %04x\n", ctx.SegEs, context.SegEs ); + ok( ctx.SegFs == context.SegFs, "cs32: wrong fs %04lx / %04x\n", ctx.SegFs, context.SegFs ); + ok( ctx.SegGs == context.SegGs, "cs32: wrong gs %04lx / %04x\n", ctx.SegGs, context.SegGs ); + ok( ctx.SegSs == context.SegSs, "cs32: wrong ss %04lx / %04x\n", ctx.SegSs, context.SegSs ); + if (teb32.DeallocationStack) + ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase, + "cs32: esp not inside 32-bit stack %08lx / %08lx-%08lx\n", ctx.Esp, + teb32.DeallocationStack, teb32.Tib.StackBase ); + /* r12 points to the TEB */ + ok( (void *)context.R12 == info.TebBaseAddress, + "cs32: r12 not pointing to the TEB %p / %p\n", (void *)context.R12, info.TebBaseAddress ); + /* r13 points inside the cpu area */ + ok( (void *)context.R13 >= teb.TlsSlots[WOW64_TLS_CPURESERVED] && + context.R13 <= ((ULONG_PTR)teb.TlsSlots[WOW64_TLS_CPURESERVED] | 0xfff), + "cs32: r13 not pointing into cpu area %p / %p\n", (void *)context.R13, + teb.TlsSlots[WOW64_TLS_CPURESERVED] ); + /* r14 stores the 64-bit stack pointer */ + ok( (void *)context.R14 >= teb.DeallocationStack && (void *)context.R14 <= teb.Tib.StackBase, + "cs32: r14 not inside 32-bit stack %p / %p-%p\n", (void *)context.R14, + (void *)teb.DeallocationStack, (void *)teb.Tib.StackBase ); + + if (pRtlWow64GetCpuAreaInfo) + { + /* in 32-bit mode, the 32-bit context is the current cpu context, not the stored one */ + if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], + cpu, cpu_size, &res )) res = 0; + ok( res == cpu_size, "wrong len %Ix\n", res ); + ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL, + "cs32: got context flags %#lx\n", ctx_ptr->ContextFlags); + + /* changing either context changes the actual cpu context */ + rcx = context.Rcx; + ecx = ctx_ptr->Ecx; + context.Rcx = 0xfedcba987654321ull; + pNtSetContextThread( pi.hThread, &context ); + memset( &ctx, 0x55, sizeof(ctx) ); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + pRtlWow64GetThreadContext( pi.hThread, &ctx ); + todo_wine + ok( ctx.Ecx == 0x87654321, "cs32: ecx set to %08lx\n", ctx.Ecx ); + ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res ); + ok( ctx_ptr->Ecx == ecx, "cs32: ecx set to %08lx\n", ctx_ptr->Ecx ); + ctx.Ecx = 0x33334444; + pRtlWow64SetThreadContext( pi.hThread, &ctx ); + memset( &ctx, 0x55, sizeof(ctx) ); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok( ctx.Ecx == 0x33334444, "cs32: ecx set to %08lx\n", ctx.Ecx ); + ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res ); + ok( ctx_ptr->Ecx == ecx, "cs32: ecx set to %08lx\n", ctx_ptr->Ecx ); + memset( &context, 0x55, sizeof(context) ); + context.ContextFlags = CONTEXT_ALL; + pNtGetContextThread( pi.hThread, &context ); + todo_wine + ok( context.Rcx == 0x33334444, "cs32: rcx set to %p\n", (void *)context.Rcx ); + /* restore everything */ + context.Rcx = rcx; + pNtSetContextThread( pi.hThread, &context ); + } + got32 = TRUE; + } + else + { + trace( "in 64-bit mode %04x\n", context.SegCs ); + if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) + ok( ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE) + || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST + | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE), + "got %#lx.\n", ctx.ContextFlags ); + ok( ctx.Eip != context.Rip, "cs64: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip); + ok( ctx.SegCs == cs32, "cs64: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 ); + if (!is_arm64ec) + { + ok( ctx.SegDs == context.SegDs, "cs64: wrong ds %04lx / %04x\n", ctx.SegDs, context.SegDs ); + ok( ctx.SegEs == context.SegEs, "cs64: wrong es %04lx / %04x\n", ctx.SegEs, context.SegEs ); + ok( ctx.SegFs == context.SegFs, "cs64: wrong fs %04lx / %04x\n", ctx.SegFs, context.SegFs ); + ok( ctx.SegGs == context.SegGs, "cs64: wrong gs %04lx / %04x\n", ctx.SegGs, context.SegGs ); + ok( ctx.SegSs == context.SegSs, "cs64: wrong ss %04lx / %04x\n", ctx.SegSs, context.SegSs ); + } + if (teb32.DeallocationStack) + ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase, + "cs64: esp not inside 32-bit stack %08lx / %08lx-%08lx\n", ctx.Esp, + teb32.DeallocationStack, teb32.Tib.StackBase ); + ok( ((void *)context.Rsp >= teb.DeallocationStack && (void *)context.Rsp <= teb.Tib.StackBase) || + (context.Rsp >= teb32.DeallocationStack && context.Rsp <= teb32.Tib.StackBase), + "cs64: rsp not inside stack %p / 64-bit %p-%p 32-bit %p-%p\n", (void *)context.Rsp, + teb.DeallocationStack, teb.Tib.StackBase, + ULongToPtr(teb32.DeallocationStack), ULongToPtr(teb32.Tib.StackBase) ); + + if (pRtlWow64GetCpuAreaInfo) + { + /* in 64-bit mode, the 32-bit context is stored in the cpu area */ + if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], + cpu, cpu_size, &res )) res = 0; + ok( res == cpu_size, "wrong len %Ix\n", res ); + ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL, + "cs64: got context flags %#lx\n", ctx_ptr->ContextFlags); + ok(ctx_ptr->Eip == ctx.Eip, "cs64: got eip %08lx / %08lx\n", ctx_ptr->Eip, ctx.Eip); + ok(ctx_ptr->Eax == ctx.Eax, "cs64: got eax %08lx / %08lx\n", ctx_ptr->Eax, ctx.Eax); + ok(ctx_ptr->Ebx == ctx.Ebx, "cs64: got ebx %08lx / %08lx\n", ctx_ptr->Ebx, ctx.Ebx); + ok(ctx_ptr->Ecx == ctx.Ecx, "cs64: got ecx %08lx / %08lx\n", ctx_ptr->Ecx, ctx.Ecx); + ok(ctx_ptr->Edx == ctx.Edx, "cs64: got edx %08lx / %08lx\n", ctx_ptr->Edx, ctx.Edx); + ok(ctx_ptr->Ebp == ctx.Ebp, "cs64: got ebp %08lx / %08lx\n", ctx_ptr->Ebp, ctx.Ebp); + ok(ctx_ptr->Esi == ctx.Esi, "cs64: got esi %08lx / %08lx\n", ctx_ptr->Esi, ctx.Esi); + ok(ctx_ptr->Edi == ctx.Edi, "cs64: got edi %08lx / %08lx\n", ctx_ptr->Edi, ctx.Edi); + ok(ctx_ptr->EFlags == ctx.EFlags, "cs64: got eflags %08lx / %08lx\n", ctx_ptr->EFlags, ctx.EFlags); + + /* changing one context doesn't change the other one */ + rcx = context.Rcx; + ecx = ctx.Ecx; + context.Rcx = 0xfedcba987654321ull; + pNtSetContextThread( pi.hThread, &context ); + memset( &ctx, 0x55, sizeof(ctx) ); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok( ctx.Ecx == ecx, "cs64: ecx set to %08lx\n", ctx.Ecx ); + ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res ); + ok( ctx_ptr->Ecx == ecx, "cs64: ecx set to %08lx\n", ctx_ptr->Ecx ); + ctx.Ecx = 0x22223333; + pRtlWow64SetThreadContext( pi.hThread, &ctx ); + memset( &ctx, 0x55, sizeof(ctx) ); + ctx.ContextFlags = WOW64_CONTEXT_ALL; + pRtlWow64GetThreadContext( pi.hThread, &ctx ); + ok( ctx.Ecx == 0x22223333, "cs64: ecx set to %08lx\n", ctx.Ecx ); + ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res ); + todo_wine + ok( ctx_ptr->Ecx == 0x22223333, "cs64: ecx set to %08lx\n", ctx_ptr->Ecx ); + memset( &context, 0x55, sizeof(context) ); + context.ContextFlags = CONTEXT_ALL; + pNtGetContextThread( pi.hThread, &context ); + ok( context.Rcx == 0xfedcba987654321ull, "cs64: rcx set to %p\n", (void *)context.Rcx ); + /* restore everything */ + context.Rcx = rcx; + pNtSetContextThread( pi.hThread, &context ); + ctx.Ecx = ecx; + pRtlWow64SetThreadContext( pi.hThread, &ctx ); + } + got64 = TRUE; + if (is_arm64ec) break; /* no 32-bit %cs on arm64ec */ + } + } + if (!got32) skip( "failed to test 32-bit context\n" ); + if (!got64) skip( "failed to test 64-bit context\n" ); + +done: + pNtTerminateProcess(pi.hProcess, 0); + free( cpu ); +} + +static BYTE saved_KiUserExceptionDispatcher_bytes[12]; +static BOOL hook_called; +static void *hook_KiUserExceptionDispatcher_rip; +static void *dbg_except_continue_handler_rip; +static void *hook_exception_address; +static struct +{ + ULONG64 old_rax; + ULONG64 old_rdx; + ULONG64 old_rsi; + ULONG64 old_rdi; + ULONG64 old_rbp; + ULONG64 old_rsp; + ULONG64 new_rax; + ULONG64 new_rdx; + ULONG64 new_rsi; + ULONG64 new_rdi; + ULONG64 new_rbp; + ULONG64 new_rsp; +} +test_kiuserexceptiondispatcher_regs; + +static ULONG64 test_kiuserexceptiondispatcher_saved_r12; + +struct machine_frame +{ + ULONG64 rip; + ULONG64 cs; + ULONG64 eflags; + ULONG64 rsp; + ULONG64 ss; +}; + +static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + trace("handler context->Rip %#Ix, codemem %p.\n", context->Rip, code_mem); + got_exception = 1; + dbg_except_continue_handler_rip = (void *)context->Rip; + ++context->Rip; + memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + + RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp, + (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef); + return ExceptionContinueExecution; +} + +static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e) +{ + EXCEPTION_RECORD *rec = e->ExceptionRecord; + CONTEXT *context = e->ContextRecord; + + trace("dbg_except_continue_vectored_handler, code %#lx, Rip %#Ix.\n", rec->ExceptionCode, context->Rip); + + if (rec->ExceptionCode == 0xceadbeef) + { + ok(context->P1Home == (ULONG64)0xdeadbeeffeedcafe, "Got unexpected context->P1Home %#Ix.\n", context->P1Home); + context->R12 = test_kiuserexceptiondispatcher_saved_r12; + return EXCEPTION_CONTINUE_EXECUTION; + } + + ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode); + + got_exception = 1; + dbg_except_continue_handler_rip = (void *)context->Rip; + if (NtCurrentTeb()->Peb->BeingDebugged && !is_arm64ec) + ++context->Rip; + + if (context->Rip >= (ULONG64)code_mem && context->Rip < (ULONG64)code_mem + 0x100) + RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp, + (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef); + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void * WINAPI hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context) +{ + struct machine_frame *frame = (struct machine_frame *)(((ULONG_PTR)(rec + 1) + 0x0f) & ~0x0f); + CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); + + trace("rec %p context %p context->Rip %#Ix, context->Rsp %#Ix, ContextFlags %#lx.\n", + rec, context, context->Rip, context->Rsp, context->ContextFlags); + + hook_called = TRUE; + /* Broken on Win2008, probably rec offset in stack is different. */ + ok(rec->ExceptionCode == 0x80000003 || rec->ExceptionCode == 0xceadbeef || broken(!rec->ExceptionCode), + "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); + + ok( !((ULONG_PTR)context & 15), "unaligned context %p\n", context ); + ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset ); + ok( xctx->All.Length >= sizeof(CONTEXT) + offsetof(CONTEXT_EX, align), "wrong All.Length %lx\n", xctx->All.Length ); + ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset ); + ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length ); + ok( (void *)(xctx + 1) == (void *)rec, "wrong ptrs %p / %p\n", xctx, rec ); + ok( frame->rip == context->Rip, "wrong rip %Ix / %Ix\n", frame->rip, context->Rip ); + ok( frame->rsp == context->Rsp, "wrong rsp %Ix / %Ix\n", frame->rsp, context->Rsp ); + + hook_KiUserExceptionDispatcher_rip = (void *)context->Rip; + hook_exception_address = rec->ExceptionAddress; + memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + return pKiUserExceptionDispatcher; +} + +static void * WINAPI hook_KiUserExceptionDispatcher_arm64ec(EXCEPTION_RECORD *rec, CONTEXT *context) +{ + ARM64_NT_CONTEXT *arm64_context = (ARM64_NT_CONTEXT *)(context + 1); + + trace("rec %p context %p context->Rip %#Ix, context->Rsp %#Ix, ContextFlags %#lx.\n", + rec, context, context->Rip, context->Rsp, context->ContextFlags); + hook_called = TRUE; + ok(rec->ExceptionCode == 0x80000003 || rec->ExceptionCode == 0xceadbeef, + "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode); + + ok( !((ULONG_PTR)context & 15), "unaligned context %p\n", context ); + ok( arm64_context->Pc == context->Rip, "wrong rip %Ix / %Ix\n", arm64_context->Pc, context->Rip ); + ok( arm64_context->Sp == context->Rsp, "wrong rsp %Ix / %Ix\n", arm64_context->Sp, context->Rsp ); + + hook_KiUserExceptionDispatcher_rip = (void *)context->Rip; + hook_exception_address = rec->ExceptionAddress; + memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + return pKiUserExceptionDispatcher; +} + +static void test_KiUserExceptionDispatcher(void) +{ + LPVOID vectored_handler; + static BYTE except_code[] = + { + 0x48, 0xb9, /* mov imm64, %rcx */ + /* offset: 0x2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */ + 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */ + 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */ + 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */ + 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */ + 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */ + 0x48, 0x83, 0xc1, 0x30, /* add $0x30, %rcx */ + + /* offset: 0x25 */ + 0xcc, /* int3 */ + + 0x0f, 0x0b, /* ud2, illegal instruction */ + + /* offset: 0x28 */ + 0x48, 0xb9, /* mov imm64, %rcx */ + /* offset: 0x2a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */ + 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */ + 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */ + 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */ + 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */ + 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */ + 0xc3, /* ret */ + }; + static BYTE hook_trampoline[] = + { + 0x48, 0x89, 0xe2, /* mov %rsp,%rdx */ + 0x48, 0x8d, 0x8c, 0x24, 0xf0, 0x04, 0x00, 0x00, + /* lea 0x4f0(%rsp),%rcx */ + 0x4c, 0x89, 0x22, /* mov %r12,(%rdx) */ + 0x48, 0xb8, /* movabs hook_KiUserExceptionDispatcher,%rax */ + 0,0,0,0,0,0,0,0, /* offset 16 */ + 0xff, 0xd0, /* callq *rax */ + 0x48, 0x31, 0xc9, /* xor %rcx, %rcx */ + 0x48, 0x31, 0xd2, /* xor %rdx, %rdx */ + 0xff, 0xe0, /* jmpq *rax */ + }; + static BYTE hook_trampoline_arm64ec[] = + { + 0x48, 0x8d, 0x54, 0x24, 0x08, /* lea 0x8(%rsp),%rdx */ + 0x48, 0x8d, 0x8a, 0x60, 0x08, 0x00, 0x00, + /* lea 0x860(%rdx),%rcx */ + 0x4c, 0x89, 0x22, /* mov %r12,(%rdx) */ + 0x48, 0xb8, /* movabs hook_KiUserExceptionDispatcher_arm64ec,%rax */ + 0,0,0,0,0,0,0,0, /* offset 16 */ + 0xff, 0xd0, /* callq *rax */ + 0x48, 0x31, 0xc9, /* xor %rcx, %rcx */ + 0x48, 0x31, 0xd2, /* xor %rdx, %rdx */ + 0xff, 0xe0, /* jmpq *rax */ + }; + + BYTE patched_KiUserExceptionDispatcher_bytes[12]; + void *bpt_address, *trampoline_ptr; + EXCEPTION_RECORD record; + DWORD old_protect; + CONTEXT ctx; + LONG pass; + BYTE *ptr; + BOOL ret; + + *(ULONG64 *)(except_code + 2) = (ULONG64)&test_kiuserexceptiondispatcher_regs; + *(ULONG64 *)(except_code + 0x2a) = (ULONG64)&test_kiuserexceptiondispatcher_regs.new_rax; + + *(ULONG_PTR *)(hook_trampoline_arm64ec + 17) = (ULONG_PTR)hook_KiUserExceptionDispatcher_arm64ec; + *(ULONG_PTR *)(hook_trampoline + 16) = (ULONG_PTR)hook_KiUserExceptionDispatcher; + trampoline_ptr = (char *)code_mem + 1024; + if (is_arm64ec) + memcpy(trampoline_ptr, hook_trampoline_arm64ec, sizeof(hook_trampoline_arm64ec)); + else + memcpy(trampoline_ptr, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes), + PAGE_EXECUTE_READWRITE, &old_protect); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + + memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher, + sizeof(saved_KiUserExceptionDispatcher_bytes)); + ptr = (BYTE *)patched_KiUserExceptionDispatcher_bytes; + /* mov hook_trampoline, %rax */ + *ptr++ = 0x48; + *ptr++ = 0xb8; + *(void **)ptr = trampoline_ptr; + ptr += sizeof(ULONG64); + /* jmp *rax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code), PAGE_EXECUTE_READ); + ok(got_exception, "Handler was not called.\n"); + todo_wine_if(is_arm64ec) + ok(hook_called, "Hook was not called.\n"); + if (!hook_called) return; + + ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#Ix.\n", + test_kiuserexceptiondispatcher_regs.new_rax); + ok(test_kiuserexceptiondispatcher_regs.old_rsi + == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n"); + ok(test_kiuserexceptiondispatcher_regs.old_rdi + == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n"); + ok(test_kiuserexceptiondispatcher_regs.old_rbp + == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n"); + + bpt_address = (BYTE *)code_mem + 0x25; + + ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */, + "Got unexpected exception address %p, expected %p.\n", + hook_exception_address, bpt_address); + ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + hook_KiUserExceptionDispatcher_rip, bpt_address); + ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + dbg_except_continue_handler_rip, bpt_address); + + memset(&record, 0, sizeof(record)); + record.ExceptionCode = 0x80000003; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; + record.NumberParameters = 0; + + vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler); + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + hook_called = FALSE; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(!hook_called, "Hook was called.\n"); + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + hook_called = FALSE; + NtCurrentTeb()->Peb->BeingDebugged = 1; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called, "Hook was not called.\n"); + + ok(hook_exception_address == (BYTE *)hook_KiUserExceptionDispatcher_rip + !is_arm64ec + || broken(!hook_exception_address) /* 2008 */, "Got unexpected addresses %p, %p.\n", + hook_KiUserExceptionDispatcher_rip, hook_exception_address); + + RemoveVectoredExceptionHandler(vectored_handler); + + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + hook_called = FALSE; + + run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code), PAGE_EXECUTE_READ); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called, "Hook was not called.\n"); + ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + hook_KiUserExceptionDispatcher_rip, bpt_address); + ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n", + dbg_except_continue_handler_rip, bpt_address); + + ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#Ix.\n", + test_kiuserexceptiondispatcher_regs.new_rax); + ok(test_kiuserexceptiondispatcher_regs.old_rsi + == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n"); + ok(test_kiuserexceptiondispatcher_regs.old_rdi + == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n"); + ok(test_kiuserexceptiondispatcher_regs.old_rbp + == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n"); + + NtCurrentTeb()->Peb->BeingDebugged = 0; + + vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler); + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */ + { + memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes, + sizeof(patched_KiUserExceptionDispatcher_bytes)); + got_exception = 0; + hook_called = FALSE; + + record.ExceptionCode = 0xceadbeef; + test_kiuserexceptiondispatcher_saved_r12 = ctx.R12; + ctx.R12 = (ULONG64)0xdeadbeeffeedcafe; + +#if defined(__REACTOS__) && defined(_MSC_VER) + Call_NtRaiseException(&record, &ctx, TRUE, pNtRaiseException); +#else +#ifdef __GNUC__ + /* Spoil r12 value to make sure it doesn't come from the current userspace registers. */ + __asm__ volatile("movq $0xdeadcafe, %%r12" : : : "%r12"); +#endif + pNtRaiseException(&record, &ctx, TRUE); +#endif + ok(0, "Shouldn't be reached.\n"); + } + else + { + ok(pass == 3, "Got unexpected pass %ld.\n", pass); + } + ok(hook_called, "Hook was not called.\n"); + RemoveVectoredExceptionHandler(vectored_handler); + + ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes), + old_protect, &old_protect); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); +} + + +static BYTE saved_KiUserApcDispatcher[12]; + +static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context) +{ + struct machine_frame *frame = (struct machine_frame *)(context + 1); + UINT i; + + trace( "context %p, context->Rip %#Ix, context->Rsp %#Ix (%#Ix), ContextFlags %#lx.\n", + context, context->Rip, context->Rsp, + (char *)context->Rsp - (char *)context, context->ContextFlags ); + + ok( context->P1Home == 0x1234, "wrong p1 %#Ix\n", context->P1Home ); + ok( context->P2Home == 0x5678, "wrong p2 %#Ix\n", context->P2Home ); + ok( context->P3Home == 0xdeadbeef, "wrong p3 %#Ix\n", context->P3Home ); + ok( context->P4Home == (ULONG_PTR)apc_func, "wrong p4 %#Ix / %p\n", context->P4Home, apc_func ); + + /* machine frame offset varies between Windows versions */ + for (i = 0; i < 16; i++) + { + if (frame->rip == context->Rip) break; + frame = (struct machine_frame *)((ULONG64 *)frame + 2); + } + ok( frame->rip == context->Rip, "wrong rip %#Ix / %#Ix\n", frame->rip, context->Rip ); + ok( frame->rsp == context->Rsp, "wrong rsp %#Ix / %#Ix\n", frame->rsp, context->Rsp ); + + hook_called = TRUE; + memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + BYTE hook_trampoline[] = + { + 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */ + 0x48, 0xb8, /* movabs hook_KiUserApcDispatcher,%rax */ + 0,0,0,0,0,0,0,0, /* offset 5 */ + 0xff, 0xd0, /* callq *rax */ + 0xff, 0xe0, /* jmpq *rax */ + }; + + BYTE patched_KiUserApcDispatcher[12]; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + *(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) ); + ptr = patched_KiUserApcDispatcher; + /* mov $code_mem, %rax */ + *ptr++ = 0x48; + *ptr++ = 0xb8; + *(void **)ptr = code_mem; + ptr += sizeof(ULONG64); + /* jmp *rax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) ); + + hook_called = FALSE; + apc_count = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 1, "APC was not called\n" ); + /* hooking is bypassed on arm64ec */ + ok( is_arm64ec ? !hook_called : hook_called, "hook was not called\n" ); + + VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect ); +} + +static void WINAPI hook_KiUserCallbackDispatcher(void *rsp) +{ + struct + { + ULONG64 padding[4]; + void *args; + ULONG len; + ULONG id; + struct machine_frame frame; + BYTE args_data[0]; + } *stack = rsp; + + KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id]; + + trace( "rsp %p args %p (%#Ix) len %lu id %lu\n", stack, stack->args, + (char *)stack->args - (char *)stack, stack->len, stack->id ); + + ok( !((ULONG_PTR)stack & 15), "unaligned stack %p\n", stack ); + ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data ); + ok( (BYTE *)stack->frame.rsp - &stack->args_data[stack->len] <= 16, "wrong rsp %p / %p\n", + (void *)stack->frame.rsp, &stack->args_data[stack->len] ); + + if (stack->frame.rip && pRtlPcToFileHeader) + { + void *mod, *win32u = GetModuleHandleA("win32u.dll"); + + pRtlPcToFileHeader( (void *)stack->frame.rip, &mod ); + if (win32u) ok( mod == win32u, "ret address %Ix not in win32u %p\n", stack->frame.rip, win32u ); + else trace( "ret address %Ix in %p\n", stack->frame.rip, mod ); + } + NtCallbackReturn( NULL, 0, func( stack->args, stack->len )); +} + +static void test_KiUserCallbackDispatcher(void) +{ + BYTE hook_trampoline[] = + { + 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */ + 0x48, 0xb8, /* movabs hook_KiUserCallbackDispatcher,%rax */ + 0,0,0,0,0,0,0,0, /* offset 5 */ + 0xff, 0xd0, /* callq *rax */ + }; + + BYTE saved_KiUserCallbackDispatcher[12]; + BYTE patched_KiUserCallbackDispatcher[12]; + DWORD old_protect; + BYTE *ptr; + BOOL ret; + + *(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserCallbackDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_KiUserCallbackDispatcher, pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher) ); + ptr = patched_KiUserCallbackDispatcher; + /* mov $code_mem, %rax */ + *ptr++ = 0x48; + *ptr++ = 0xb8; + *(void **)ptr = code_mem; + ptr += sizeof(ULONG64); + /* jmp *rax */ + *ptr++ = 0xff; + *ptr++ = 0xe0; + memcpy( pKiUserCallbackDispatcher, patched_KiUserCallbackDispatcher, sizeof(patched_KiUserCallbackDispatcher) ); + + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + + memcpy( pKiUserCallbackDispatcher, saved_KiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher)); + VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher), old_protect, &old_protect ); +} + +static BOOL got_nested_exception, got_prev_frame_exception; +static void *nested_exception_initial_frame; + +static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + trace("nested_exception_handler Rip %p, Rsp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n", + (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress); + + if (rec->ExceptionCode == 0x80000003 + && !(rec->ExceptionFlags & EXCEPTION_NESTED_CALL)) + { + ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok((void *)context->Rsp == frame, "Got unexpected frame %p.\n", frame); + ok(*(void **)frame == (char *)code_mem + 5, "Got unexpected *frame %p.\n", *(void **)frame); + ok(context->Rip == (ULONG_PTR)((char *)code_mem + 7), "Got unexpected Rip %#Ix.\n", context->Rip); + + nested_exception_initial_frame = frame; + RaiseException(0xdeadbeef, 0, 0, 0); + ++context->Rip; + return ExceptionContinueExecution; + } + + if (rec->ExceptionCode == 0xdeadbeef && (rec->ExceptionFlags == EXCEPTION_NESTED_CALL + || rec->ExceptionFlags == (EXCEPTION_NESTED_CALL | EXCEPTION_SOFTWARE_ORIGINATE))) + { + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + got_nested_exception = TRUE; + ok(frame == nested_exception_initial_frame, "Got unexpected frame %p.\n", frame); + return ExceptionContinueSearch; + } + + ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE), + "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags); + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok(frame == (void *)((BYTE *)nested_exception_initial_frame + 8), + "Got unexpected frame %p.\n", frame); + got_prev_frame_exception = TRUE; + return ExceptionContinueExecution; +} + +static const BYTE nested_except_code[] = +{ + 0xe8, 0x02, 0x00, 0x00, 0x00, /* call nest */ + 0x90, /* nop */ + 0xc3, /* ret */ + /* nest: */ + 0xcc, /* int3 */ + 0x90, /* nop */ + 0xc3, /* ret */ +}; + +static void test_nested_exception(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), PAGE_EXECUTE_READ); + ok(got_nested_exception, "Did not get nested exception.\n"); + ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n"); +} + +static unsigned int collided_unwind_exception_count; + +static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + CONTEXT ctx; + + trace("collided_exception_handler Rip %p, Rsp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n", + (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame); + + switch(collided_unwind_exception_count++) + { + case 0: + /* Initial exception from nested_except_code. */ + ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode); + nested_exception_initial_frame = frame; + /* Start unwind. */ + pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 1: + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)context->Rip == (char *)code_mem + 7, "got %p.\n", rec->ExceptionAddress); + /* generate exception in unwind handler. */ + RaiseException(0xdeadbeef, 0, 0, 0); + ok(0, "shouldn't be reached\n"); + break; + case 2: + /* Inner call frame, continue search. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 3: + /* Top level call frame, handle exception by unwinding. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 4: + /* Collided unwind. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_COLLIDED_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 5: + /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_TARGET_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, + (char *)nested_exception_initial_frame + 8); + break; + } + return ExceptionContinueSearch; +} + +static void test_collided_unwind(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + collided_unwind_exception_count = 0; + run_exception_test_flags(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER); + ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count); +} + +static CONTEXT test_unwind_apc_context; +static BOOL test_unwind_apc_called; + +static void CALLBACK test_unwind_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3) +{ + EXCEPTION_RECORD rec; + + test_unwind_apc_called = TRUE; + memset(&rec, 0, sizeof(rec)); + pRtlUnwind((void *)test_unwind_apc_context.Rsp, (void *)test_unwind_apc_context.Rip, &rec, (void *)0xdeadbeef); + ok(0, "Should not get here.\n"); +} + +static void test_unwind_from_apc(void) +{ + NTSTATUS status; + LONG pass; + + if (!pNtQueueApcThread) + { + win_skip("NtQueueApcThread is not available.\n"); + return; + } + + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&test_unwind_apc_context); + InterlockedIncrement(&pass); + + if (pass == 2) + { + test_unwind_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#lx.\n", status); + SleepEx(0, TRUE); + ok(0, "Should not get here.\n"); + } + if (pass == 3) + { + ok(test_unwind_apc_called, "Test user APC was not called.\n"); + test_unwind_apc_called = FALSE; + status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0); + ok(!status, "Got unexpected status %#lx.\n", status); + NtContinue(&test_unwind_apc_context, TRUE ); + ok(0, "Should not get here.\n"); + } + ok(pass == 4, "Got unexpected pass %ld.\n", pass); + ok(test_unwind_apc_called, "Test user APC was not called.\n"); +} + +static void test_syscall_clobbered_regs(void) +{ + struct regs + { + UINT64 rcx; + UINT64 r10; + UINT64 r11; + UINT32 eflags; + }; + static const BYTE code[] = + { + 0x48, 0x8d, 0x05, 0x00, 0x10, 0x00, 0x00, + /* leaq 0x1000(%rip),%rax */ + 0x48, 0x25, 0x00, 0xf0, 0xff, 0xff, + /* andq $~0xfff,%rax */ + 0x48, 0x83, 0xe8, 0x08, /* subq $8,%rax */ + 0x48, 0x89, 0x20, /* movq %rsp,0(%rax) */ + 0x48, 0x89, 0xc4, /* movq %rax,%rsp */ + 0xfd, /* std */ + 0x45, 0x31, 0xdb, /* xorl %r11d,%r11d */ + 0x41, 0x50, /* push %r8 */ + 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, + /* push %rbx, %rbp, %rdi, %rsi, %r12, %r13, %r14, %r15 */ + 0x49, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, + /* movabs $0xdeadbeef,%r10 */ + 0x41, 0xff, 0xd1, /* callq *r9 */ + 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5e, 0x5f, 0x5d, 0x5b, + /* pop %r15, %r14, %r13, %r12, %rsi, %rdi, %rbp, %rbx */ + 0x41, 0x58, /* pop %r8 */ + 0x49, 0x89, 0x48, 0x00, /* mov %rcx,(%r8) */ + 0x4d, 0x89, 0x50, 0x08, /* mov %r10,0x8(%r8) */ + 0x4d, 0x89, 0x58, 0x10, /* mov %r11,0x10(%r8) */ + 0x9c, /* pushfq */ + 0x59, /* pop %rcx */ + 0xfc, /* cld */ + 0x41, 0x89, 0x48, 0x18, /* mov %ecx,0x18(%r8) */ + 0x5c, /* pop %rsp */ + 0xc3, /* ret */ + }; + + NTSTATUS (WINAPI *func)(void *arg1, void *arg2, struct regs *, void *call_addr); + NTSTATUS (WINAPI *pNtCancelTimer)(HANDLE, BOOLEAN *); + struct regs regs; + CONTEXT context; + NTSTATUS status; + + if (is_arm64ec) return; /* arm64ec register handling is different */ + + pNtCancelTimer = (void *)GetProcAddress(hntdll, "NtCancelTimer"); + ok(!!pNtCancelTimer, "NtCancelTimer not found.\n"); + memcpy(code_mem, code, sizeof(code)); + func = code_mem; + memset(®s, 0, sizeof(regs)); + status = func((HANDLE)0xdeadbeef, NULL, ®s, pNtCancelTimer); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); + + /* After the syscall instruction rcx contains the address of the instruction next after syscall. */ + ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, + "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer); + + status = func((HANDLE)0xdeadbeef, (BOOLEAN *)0xdeadbeef, ®s, pNtCancelTimer); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); + ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, + "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer); + ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); + + context.ContextFlags = CONTEXT_CONTROL; + status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, + "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); + ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); + + status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, + "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); + ok((regs.r11 | 0x2) == regs.eflags, "Expected r11 (%#I64x) | 0x2 to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); + + context.ContextFlags = CONTEXT_INTEGER; + status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, + "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); + ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); + + status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok((BYTE *)regs.rcx > (BYTE *)pNtSetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtSetContextThread + 0x20, + "Got unexpected rcx %s, pNtSetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtSetContextThread); + ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); +} + +static CONTEXT test_raiseexception_regs_context; +static LONG CALLBACK test_raiseexception_regs_handle(EXCEPTION_POINTERS *exception_info) +{ + EXCEPTION_RECORD *rec = exception_info->ExceptionRecord; + unsigned int i; + + test_raiseexception_regs_context = *exception_info->ContextRecord; + ok(rec->NumberParameters == EXCEPTION_MAXIMUM_PARAMETERS, "got %lu.\n", rec->NumberParameters); + ok(rec->ExceptionCode == 0xdeadbeaf, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionRecord, "got %p.\n", rec->ExceptionRecord); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + for (i = 0; i < rec->NumberParameters; ++i) + ok(rec->ExceptionInformation[i] == i, "got %Iu, i %u.\n", rec->ExceptionInformation[i], i); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void test_raiseexception_regs(void) +{ + static const BYTE code[] = + { + 0xb8, 0x00, 0xb0, 0xad, 0xde, /* mov $0xdeadb000,%eax */ + 0x53, /* push %rbx */ + 0x48, 0x89, 0xc3, /* mov %rax,%rbx */ + 0x56, /* push %rsi */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x48, 0x89, 0xc6, /* mov %rax,%rsi */ + 0x57, /* push %rdi */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x48, 0x89, 0xc7, /* mov %rax,%rdi */ + 0x55, /* push %rbp */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x48, 0x89, 0xc5, /* mov %rax,%rbp */ + 0x41, 0x54, /* push %r12 */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x49, 0x89, 0xc4, /* mov %rax,%r12 */ + 0x41, 0x55, /* push %r13 */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x49, 0x89, 0xc5, /* mov %rax,%r13 */ + 0x41, 0x56, /* push %r14 */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x49, 0x89, 0xc6, /* mov %rax,%r14 */ + 0x41, 0x57, /* push %r15 */ + 0x48, 0xff, 0xc0, /* inc %rax */ + 0x49, 0x89, 0xc7, /* mov %rax,%r15 */ + + 0x50, /* push %rax */ /* align stack */ + 0x48, 0x89, 0xc8, /* mov %rcx,%rax */ + 0xb9, 0xaf, 0xbe, 0xad, 0xde, /* mov $0xdeadbeaf,%ecx */ + 0xff, 0xd0, /* call *%rax */ + 0x58, /* pop %rax */ + + 0x41, 0x5f, /* pop %r15 */ + 0x41, 0x5e, /* pop %r14 */ + 0x41, 0x5d, /* pop %r13 */ + 0x41, 0x5c, /* pop %r12 */ + 0x5d, /* pop %rbp */ + 0x5f, /* pop %rdi */ + 0x5e, /* pop %rsi */ + 0x5b, /* pop %rbx */ + 0xc3, /* ret */ + }; + void (WINAPI *pRaiseException)( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args ) = RaiseException; + void (WINAPI *func)(void *raise_exception, DWORD flags, DWORD count, const ULONG_PTR *args); + void *vectored_handler; + ULONG_PTR args[20]; + ULONG64 expected; + unsigned int i; + + vectored_handler = AddVectoredExceptionHandler(TRUE, test_raiseexception_regs_handle); + ok(!!vectored_handler, "failed.\n"); + + memcpy(code_mem, code, sizeof(code)); + func = code_mem; + + for (i = 0; i < ARRAY_SIZE(args); ++i) + args[i] = i; + + func(pRaiseException, 0, ARRAY_SIZE(args), args); + expected = 0xdeadb000; + ok(test_raiseexception_regs_context.Rbx == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rbx); + ++expected; + ok(test_raiseexception_regs_context.Rsi == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rsi); + ++expected; + ok(test_raiseexception_regs_context.Rdi == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rdi); + ++expected; + ok(test_raiseexception_regs_context.Rbp == expected || is_arm64ec /* x29 modified by entry thunk */, + "got %#I64x.\n", test_raiseexception_regs_context.Rbp); + ++expected; + ok(test_raiseexception_regs_context.R12 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R12); + ++expected; + ok(test_raiseexception_regs_context.R13 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R13); + ++expected; + ok(test_raiseexception_regs_context.R14 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R14); + ++expected; + ok(test_raiseexception_regs_context.R15 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R15); + + RemoveVectoredExceptionHandler(vectored_handler); +} + +static LONG CALLBACK test_instrumentation_callback_handler( EXCEPTION_POINTERS *exception_info ) +{ + EXCEPTION_RECORD *rec = exception_info->ExceptionRecord; + CONTEXT *c = exception_info->ContextRecord; + + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ++c->Rip; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static HANDLE instrumentation_callback_thread_ready, instrumentation_callback_thread_wait; + +static DWORD WINAPI test_instrumentation_callback_thread( void *arg ) +{ + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + return 0; +} + +struct instrumentation_callback_data +{ + unsigned int call_count; + struct + { + char *r10; + char *rcx; + } + call_data[256]; +}; + +static void init_instrumentation_data(struct instrumentation_callback_data *d) +{ + memset( d, 0xcc, sizeof(*d) ); + d->call_count = 0; +} + +static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0x50, 0x52, /* push %rax, %rdx */ + + 0x48, 0xba, /* movabs instrumentation_call_count, %rdx */ + /* &instrumentation_call_count, offset 4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%eax */ + 0xf0, 0x0f, 0xc1, 0x02, /* lock xadd %eax,(%rdx) */ + 0x0f, 0xb6, 0xc0, /* movzx %al,%eax */ + 0x48, 0xba, /* movabs instrumentation_call_data, %rdx */ + /* instrumentation_call_data, offset 26 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x01, 0xc0, /* add %rax,%rax */ + 0x48, 0x8d, 0x14, 0xc2, /* lea (%rdx,%rax,8),%rdx */ + 0x4c, 0x89, 0x12, /* mov %r10,(%rdx) */ + 0x48, 0x89, 0x4a, 0x08, /* mov %rcx,0x8(%rdx) */ + + 0x5a, 0x58, /* pop %rdx, %rax */ + 0x41, 0xff, 0xe2, /* jmp *r10 */ + }; + + struct instrumentation_callback_data curr_data, data; + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + void *pLdrInitializeThunk; + EXCEPTION_RECORD record; + void *vectored_handler; + unsigned int i, count; + NTSTATUS status; + HANDLE thread; + CONTEXT ctx; + HWND hwnd; + LONG pass; + + if (is_arm64ec) return; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(void **)((char *)code_mem + 4) = &curr_data.call_count; + *(void **)((char *)code_mem + 26) = curr_data.call_data; + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + /* If instrumentation callback is not yet set during syscall entry it won't be called on exit. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + + init_instrumentation_data( &curr_data ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info), NULL ); + data = curr_data; + ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess + && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20, + "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess ); + ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + + vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler ); + ok( !!vectored_handler, "failed.\n" ); + init_instrumentation_data( &curr_data ); + DbgBreakPoint(); + data = curr_data; + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + + pass = 0; + InterlockedIncrement( &pass ); + pRtlCaptureContext( &ctx ); + if (InterlockedIncrement( &pass ) == 2) /* interlocked to prevent compiler from moving before capture */ + { + record.ExceptionCode = 0xceadbeef; + record.NumberParameters = 0; + init_instrumentation_data( &curr_data ); + status = pNtRaiseException( &record, &ctx, TRUE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 3) + { + data = curr_data; + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + init_instrumentation_data( &curr_data ); + NtContinue( &ctx, FALSE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 4) + { + data = curr_data; + /* Not called for NtContinue. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + init_instrumentation_data( &curr_data ); + NtSetContextThread( GetCurrentThread(), &ctx ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 5) + { + data = curr_data; + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); + init_instrumentation_data( &curr_data ); + } + ok( pass == 5, "got %ld.\n", pass ); + RemoveVectoredExceptionHandler( vectored_handler ); + + apc_count = 0; + status = pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); + init_instrumentation_data( &curr_data ); + SleepEx( 0, TRUE ); + data = curr_data; + ok( apc_count == 1, "APC was not called.\n" ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); + + instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL ); + instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + init_instrumentation_data( &curr_data ); + thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pLdrInitializeThunk) break; + } + ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); + + init_instrumentation_data( &curr_data ); + SetEvent( instrumentation_callback_thread_wait ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + count = 0; + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20) + ++count; + } + ok( count == 2, "got %u.\n", count ); + + SetEvent( instrumentation_callback_thread_wait ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( instrumentation_callback_thread_ready ); + CloseHandle( instrumentation_callback_thread_wait ); + + hwnd = CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + init_instrumentation_data( &curr_data ); + DestroyWindow( hwnd ); + data = curr_data; + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pKiUserCallbackDispatcher) + break; + } + ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); + + init_instrumentation_data( &curr_data ); + memset(&info, 0, sizeof(info)); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( !status, "got %#lx.\n", status ); + ok( !data.call_count, "got %u.\n", data.call_count ); +} + +static UINT32 find_syscall_nr(const char *function) +{ + UINT32 syscall_nr; + + char *code = (char *)GetProcAddress(hntdll, function); + + /* This assumes that Nt* syscall thunks are all formatted as: + * + * 4c 8b d1 movq %rcx, %r10 + * b8 ?? ?? ?? ?? movl $(syscall number), %eax + */ + memcpy(&syscall_nr, code + 4, sizeof(UINT32)); + return syscall_nr; +} + +static void test_direct_syscalls(void) +{ + static const BYTE code[] = + { + 0x49, 0x89, 0xd2, /* movq %rdx, %r10 */ + 0x4c, 0x89, 0xc2, /* movq %r8, %rdx */ + 0x89, 0xc8, /* movl %ecx, %eax */ + 0x0f, 0x05, /* syscall */ + 0xc3, /* ret */ + }; + + HANDLE event; + NTSTATUS (WINAPI *func)(UINT32 syscall_nr, HANDLE h, LONG *prev_state); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + memcpy(code_mem, code, sizeof(code)); + func = code_mem; + func(find_syscall_nr("NtSetEvent"), event, NULL); + + todo_wine + ok(WaitForSingleObject(event, 0) == WAIT_OBJECT_0, "Event not signaled.\n"); + CloseHandle(event); +} + +#elif defined(__arm__) + +static void test_thread_context(void) +{ + CONTEXT context; + NTSTATUS status; + struct expected + { + DWORD R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, Sp, Lr, Pc, Cpsr; + } expect; + NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ); + + static const WORD call_func[] = + { + 0xb502, /* push {r1, lr} */ + 0xe882, 0x1fff, /* stmia.w r2, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip} */ + 0xf8c2, 0xd034, /* str.w sp, [r2, #52] */ + 0xf8c2, 0xe038, /* str.w lr, [r2, #56] */ + 0xf3ef, 0x8100, /* mrs r1, CPSR */ + 0xf041, 0x0120, /* orr.w r1, r1, #32 */ + 0x6411, /* str r1, [r2, #64] */ + 0x9900, /* ldr r1, [sp, #0] */ + 0x4679, /* mov r1, pc */ + 0xf101, 0x0109, /* add.w r1, r1, #9 */ + 0x63d1, /* str r1, [r2, #60] */ + 0x9900, /* ldr r1, [sp, #0] */ + 0x4798, /* blx r3 */ + 0xbd02, /* pop {r1, pc} */ + }; + + memcpy( code_mem, call_func, sizeof(call_func) ); + func_ptr = (void *)((char *)code_mem + 1); /* thumb */ + +#define COMPARE(reg) \ + ok( context.reg == expect.reg, "wrong " #reg " %08lx/%08lx\n", context.reg, expect.reg ) + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + func_ptr( &context, 0, &expect, pRtlCaptureContext ); + trace( "expect: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx " + "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n", + expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7, + expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr ); + trace( "actual: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx " + "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n", + context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7, + context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr ); + + ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT), + "wrong flags %08lx\n", context.ContextFlags ); + ok( !context.R0, "wrong R0 %08lx\n", context.R0 ); + COMPARE( R1 ); + COMPARE( R2 ); + COMPARE( R3 ); + COMPARE( R4 ); + COMPARE( R5 ); + COMPARE( R6 ); + COMPARE( R7 ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + COMPARE( R12 ); + COMPARE( Sp ); + COMPARE( Pc ); + COMPARE( Cpsr ); + ok( !context.Lr, "wrong Lr %08lx\n", context.Lr ); + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + context.ContextFlags = CONTEXT_FULL; + + status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread ); + ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status ); + trace( "expect: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx " + "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n", + expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7, + expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr ); + trace( "actual: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx " + "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n", + context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7, + context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr ); + /* other registers are not preserved */ + COMPARE( R4 ); + COMPARE( R5 ); + COMPARE( R6 ); + COMPARE( R7 ); + COMPARE( R8 ); + COMPARE( R9 ); + COMPARE( R10 ); + COMPARE( R11 ); + ok( (context.Cpsr & 0xff0f0030) == (expect.Cpsr & 0xff0f0030), + "wrong Cpsr %08lx/%08lx\n", context.Cpsr, expect.Cpsr ); + ok( context.Sp == expect.Sp - 16, + "wrong Sp %08lx/%08lx\n", context.Sp, expect.Sp - 16 ); + /* Pc is somewhere close to the NtGetContextThread implementation */ + ok( (char *)context.Pc >= (char *)pNtGetContextThread && + (char *)context.Pc <= (char *)pNtGetContextThread + 0x10, + "wrong Pc %08lx/%08lx\n", context.Pc, (DWORD)pNtGetContextThread ); +#undef COMPARE +} + +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) +{ + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + DEBUG_EVENT de; + DWORD continuestatus; + PVOID code_mem_address = NULL; + NTSTATUS status; + SIZE_T size_read; + BOOL ret; + int counter = 0; + si.cb = sizeof(si); + + if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess) + { + skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n"); + return; + } + + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "could not create child process error: %lu\n", GetLastError()); + if (!ret) + return; + + do + { + continuestatus = cont_status; + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); + + ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); + ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError()); + + if (de.dwThreadId != pi.dwThreadId) + { + trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode); + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status); + continue; + } + + if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) + { + if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress) + { + skip("child process loaded at different address, terminating it\n"); + pNtTerminateProcess(pi.hProcess, 0); + } + } + else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) + { + CONTEXT ctx; + enum debugger_stages stage; + + counter++; + status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, + sizeof(code_mem_address), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + ctx.ContextFlags = CONTEXT_FULL; + status = pNtGetContextThread(pi.hThread, &ctx); + ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + + trace("exception 0x%lx at %p firstchance=%ld pc=%08lx, r0=%08lx\n", + de.u.Exception.ExceptionRecord.ExceptionCode, + de.u.Exception.ExceptionRecord.ExceptionAddress, + de.u.Exception.dwFirstChance, ctx.Pc, ctx.R0); + + if (counter > 100) + { + ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n"); + pNtTerminateProcess(pi.hProcess, 1); + } + else if (counter < 2) /* startup breakpoint */ + { + /* breakpoint is inside ntdll */ + void *ntdll = GetModuleHandleA( "ntdll.dll" ); + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll ); + + ok( (char *)ctx.Pc >= (char *)ntdll && + (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage, + "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll, + (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + } + else + { + if (stage == STAGE_RTLRAISE_NOT_HANDLED) + { + ok((char *)ctx.Pc == (char *)code_mem_address + 7, "Pc at %lx instead of %p\n", + ctx.Pc, (char *)code_mem_address + 7); + /* setting the context from debugger does not affect the context that the + * exception handler gets, except on w2008 */ + ctx.Pc = (UINT_PTR)code_mem_address + 9; + ctx.R0 = 0xf00f00f1; + /* let the debuggee handle the exception */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) + { + if (de.u.Exception.dwFirstChance) + { + /* debugger gets first chance exception with unmodified ctx.Pc */ + ok((char *)ctx.Pc == (char *)code_mem_address + 7, "Pc at 0x%lx instead of %p\n", + ctx.Pc, (char *)code_mem_address + 7); + ctx.Pc = (UINT_PTR)code_mem_address + 9; + ctx.R0 = 0xf00f00f1; + /* pass exception to debuggee + * exception will not be handled and a second chance exception will be raised */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + { + /* debugger gets context after exception handler has played with it */ + /* ctx.Pc is the same value the exception handler got */ + if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + ok((char *)ctx.Pc == (char *)code_mem_address + 7, + "Pc at 0x%lx instead of %p\n", ctx.Pc, (char *)code_mem_address + 7); + /* need to fixup Pc for debuggee */ + ctx.Pc += 2; + } + else ok((char *)ctx.Pc == (char *)code_mem_address + 7, + "Pc at 0x%lx instead of %p\n", ctx.Pc, (char *)code_mem_address + 7); + /* here we handle exception */ + } + } + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d, + "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Pc); + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Pc == (char *)code_mem_address + 3, + "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 3, ctx.Pc); + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, + "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, + EXCEPTION_INVALID_HANDLE); + ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, + "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); + + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + ok(FALSE, "unexpected stage %x\n", stage); + + status = pNtSetContextThread(pi.hThread, &ctx); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); + } + } + else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) + { + enum debugger_stages stage; + char buffer[64 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); + + memset(buffer, 0, sizeof(buffer)); + status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, + de.u.DebugString.nDebugStringLength * char_size, &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } + else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ + ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (de.dwDebugEventCode == RIP_EVENT) + { + enum debugger_stages stage; + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) + { + ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", + de.u.RipInfo.dwError, 0x11223344); + ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n", + de.u.RipInfo.dwType, 0x55667788); + } + else + ok(FALSE, "unexpected stage %x\n", stage); + + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); + + } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + wait_child_process( pi.hProcess ); + ret = CloseHandle(pi.hThread); + ok(ret, "error %lu\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "error %lu\n", GetLastError()); +} + +static void test_debug_service(DWORD numexc) +{ + /* not supported */ +} + + +static BOOL hook_called; +static BOOL got_exception; +static void *code_ptr; + +static WORD patched_code[] = +{ + 0x4668, /* mov r0, sp */ + 0xf8df, 0xc004, /* ldr.w ip, [pc, #0x4] */ + 0x4760, /* bx ip */ + 0, 0, /* 1: hook_trampoline */ +}; +static WORD saved_code[ARRAY_SIZE(patched_code)]; + +static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *ptrs) +{ + EXCEPTION_RECORD *rec = ptrs->ExceptionRecord; + CONTEXT *context = ptrs->ContextRecord; + + trace("dbg_except_continue_vectored_handler, code %#lx, pc %#lx.\n", rec->ExceptionCode, context->Pc); + got_exception = TRUE; + + ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void * WINAPI hook_KiUserExceptionDispatcher(void *stack) +{ + CONTEXT *context = stack; + EXCEPTION_RECORD *rec = (EXCEPTION_RECORD *)(context + 1); + + trace( "rec %p context %p pc %#lx sp %#lx flags %#lx\n", + rec, context, context->Pc, context->Sp, context->ContextFlags ); + + ok( !((ULONG_PTR)stack & 7), "unaligned stack %p\n", stack ); + ok( rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode ); + + hook_called = TRUE; + memcpy(code_ptr, saved_code, sizeof(saved_code)); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(saved_code)); + return pKiUserExceptionDispatcher; +} + +static void test_KiUserExceptionDispatcher(void) +{ + WORD hook_trampoline[] = + { + 0x4668, /* mov r0, sp */ + 0xf8df, 0xc006, /* ldr.w r12, [pc, #0x6] */ + 0x47e0, /* blx r12 */ + 0x4700, /* bx r0 */ + 0, 0, /* 1: hook_KiUserExceptionDispatcher */ + }; + + EXCEPTION_RECORD record = { EXCEPTION_BREAKPOINT }; + void *trampoline_ptr, *vectored_handler; + DWORD old_protect; + BOOL ret; + + code_ptr = (void *)(((ULONG_PTR)pKiUserExceptionDispatcher) & ~1); /* mask thumb bit */ + *(void **)&hook_trampoline[5] = hook_KiUserExceptionDispatcher; + trampoline_ptr = (char *)code_mem + 1024; + memcpy( trampoline_ptr, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( code_ptr, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, code_ptr, sizeof(saved_code) ); + *(void **)&patched_code[4] = (char *)trampoline_ptr + 1; /* thumb */ + + vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler); + + memcpy( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code)); + + got_exception = FALSE; + hook_called = FALSE; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(!hook_called, "Hook was called.\n"); + + memcpy( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code)); + + got_exception = 0; + hook_called = FALSE; + NtCurrentTeb()->Peb->BeingDebugged = 1; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called, "Hook was not called.\n"); + NtCurrentTeb()->Peb->BeingDebugged = 0; + + RemoveVectoredExceptionHandler(vectored_handler); + VirtualProtect(code_ptr, sizeof(saved_code), old_protect, &old_protect); +} + +static UINT alertable_supported; + +static void * WINAPI hook_KiUserApcDispatcher(void *stack) +{ + struct + { + void *func; + ULONG args[3]; + ULONG alertable; + ULONG align; + CONTEXT context; + } *args = stack; + CONTEXT *context = &args->context; + + if (args->alertable == 1) alertable_supported = TRUE; + else context = (CONTEXT *)&args->alertable; + + trace( "stack=%p func=%p args=%lx,%lx,%lx alertable=%lx context=%p pc=%lx sp=%lx (%lx)\n", + args, args->func, args->args[0], args->args[1], args->args[2], + args->alertable, context, context->Pc, context->Sp, + context->Sp - (ULONG_PTR)stack ); + + ok( args->func == apc_func, "wrong func %p / %p\n", args->func, apc_func ); + ok( args->args[0] == 0x1234 + apc_count, "wrong arg1 %lx\n", args->args[0] ); + ok( args->args[1] == 0x5678, "wrong arg2 %lx\n", args->args[1] ); + ok( args->args[2] == 0xdeadbeef, "wrong arg3 %lx\n", args->args[2] ); + + if (apc_count && alertable_supported) args->alertable = FALSE; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef ); + + hook_called = TRUE; + memcpy( code_ptr, saved_code, sizeof(saved_code)); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(saved_code)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + WORD hook_trampoline[] = + { + 0x4668, /* mov r0, sp */ + 0xf8df, 0xc006, /* ldr.w r12, [pc, #0x6] */ + 0x47e0, /* blx r12 */ + 0x4700, /* bx r0 */ + 0, 0, /* 1: hook_KiUserApcDispatcher */ + }; + DWORD old_protect; + BOOL ret; + + code_ptr = (void *)(((ULONG_PTR)pKiUserApcDispatcher) & ~1); /* mask thumb bit */ + *(void **)&hook_trampoline[5] = hook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( code_ptr, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, code_ptr, sizeof(saved_code) ); + *(void **)&patched_code[4] = (char *)code_mem + 1; /* thumb */ + memcpy( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code)); + + hook_called = FALSE; + apc_count = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 2, "APC count %u\n", apc_count ); + ok( hook_called, "hook was not called\n" ); + + memcpy( code_ptr, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code)); + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + if (alertable_supported) + { + ok( apc_count == 3, "APC count %u\n", apc_count ); + SleepEx( 0, TRUE ); + } + ok( apc_count == 4, "APC count %u\n", apc_count ); + + VirtualProtect( code_ptr, sizeof(saved_code), old_protect, &old_protect ); +} + +static void WINAPI hook_KiUserCallbackDispatcher(void *sp) +{ + struct + { + void *args; + ULONG len; + ULONG id; + ULONG lr; + ULONG sp; + ULONG pc; + BYTE args_data[0]; + } *stack = sp; + ULONG_PTR redzone = (BYTE *)stack->sp - &stack->args_data[stack->len]; + KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id]; + + trace( "stack=%p len=%lx id=%lx lr=%lx sp=%lx pc=%lx\n", + stack, stack->len, stack->id, stack->lr, stack->sp, stack->pc ); + NtCallbackReturn( NULL, 0, 0 ); + + ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data ); + ok( redzone >= 8 && redzone <= 16, "wrong sp %p / %p (%Iu)\n", + (void *)stack->sp, stack->args_data, redzone ); + + if (pRtlPcToFileHeader) + { + void *mod, *win32u = GetModuleHandleA("win32u.dll"); + + pRtlPcToFileHeader( (void *)stack->pc, &mod ); + ok( mod == win32u, "pc %lx not in win32u %p\n", stack->pc, win32u ); + } + NtCallbackReturn( NULL, 0, func( stack->args, stack->len )); +} + +static void test_KiUserCallbackDispatcher(void) +{ + DWORD old_protect; + BOOL ret; + + code_ptr = (void *)(((ULONG_PTR)pKiUserCallbackDispatcher) & ~1); /* mask thumb bit */ + ret = VirtualProtect( code_ptr, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, code_ptr, sizeof(saved_code)); + *(void **)&patched_code[4] = hook_KiUserCallbackDispatcher; + memcpy( code_ptr, patched_code, sizeof(patched_code)); + FlushInstructionCache(GetCurrentProcess(), code_ptr, sizeof(patched_code)); + + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + + memcpy( code_ptr, saved_code, sizeof(saved_code)); + FlushInstructionCache(GetCurrentProcess(), code_ptr, sizeof(saved_code)); + VirtualProtect( code_ptr, sizeof(saved_code), old_protect, &old_protect ); +} + +struct unwind_info +{ + DWORD function_length : 18; + DWORD version : 2; + DWORD x : 1; + DWORD e : 1; + DWORD f : 1; + DWORD epilog : 5; + DWORD codes : 4; +}; + +static void run_exception_test(void *handler, const void* context, + const void *code, unsigned int code_size, + unsigned int func2_offset, DWORD access, DWORD handler_flags, + void *arg1, void *arg2) +{ + DWORD buf[11]; + RUNTIME_FUNCTION runtime_func[2]; + struct unwind_info unwind; + void (*func)(void*, void*) = (void *)((char *)code_mem + 1); /* thumb */ + DWORD oldaccess, oldaccess2; + + runtime_func[0].BeginAddress = 0; + runtime_func[0].UnwindData = 0x1000; + runtime_func[1].BeginAddress = func2_offset; + runtime_func[1].UnwindData = 0x1010; + + unwind.function_length = func2_offset / 2; + unwind.version = 0; + unwind.x = 1; + unwind.e = 1; + unwind.f = 0; + unwind.epilog = 1; + unwind.codes = 1; + buf[0] = *(DWORD *)&unwind; + buf[1] = 0xfbfbffd4; /* push {r4, lr}; end; nop; nop */ + buf[2] = 0x1021; + *(const void **)&buf[3] = context; + unwind.function_length = (code_size - func2_offset) / 2; + buf[4] = *(DWORD *)&unwind; + buf[5] = 0xfbfbffd4; /* push {r4, lr}; end; nop; nop */ + buf[6] = 0x1021; + *(const void **)&buf[7] = context; + buf[8] = 0xc004f8df; /* ldr ip, 1f */ + buf[9] = 0xbf004760; /* bx ip; nop */ + *(const void **)&buf[10] = handler; + + memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf)); + memcpy(code_mem, code, code_size); + if (access) VirtualProtect(code_mem, code_size, access, &oldaccess); + FlushInstructionCache( GetCurrentProcess(), code_mem, 0x2000 ); + + pRtlAddFunctionTable(runtime_func, ARRAY_SIZE(runtime_func), (ULONG_PTR)code_mem); + func( arg1, arg2 ); + pRtlDeleteFunctionTable(runtime_func); + + if (access) VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2); +} + +static BOOL got_nested_exception, got_prev_frame_exception; +static void *nested_exception_initial_frame; + +static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + trace("nested_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n", + (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress); + + if (rec->ExceptionCode == 0x80000003 && !(rec->ExceptionFlags & EXCEPTION_NESTED_CALL)) + { + ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok((char *)context->Sp == (char *)frame - 8, "Got unexpected frame %p / %p.\n", frame, (void *)context->Sp); + ok((char *)context->Lr == (char *)code_mem + 0x07, "Got unexpected lr %p.\n", (void *)context->Lr); + ok((char *)context->Pc == (char *)code_mem + 0x0b, "Got unexpected pc %p.\n", (void *)context->Pc); + + nested_exception_initial_frame = frame; + RaiseException(0xdeadbeef, 0, 0, 0); + context->Pc += 2; + return ExceptionContinueExecution; + } + + if (rec->ExceptionCode == 0xdeadbeef && + (rec->ExceptionFlags == EXCEPTION_NESTED_CALL || + rec->ExceptionFlags == (EXCEPTION_NESTED_CALL | EXCEPTION_SOFTWARE_ORIGINATE))) + { + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + got_nested_exception = TRUE; + ok(frame == nested_exception_initial_frame, "Got unexpected frame %p / %p.\n", + frame, nested_exception_initial_frame); + return ExceptionContinueSearch; + } + + ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE), + "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags); + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "Got unexpected frame %p / %p.\n", + frame, nested_exception_initial_frame); + got_prev_frame_exception = TRUE; + return ExceptionContinueExecution; +} + +static const WORD nested_except_code[] = +{ + 0xb510, /* 00: push {r4, lr} */ + 0xf000, 0xf801, /* 02: bl 1f */ + 0xbd10, /* 06: pop {r4, pc} */ + 0xb510, /* 08: 1: push {r4, lr} */ + 0xdefe, /* 0a: trap */ + 0xbf00, /* 0c: nop */ + 0xbd10, /* 0e: pop {r4, pc} */ +}; + +static void test_nested_exception(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), + 4 * sizeof(WORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, 0, 0); + ok(got_nested_exception, "Did not get nested exception.\n"); + ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n"); +} + +static unsigned int collided_unwind_exception_count; + +static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + CONTEXT ctx; + + trace("collided_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n", + (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame); + + switch(collided_unwind_exception_count++) + { + case 0: + /* Initial exception from nested_except_code. */ + ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode); + nested_exception_initial_frame = frame; + /* Start unwind. */ + pRtlUnwindEx((char *)frame + 8, (char *)code_mem + 0x07, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 1: + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)context->Pc == (char *)code_mem + 0x0b, "got %p.\n", (void *)context->Pc); + /* generate exception in unwind handler. */ + RaiseException(0xdeadbeef, 0, 0, 0); + ok(0, "shouldn't be reached\n"); + break; + case 2: + /* Inner call frame, continue search. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 3: + /* Top level call frame, handle exception by unwinding. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 0x07, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 4: + /* Collided unwind. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_COLLIDED_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 5: + /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_TARGET_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + } + return ExceptionContinueSearch; +} + +static void test_collided_unwind(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + collided_unwind_exception_count = 0; + run_exception_test(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), + 4 * sizeof(WORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER, 0, 0); + ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count); +} + + +static int rtlraiseexception_unhandled_handler_called; +static int rtlraiseexception_teb_handler_called; +static int rtlraiseexception_handler_called; + +static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, + void *dispatcher, BOOL unhandled_handler ) +{ + void *addr = rec->ExceptionAddress; + + trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc ); + + ok( addr == (char *)code_mem + 7, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 7 ); + ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_ARM_FLOATING_POINT | CONTEXT_UNWOUND_TO_CALL) || + context->ContextFlags == CONTEXT_ALL, + "wrong context flags %lx\n", context->ContextFlags ); + ok( context->Pc == (UINT_PTR)addr, + "%d: Pc at %lx instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr ); + + ok( context->R0 == 0xf00f00f0, "context->X0 is %lx, should have been set to 0xf00f00f0 in vectored handler\n", context->R0 ); +} + +static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc ); + rtlraiseexception_unhandled_handler_called = 1; + rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE); + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return EXCEPTION_CONTINUE_SEARCH; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec, + EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, + EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + rtlraiseexception_teb_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + ULONG *nonvol_regs = (void *)dispatcher->NonVolatileRegisters; + int i; + + for (i = 0; i < 8; i++) + ok( nonvol_regs[i] == ((ULONG *)&context->R4)[i], + "wrong non volatile reg r%u %lx / %lx\n", i + 4, + nonvol_regs[i], ((ULONG *)&context->R4)[i] ); + for (i = 0; i < 8; i++) + ok( ((ULONGLONG *)(nonvol_regs + 8))[i] == context->D[i + 8], + "wrong non volatile reg d%u %I64x / %I64x\n", i + 8, + ((ULONGLONG *)(nonvol_regs + 8))[i], context->D[i + 8] ); + + rtlraiseexception_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + void *addr = rec->ExceptionAddress; + + trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc ); + ok( addr == (char *)code_mem + 7, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 7 ); + ok( context->Pc == (UINT_PTR)addr, + "%d: Pc at %lx instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr ); + + context->R0 = 0xf00f00f0; + return EXCEPTION_CONTINUE_SEARCH; +} + +static const DWORD call_one_arg_code[] = +{ + 0xb510, /* 00: push {r4, lr} */ + 0x4788, /* 02: blx r1 */ + 0xbf00, /* 04: nop */ + 0xbd10, /* 06: pop {r4, pc} */ +}; + +static void run_rtlraiseexception_test(DWORD exceptioncode) +{ + EXCEPTION_REGISTRATION_RECORD frame; + EXCEPTION_RECORD record; + PVOID vectored_handler = NULL; + + record.ExceptionCode = exceptioncode; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; /* does not matter, copied return address */ + record.NumberParameters = 0; + + frame.Handler = rtlraiseexception_teb_handler; + frame.Prev = NtCurrentTeb()->Tib.ExceptionList; + + NtCurrentTeb()->Tib.ExceptionList = &frame; + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler); + + rtlraiseexception_handler_called = 0; + rtlraiseexception_teb_handler_called = 0; + rtlraiseexception_unhandled_handler_called = 0; + + run_exception_test( rtlraiseexception_handler, NULL, call_one_arg_code, + sizeof(call_one_arg_code), sizeof(call_one_arg_code), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + &record, pRtlRaiseException); + + ok( record.ExceptionAddress == (char *)code_mem + 7, + "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 7 ); + + todo_wine + ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" ); + ok( rtlraiseexception_handler_called, "Frame handler called\n" ); + ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" ); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL); + NtCurrentTeb()->Tib.ExceptionList = frame.Prev; +} + +static void test_rtlraiseexception(void) +{ + run_rtlraiseexception_test(0x12345); + run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); + run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); +} + + +static LONG consolidate_dummy_called; +static LONG pass; + +static const WORD call_rtlunwind[] = +{ + 0xf8dd, 0xc00c, /* ldr r12, [sp, #0xc] */ + 0xe8ac, 0x0ff0, /* stm r12!, {r4-r11} */ + 0xec8c, 0x8b10, /* vstm r12, {d8-d15} */ + 0xf8dd, 0xc008, /* ldr r12, [sp, #0x8] */ + 0x4760, /* bx r12 */ +}; + +static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec) +{ + CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1]; + DWORD *saved_regs = (DWORD *)rec->ExceptionInformation[3]; + DWORD *regs = (DWORD *)rec->ExceptionInformation[10]; + int i; + + switch (InterlockedIncrement(&consolidate_dummy_called)) + { + case 1: /* RtlRestoreContext */ + ok(ctx->Pc == 0xdeadbeef, "RtlRestoreContext wrong Pc, expected: 0xdeadbeef, got: %lx\n", ctx->Pc); + ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + break; + case 2: /* RtlUnwindEx */ + ok(ctx->Pc != 0xdeadbeef, "RtlUnwindEx wrong Pc, got: %lx\n", ctx->Pc ); + ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + for (i = 0; i < 8; i++) + ok( saved_regs[i] == regs[i], "wrong reg R%u, expected: %lx, got: %lx\n", + i + 4, saved_regs[i], regs[i] ); + regs += 8; + saved_regs += 8; + for (i = 0; i < 8; i++) + ok( ((DWORD64 *)saved_regs)[i] == ((DWORD64 *)regs)[i], + "wrong reg D%u, expected: %I64x, got: %I64x\n", + i + 8, ((DWORD64 *)saved_regs)[i], ((DWORD64 *)regs)[i] ); + break; + } + return (PVOID)rec->ExceptionInformation[2]; +} + +static void test_restore_context(void) +{ + EXCEPTION_RECORD rec; + _JUMP_BUFFER buf; + CONTEXT ctx; + int i; + + if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext) + { + skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n"); + return; + } + + /* test simple case of capture and restore context */ + pass = 0; + InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */ + pRtlCaptureContext(&ctx); + if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */ + { + pRtlRestoreContext(&ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass < 4, "unexpected pass %ld\n", pass); + + /* test with jmp using RtlRestoreContext */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + setjmp((_JBTYPE *)&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD)&buf; + /* uses buf.Pc instead of ctx.Pc */ + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 4) + { + ok(buf.R4 == ctx.R4 , "longjmp failed for R4, expected: %lx, got: %lx\n", buf.R4, ctx.R4 ); + ok(buf.R5 == ctx.R5 , "longjmp failed for R5, expected: %lx, got: %lx\n", buf.R5, ctx.R5 ); + ok(buf.R6 == ctx.R6 , "longjmp failed for R6, expected: %lx, got: %lx\n", buf.R6, ctx.R6 ); + ok(buf.R7 == ctx.R7 , "longjmp failed for R7, expected: %lx, got: %lx\n", buf.R7, ctx.R7 ); + ok(buf.R8 == ctx.R8 , "longjmp failed for R8, expected: %lx, got: %lx\n", buf.R8, ctx.R8 ); + ok(buf.R9 == ctx.R9 , "longjmp failed for R9, expected: %lx, got: %lx\n", buf.R9, ctx.R9 ); + ok(buf.R10 == ctx.R10, "longjmp failed for R10, expected: %lx, got: %lx\n", buf.R10, ctx.R10 ); + ok(buf.R11 == ctx.R11, "longjmp failed for R11, expected: %lx, got: %lx\n", buf.R11, ctx.R11 ); + for (i = 0; i < 8; i++) + ok(buf.D[i] == ctx.D[i + 8], "longjmp failed for D%u, expected: %I64x, got: %I64x\n", + i + 8, buf.D[i], ctx.D[i + 8]); + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 5, "unexpected pass %ld\n", pass); + + /* test with jmp through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + setjmp((_JBTYPE *)&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD)&buf; + + /* uses buf.Pc instead of bogus 0xdeadbeef */ + pRtlUnwindEx((void*)buf.Sp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 4, "unexpected pass %ld\n", pass); + + + /* test with consolidate */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 3; + rec.ExceptionInformation[0] = (DWORD)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD)&ctx; + rec.ExceptionInformation[2] = ctx.Pc; + rec.ExceptionInformation[10] = -1; + ctx.Pc = 0xdeadbeef; + + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %ld\n", pass); + + /* test with consolidate through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + void (*func)(DWORD,DWORD,EXCEPTION_RECORD*,DWORD,CONTEXT*,void*,void*,void*); + DWORD64 nonvol_regs[12]; + + func = (void *)((ULONG_PTR)code_mem | 1); /* thumb */ + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 4; + rec.ExceptionInformation[0] = (DWORD)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD)&ctx; + rec.ExceptionInformation[2] = ctx.Pc; + rec.ExceptionInformation[3] = (DWORD)&nonvol_regs; + rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */ + ctx.Pc = 0xdeadbeef; + /* uses consolidate callback Pc instead of bogus 0xdeadbeef */ + memcpy( code_mem, call_rtlunwind, sizeof(call_rtlunwind) ); + FlushInstructionCache( GetCurrentProcess(), code_mem, sizeof(call_rtlunwind) ); + func( buf.Frame, 0xdeadbeef, &rec, 0, &ctx, NULL, pRtlUnwindEx, nonvol_regs ); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %ld\n", pass); +} + +#elif defined(__aarch64__) + +static void test_thread_context(void) +{ + CONTEXT context; + NTSTATUS status; + struct expected + { + ULONG64 X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, + X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, Fp, Lr, Sp, Pc; + ULONG Cpsr, Fpcr, Fpsr; + } expect; + NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = code_mem; + + static const DWORD call_func[] = + { + 0xa9bf7bfd, /* stp x29, x30, [sp, #-16]! */ + 0xa9000440, /* stp x0, x1, [x2] */ + 0xa9010c42, /* stp x2, x3, [x2, #16] */ + 0xa9021444, /* stp x4, x5, [x2, #32] */ + 0xa9031c46, /* stp x6, x7, [x2, #48] */ + 0xa9042448, /* stp x8, x9, [x2, #64] */ + 0xa9052c4a, /* stp x10, x11, [x2, #80] */ + 0xa906344c, /* stp x12, x13, [x2, #96] */ + 0xa9073c4e, /* stp x14, x15, [x2, #112] */ + 0xa9084450, /* stp x16, x17, [x2, #128] */ + 0xa9094c52, /* stp x18, x19, [x2, #144] */ + 0xa90a5454, /* stp x20, x21, [x2, #160] */ + 0xa90b5c56, /* stp x22, x23, [x2, #176] */ + 0xa90c6458, /* stp x24, x25, [x2, #192] */ + 0xa90d6c5a, /* stp x26, x27, [x2, #208] */ + 0xa90e745c, /* stp x28, x29, [x2, #224] */ + 0xf900785e, /* str x30, [x2, #240] */ + 0x910003e1, /* mov x1, sp */ + 0xf9007c41, /* str x1, [x2, #248] */ + 0x90000001, /* adrp x1, 1f */ + 0x9101e021, /* add x1, x1, #:lo12:1f */ + 0xf9008041, /* str x1, [x2, #256] */ + 0xd53b4201, /* mrs x1, nzcv */ + 0xb9010841, /* str w1, [x2, #264] */ + 0xd53b4401, /* mrs x1, fpcr */ + 0xb9010c41, /* str w1, [x2, #268] */ + 0xd53b4421, /* mrs x1, fpsr */ + 0xb9011041, /* str w1, [x2, #272] */ + 0xf9400441, /* ldr x1, [x2, #8] */ + 0xd63f0060, /* blr x3 */ + 0xa8c17bfd, /* 1: ldp x29, x30, [sp], #16 */ + 0xd65f03c0, /* ret */ + }; + + memcpy( func_ptr, call_func, sizeof(call_func) ); + +#define COMPARE(reg) \ + ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg ) + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + func_ptr( &context, 0, &expect, pRtlCaptureContext ); + trace( "expect: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08lx\n", + (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3, + (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7, + (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11, + (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15, + (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19, + (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23, + (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27, + (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp, + (void *)expect.Pc, expect.Cpsr ); + trace( "actual: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08lx\n", + (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3, + (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7, + (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11, + (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15, + (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19, + (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23, + (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27, + (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp, + (void *)context.Pc, context.Cpsr ); + + ok( context.ContextFlags == CONTEXT_FULL, + "wrong flags %08lx\n", context.ContextFlags ); + ok( !context.X0, "wrong X0 %p\n", (void *)context.X0 ); + COMPARE( X1 ); + COMPARE( X2 ); + COMPARE( X3 ); + COMPARE( X4 ); + COMPARE( X5 ); + COMPARE( X6 ); + COMPARE( X7 ); + COMPARE( X8 ); + COMPARE( X9 ); + COMPARE( X10 ); + COMPARE( X11 ); + COMPARE( X12 ); + COMPARE( X13 ); + COMPARE( X14 ); + COMPARE( X15 ); + COMPARE( X16 ); + COMPARE( X17 ); + COMPARE( X18 ); + COMPARE( X19 ); + COMPARE( X20 ); + COMPARE( X21 ); + COMPARE( X22 ); + COMPARE( X23 ); + COMPARE( X24 ); + COMPARE( X25 ); + COMPARE( X26 ); + COMPARE( X27 ); + COMPARE( X28 ); + COMPARE( Fp ); + COMPARE( Sp ); + COMPARE( Pc ); + COMPARE( Cpsr ); + COMPARE( Fpcr ); + COMPARE( Fpsr ); + ok( !context.Lr, "wrong Lr %p\n", (void *)context.Lr ); + + memset( &context, 0xcc, sizeof(context) ); + memset( &expect, 0xcc, sizeof(expect) ); + context.ContextFlags = CONTEXT_FULL; + + status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread ); + ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status ); + trace( "expect: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08lx\n", + (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3, + (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7, + (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11, + (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15, + (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19, + (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23, + (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27, + (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp, + (void *)expect.Pc, expect.Cpsr ); + trace( "actual: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08lx\n", + (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3, + (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7, + (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11, + (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15, + (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19, + (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23, + (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27, + (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp, + (void *)context.Pc, context.Cpsr ); + /* other registers are not preserved */ + COMPARE( X19 ); + COMPARE( X20 ); + COMPARE( X21 ); + COMPARE( X22 ); + COMPARE( X23 ); + COMPARE( X24 ); + COMPARE( X25 ); + COMPARE( X26 ); + COMPARE( X27 ); + COMPARE( X28 ); + COMPARE( Fp ); + COMPARE( Fpcr ); + COMPARE( Fpsr ); + ok( context.Lr == expect.Pc, "wrong Lr %p/%p\n", (void *)context.Lr, (void *)expect.Pc ); + ok( context.Sp == expect.Sp, "wrong Sp %p/%p\n", (void *)context.Sp, (void *)expect.Sp ); + ok( (char *)context.Pc >= (char *)pNtGetContextThread && + (char *)context.Pc <= (char *)pNtGetContextThread + 32, + "wrong Pc %p/%p\n", (void *)context.Pc, pNtGetContextThread ); +#undef COMPARE +} + +static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx) +{ + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + DEBUG_EVENT de; + DWORD continuestatus; + PVOID code_mem_address = NULL; + NTSTATUS status; + SIZE_T size_read; + BOOL ret; + int counter = 0; + si.cb = sizeof(si); + + if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess) + { + skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n"); + return; + } + + if (with_WaitForDebugEventEx && !pWaitForDebugEventEx) + { + skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n"); + return; + } + + sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "could not create child process error: %lu\n", GetLastError()); + if (!ret) + return; + + do + { + continuestatus = cont_status; + ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE); + ok(ret, "reading debug event\n"); + + ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); + ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError()); + + if (de.dwThreadId != pi.dwThreadId) + { + trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode); + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status); + continue; + } + + if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) + { + if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress) + { + skip("child process loaded at different address, terminating it\n"); + pNtTerminateProcess(pi.hProcess, 0); + } + } + else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) + { + CONTEXT ctx; + enum debugger_stages stage; + + counter++; + status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address, + sizeof(code_mem_address), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + ctx.ContextFlags = CONTEXT_FULL; + status = pNtGetContextThread(pi.hThread, &ctx); + ok(!status, "NtGetContextThread failed with 0x%lx\n", status); + + trace("exception 0x%lx at %p firstchance=%ld pc=%p, x0=%p\n", + de.u.Exception.ExceptionRecord.ExceptionCode, + de.u.Exception.ExceptionRecord.ExceptionAddress, + de.u.Exception.dwFirstChance, (char *)ctx.Pc, (char *)ctx.X0); + + if (counter > 100) + { + ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n"); + pNtTerminateProcess(pi.hProcess, 1); + } + else if (counter < 2) /* startup breakpoint */ + { + /* breakpoint is inside ntdll */ + void *ntdll = GetModuleHandleA( "ntdll.dll" ); + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll ); + + ok( (char *)ctx.Pc >= (char *)ntdll && + (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage, + "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll, + (char *)ntdll + nt->OptionalHeader.SizeOfImage ); + } + else + { + if (stage == STAGE_RTLRAISE_NOT_HANDLED) + { + ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, "Pc at %p instead of %p\n", + (char *)ctx.Pc, (char *)code_mem_address + 0xc); + /* setting the context from debugger does not affect the context that the + * exception handler gets, except on w2008 */ + ctx.Pc = (UINT_PTR)code_mem_address + 0x10; + ctx.X0 = 0xf00f00f1; + /* let the debuggee handle the exception */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) + { + if (de.u.Exception.dwFirstChance) + { + /* debugger gets first chance exception with unmodified ctx.Pc */ + ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, "Pc at %p instead of %p\n", + (char *)ctx.Pc, (char *)code_mem_address + 0xc); + ctx.Pc = (UINT_PTR)code_mem_address + 0x10; + ctx.X0 = 0xf00f00f1; + /* pass exception to debuggee + * exception will not be handled and a second chance exception will be raised */ + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + { + /* debugger gets context after exception handler has played with it */ + /* ctx.Pc is the same value the exception handler got */ + if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + { + ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, + "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xc); + /* need to fixup Pc for debuggee */ + ctx.Pc += 4; + } + else ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, + "Pc at %p instead of %p\n", (void *)ctx.Pc, (char *)code_mem_address + 0xc); + /* here we handle exception */ + } + } + else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d, + "expected Pc = %p, got %p\n", (char *)code_mem_address + 0x1d, (char *)ctx.Pc); + if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, + "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode); + ok((char *)ctx.Pc == (char *)code_mem_address + 4, + "expected Pc = %p, got %p\n", (char *)code_mem_address + 4, (char *)ctx.Pc); + if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_EXCEPTION_INVHANDLE_CONTINUE || stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, + "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode, + EXCEPTION_INVALID_HANDLE); + ok(de.u.Exception.ExceptionRecord.NumberParameters == 0, + "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters); + + if (stage == STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (stage == STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED) + { + ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n"); + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + ok(FALSE, "unexpected stage %x\n", stage); + + status = pNtSetContextThread(pi.hThread, &ctx); + ok(!status, "NtSetContextThread failed with 0x%lx\n", status); + } + } + else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) + { + enum debugger_stages stage; + char buffer[128 * sizeof(WCHAR)]; + unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char); + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (de.u.DebugString.fUnicode) + ok(with_WaitForDebugEventEx && + (stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED), + "unexpected unicode debug string event\n"); + else + ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE, + "unexpected ansi debug string event %u %s %lx\n", + stage, with_WaitForDebugEventEx ? "with" : "without", cont_status); + + ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1, + "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength); + + memset(buffer, 0, sizeof(buffer)); + status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer, + de.u.DebugString.nDebugStringLength * char_size, &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || + stage == STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + { + if (de.u.DebugString.fUnicode) + ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer); + else + ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer); + } + else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */ + ok(strstr(buffer, "SHIMVIEW") || !strncmp(buffer, "RTL:", 4), + "unexpected stage %x, got debug string event '%s'\n", stage, buffer); + + if (stage == STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED || stage == STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED) + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else if (de.dwDebugEventCode == RIP_EVENT) + { + enum debugger_stages stage; + + status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage, + sizeof(stage), &size_read); + ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status); + + if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED) + { + ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n", + de.u.RipInfo.dwError, 0x11223344); + ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n", + de.u.RipInfo.dwType, 0x55667788); + } + else + ok(FALSE, "unexpected stage %x\n", stage); + + if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); + + } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + wait_child_process( pi.hProcess ); + ret = CloseHandle(pi.hThread); + ok(ret, "error %lu\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "error %lu\n", GetLastError()); +} + +static void test_debug_service(DWORD numexc) +{ + /* not supported */ +} + +static void test_continue(void) +{ + struct context_pair { + CONTEXT before; + CONTEXT after; + } contexts; + KCONTINUE_ARGUMENT args = { .ContinueType = KCONTINUE_UNWIND }; + unsigned int i; + NTSTATUS (*func_ptr)( struct context_pair *, void *arg, void *continue_func, void *capture_func ) = code_mem; + + static const DWORD call_func[] = + { + 0xa9bd7bfd, /* stp x29, x30, [sp, #-0x30]! */ + /* stash volatile registers before calling capture */ + 0xa90107e0, /* stp x0, x1, [sp, #0x10] */ + 0xa9020fe2, /* stp x2, x3, [sp, #0x20] */ + 0xd63f0060, /* blr x3 * capture context from before NtContinue to contexts->before */ + 0xa9420fe2, /* ldp x2, x3, [sp, #0x20] */ + 0xa94107e0, /* ldp x0, x1, [sp, #0x10] */ + /* overwrite the contents of x4...k28 with a dummy value */ + 0xd297dde4, /* mov x4, #0xbeef */ + 0xf2bbd5a4, /* movk x4, #0xdead, lsl #16 */ + 0xaa048084, /* orr x4, x4, x4, lsl #32 */ + 0xaa0403e5, /* mov x5, x4 */ + 0xaa0403e6, /* mov x6, x4 */ + 0xaa0403e7, /* mov x7, x4 */ + 0xaa0403e8, /* mov x8, x4 */ + 0xaa0403e9, /* mov x9, x4 */ + 0xaa0403ea, /* mov x10, x4 */ + 0xaa0403eb, /* mov x11, x4 */ + 0xaa0403ec, /* mov x12, x4 */ + 0xaa0403ed, /* mov x13, x4 */ + 0xaa0403ee, /* mov x14, x4 */ + 0xaa0403ef, /* mov x15, x4 */ + 0xaa0403f0, /* mov x16, x4 */ + 0xaa0403f1, /* mov x17, x4 */ + /* avoid overwriting the TEB in x18 */ + 0xaa0403f3, /* mov x19, x4 */ + 0xaa0403f4, /* mov x20, x4 */ + 0xaa0403f5, /* mov x21, x4 */ + 0xaa0403f6, /* mov x22, x4 */ + 0xaa0403f7, /* mov x23, x4 */ + 0xaa0403f8, /* mov x24, x4 */ + 0xaa0403f9, /* mov x25, x4 */ + 0xaa0403fa, /* mov x26, x4 */ + 0xaa0403fb, /* mov x27, x4 */ + 0xaa0403fc, /* mov x28, x4 */ + /* overwrite the contents all vector registers a dummy value */ + 0x4e080c80, /* dup v0.2d, x4 */ + 0x4ea01c01, /* mov v1.2d, v0.2d */ + 0x4ea01c02, /* mov v2.2d, v0.2d */ + 0x4ea01c03, /* mov v3.2d, v0.2d */ + 0x4ea01c04, /* mov v4.2d, v0.2d */ + 0x4ea01c05, /* mov v5.2d, v0.2d */ + 0x4ea01c06, /* mov v6.2d, v0.2d */ + 0x4ea01c07, /* mov v7.2d, v0.2d */ + 0x4ea01c08, /* mov v8.2d, v0.2d */ + 0x4ea01c09, /* mov v9.2d, v0.2d */ + 0x4ea01c0a, /* mov v10.2d, v0.2d */ + 0x4ea01c0b, /* mov v11.2d, v0.2d */ + 0x4ea01c0c, /* mov v12.2d, v0.2d */ + 0x4ea01c0d, /* mov v13.2d, v0.2d */ + 0x4ea01c0e, /* mov v14.2d, v0.2d */ + 0x4ea01c0f, /* mov v15.2d, v0.2d */ + 0x4ea01c10, /* mov v16.2d, v0.2d */ + 0x4ea01c11, /* mov v17.2d, v0.2d */ + 0x4ea01c12, /* mov v18.2d, v0.2d */ + 0x4ea01c13, /* mov v19.2d, v0.2d */ + 0x4ea01c14, /* mov v20.2d, v0.2d */ + 0x4ea01c15, /* mov v21.2d, v0.2d */ + 0x4ea01c16, /* mov v22.2d, v0.2d */ + 0x4ea01c17, /* mov v23.2d, v0.2d */ + 0x4ea01c18, /* mov v24.2d, v0.2d */ + 0x4ea01c19, /* mov v25.2d, v0.2d */ + 0x4ea01c1a, /* mov v26.2d, v0.2d */ + 0x4ea01c1b, /* mov v27.2d, v0.2d */ + 0x4ea01c1c, /* mov v28.2d, v0.2d */ + 0x4ea01c1d, /* mov v29.2d, v0.2d */ + 0x4ea01c1e, /* mov v30.2d, v0.2d */ + 0x4ea01c1f, /* mov v31.2d, v0.2d */ + 0xd51b441f, /* msr fpcr, xzr */ + 0xd51b443f, /* msr fpsr, xzr */ + /* setup the control context so execution continues from label 1 */ + 0x10000064, /* adr x4, #0xc */ + 0xf9008404, /* str x4, [x0, #0x108] */ + 0xd63f0040, /* blr x2 * restore the captured integer and floating point context */ + 0xf94017e3, /* 1: ldr x3, [sp, #0x28] */ + 0xf9400be0, /* ldr x0, [sp, #0x10] */ + 0x910e4000, /* add x0, x0, #0x390 * adjust contexts to point to contexts->after */ + 0xd63f0060, /* blr x3 * capture context from after NtContinue to contexts->after */ + 0xa8c37bfd, /* ldp x29, x30, [sp], #0x30 */ + 0xd65f03c0, /* ret */ + }; + + if (!pRtlCaptureContext) + { + win_skip("RtlCaptureContext is not available.\n"); + return; + } + + memcpy( func_ptr, call_func, sizeof(call_func) ); + FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) ); + +#define COMPARE(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) +#define COMPARE_INDEXED(reg) \ + ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " i: %u, %p/%p\n", i, (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg ) + + func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext ); + + for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] ); + + COMPARE( Fpcr ); + COMPARE( Fpsr ); + + for (i = 0; i < 32; i++) + { + COMPARE_INDEXED( V[i].Low ); + COMPARE_INDEXED( V[i].High ); + } + + apc_count = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext ); + ok( apc_count == 0, "apc called\n" ); + func_ptr( &contexts, (void *)1, NtContinue, pRtlCaptureContext ); + ok( apc_count == 1, "apc not called\n" ); + + if (!pNtContinueEx) + { + win_skip( "NtContinueEx not supported\n" ); + return; + } + + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + + for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] ); + + COMPARE( Fpcr ); + COMPARE( Fpsr ); + + for (i = 0; i < 32; i++) + { + COMPARE_INDEXED( V[i].Low ); + COMPARE_INDEXED( V[i].High ); + } + + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + ok( apc_count == 1, "apc called\n" ); + args.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT; + func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext ); + ok( apc_count == 2, "apc not called\n" ); + +#undef COMPARE +} + +static BOOL hook_called; +static BOOL got_exception; + +static ULONG patched_code[] = +{ + 0x910003e0, /* mov x0, sp */ + 0x5800004f, /* ldr x15, 1f */ + 0xd61f01e0, /* br x15 */ + 0, 0, /* 1: hook_trampoline */ +}; +static ULONG saved_code[ARRAY_SIZE(patched_code)]; + +static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *ptrs) +{ + EXCEPTION_RECORD *rec = ptrs->ExceptionRecord; + CONTEXT *context = ptrs->ContextRecord; + + trace("dbg_except_continue_vectored_handler, code %#lx, pc %#Ix.\n", rec->ExceptionCode, context->Pc); + got_exception = TRUE; + + ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void * WINAPI hook_KiUserExceptionDispatcher(void *stack) +{ + struct + { + CONTEXT context; /* 000 */ + CONTEXT_EX context_ex; /* 390 */ + EXCEPTION_RECORD rec; /* 3b0 */ + ULONG64 align; /* 448 */ + ULONG64 sp; /* 450 */ + ULONG64 pc; /* 458 */ + } *args = stack; + EXCEPTION_RECORD *old_rec = (EXCEPTION_RECORD *)&args->context_ex; + + trace("stack %p context->Pc %#Ix, context->Sp %#Ix, ContextFlags %#lx.\n", + stack, args->context.Pc, args->context.Sp, args->context.ContextFlags); + + hook_called = TRUE; + ok( !((ULONG_PTR)stack & 15), "unaligned stack %p\n", stack ); + + if (!broken( old_rec->ExceptionCode == 0x80000003 )) /* Windows 11 versions prior to 27686 */ + { + ok( args->rec.ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", args->rec.ExceptionCode ); + + ok( args->context_ex.All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", args->context_ex.All.Offset ); + ok( args->context_ex.All.Length >= sizeof(CONTEXT) + offsetof(CONTEXT_EX, align), "wrong All.Length %lx\n", args->context_ex.All.Length ); + ok( args->context_ex.Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", args->context_ex.All.Offset ); + ok( args->context_ex.Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", args->context_ex.All.Length ); + ok( args->sp == args->context.Sp, "wrong sp %Ix / %Ix\n", args->sp, args->context.Sp ); + ok( args->pc == args->context.Pc, "wrong pc %Ix / %Ix\n", args->pc, args->context.Pc ); + } + + memcpy(pKiUserExceptionDispatcher, saved_code, sizeof(saved_code)); + FlushInstructionCache( GetCurrentProcess(), pKiUserExceptionDispatcher, sizeof(saved_code)); + return pKiUserExceptionDispatcher; +} + +static void test_KiUserExceptionDispatcher(void) +{ + ULONG hook_trampoline[] = + { + 0x910003e0, /* mov x0, sp */ + 0x5800006f, /* ldr x15, 1f */ + 0xd63f01e0, /* blr x15 */ + 0xd61f0000, /* br x0 */ + 0, 0, /* 1: hook_KiUserExceptionDispatcher */ + }; + + EXCEPTION_RECORD record = { EXCEPTION_BREAKPOINT }; + void *trampoline_ptr, *vectored_handler; + DWORD old_protect; + BOOL ret; + + *(void **)&hook_trampoline[4] = hook_KiUserExceptionDispatcher; + trampoline_ptr = (char *)code_mem + 1024; + memcpy( trampoline_ptr, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserExceptionDispatcher, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, pKiUserExceptionDispatcher, sizeof(saved_code) ); + *(void **)&patched_code[3] = trampoline_ptr; + + vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler); + + memcpy( pKiUserExceptionDispatcher, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), pKiUserExceptionDispatcher, sizeof(patched_code)); + + got_exception = FALSE; + hook_called = FALSE; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(!hook_called, "Hook was called.\n"); + + memcpy( pKiUserExceptionDispatcher, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), pKiUserExceptionDispatcher, sizeof(patched_code)); + + got_exception = 0; + hook_called = FALSE; + NtCurrentTeb()->Peb->BeingDebugged = 1; + + pRtlRaiseException(&record); + + ok(got_exception, "Handler was not called.\n"); + ok(hook_called, "Hook was not called.\n"); + NtCurrentTeb()->Peb->BeingDebugged = 0; + + RemoveVectoredExceptionHandler(vectored_handler); + VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_code), old_protect, &old_protect); +} + +static void * WINAPI hook_KiUserApcDispatcher(void *stack) +{ + struct + { + void *func; + ULONG64 args[3]; + ULONG64 alertable; + ULONG64 align; + CONTEXT context; + } *args = stack; + + trace( "stack=%p func=%p args=%Ix,%Ix,%Ix alertable=%Ix context=%p pc=%Ix sp=%Ix (%Ix)\n", + args, args->func, args->args[0], args->args[1], args->args[2], + args->alertable, &args->context, args->context.Pc, args->context.Sp, + args->context.Sp - (ULONG_PTR)stack ); + + ok( args->func == apc_func, "wrong func %p / %p\n", args->func, apc_func ); + ok( args->args[0] == 0x1234 + apc_count, "wrong arg1 %Ix\n", args->args[0] ); + ok( args->args[1] == 0x5678, "wrong arg2 %Ix\n", args->args[1] ); + ok( args->args[2] == 0xdeadbeef, "wrong arg3 %Ix\n", args->args[2] ); + + if (apc_count) args->alertable = FALSE; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef ); + + hook_called = TRUE; + memcpy( pKiUserApcDispatcher, saved_code, sizeof(saved_code)); + FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(saved_code)); + return pKiUserApcDispatcher; +} + +static void test_KiUserApcDispatcher(void) +{ + ULONG hook_trampoline[] = + { + 0x910003e0, /* mov x0, sp */ + 0x5800006f, /* ldr x15, 1f */ + 0xd63f01e0, /* blr x15 */ + 0xd61f0000, /* br x0 */ + 0, 0, /* 1: hook_KiUserApcDispatcher */ + }; + DWORD old_protect; + BOOL ret; + + *(void **)&hook_trampoline[4] = hook_KiUserApcDispatcher; + memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline)); + + ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, pKiUserApcDispatcher, sizeof(saved_code) ); + *(void **)&patched_code[3] = code_mem; + memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(patched_code)); + + hook_called = FALSE; + apc_count = 0; + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 2, "APC count %u\n", apc_count ); + ok( hook_called, "hook was not called\n" ); + + memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) ); + FlushInstructionCache( GetCurrentProcess(), pKiUserApcDispatcher, sizeof(patched_code)); + pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef ); + SleepEx( 0, TRUE ); + ok( apc_count == 3, "APC count %u\n", apc_count ); + SleepEx( 0, TRUE ); + ok( apc_count == 4, "APC count %u\n", apc_count ); + + VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code), old_protect, &old_protect ); +} + +static void WINAPI hook_KiUserCallbackDispatcher(void *sp) +{ + struct + { + void *args; + ULONG len; + ULONG id; + ULONG64 unknown; + ULONG64 lr; + ULONG64 sp; + ULONG64 pc; + BYTE args_data[0]; + } *stack = sp; + ULONG_PTR redzone = (BYTE *)stack->sp - &stack->args_data[stack->len]; + KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id]; + + trace( "stack=%p len=%lx id=%lx unk=%Ix lr=%Ix sp=%Ix pc=%Ix\n", + stack, stack->len, stack->id, stack->unknown, stack->lr, stack->sp, stack->pc ); + + ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data ); + ok( redzone >= 16 && redzone <= 32, "wrong sp %p / %p (%Iu)\n", + (void *)stack->sp, stack->args_data, redzone ); + + if (pRtlPcToFileHeader) + { + void *mod, *win32u = GetModuleHandleA("win32u.dll"); + + pRtlPcToFileHeader( (void *)stack->pc, &mod ); + ok( mod == win32u, "pc %Ix not in win32u %p\n", stack->pc, win32u ); + } + NtCallbackReturn( NULL, 0, func( stack->args, stack->len )); +} + +static void test_KiUserCallbackDispatcher(void) +{ + DWORD old_protect; + BOOL ret; + + ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), + PAGE_EXECUTE_READWRITE, &old_protect ); + ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() ); + + memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code)); + *(void **)&patched_code[3] = hook_KiUserCallbackDispatcher; + memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code)); + FlushInstructionCache(GetCurrentProcess(), pKiUserCallbackDispatcher, sizeof(patched_code)); + + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + + memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code)); + FlushInstructionCache(GetCurrentProcess(), pKiUserCallbackDispatcher, sizeof(saved_code)); + VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect ); +} + +static void run_exception_test(void *handler, const void* context, + const void *code, unsigned int code_size, + unsigned int func2_offset, DWORD access, DWORD handler_flags, + void *arg1, void *arg2) +{ + DWORD buf[14]; + RUNTIME_FUNCTION runtime_func[2]; + IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA unwind; + void (*func)(void*,void*) = code_mem; + DWORD oldaccess, oldaccess2; + + runtime_func[0].BeginAddress = 0; + runtime_func[0].UnwindData = 0x1000; + runtime_func[1].BeginAddress = func2_offset; + runtime_func[1].UnwindData = 0x1014; + + unwind.FunctionLength = func2_offset / 4; + unwind.Version = 0; + unwind.ExceptionDataPresent = 1; + unwind.EpilogInHeader = 1; + unwind.EpilogCount = 1; + unwind.CodeWords = 1; + buf[0] = unwind.HeaderData; + buf[1] = 0xe3e481e1; /* mov x29,sp; stp r29,lr,[sp,-#0x10]!; end; nop */ + buf[2] = 0x1028; + *(const void **)&buf[3] = context; + + unwind.FunctionLength = (code_size - func2_offset) / 4; + buf[5] = unwind.HeaderData; + buf[6] = 0xe3e481e1; /* mov x29,sp; stp r29,lr,[sp,-#0x10]!; end; nop */ + buf[7] = 0x1028; + *(const void **)&buf[8] = context; + + buf[10] = 0x5800004f; /* ldr x15, 1f */ + buf[11] = 0xd61f01e0; /* br x15 */ + *(const void **)&buf[12] = handler; + + memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf)); + memcpy(code_mem, code, code_size); + if (access) VirtualProtect(code_mem, code_size, access, &oldaccess); + FlushInstructionCache( GetCurrentProcess(), code_mem, 0x2000 ); + + pRtlAddFunctionTable(runtime_func, ARRAY_SIZE(runtime_func), (ULONG_PTR)code_mem); + func( arg1, arg2 ); + pRtlDeleteFunctionTable(runtime_func); + + if (access) VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2); +} + +static BOOL got_nested_exception, got_prev_frame_exception; +static void *nested_exception_initial_frame; + +static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + trace("nested_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n", + (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress); + + if (rec->ExceptionCode == 0x80000003 && !(rec->ExceptionFlags & EXCEPTION_NESTED_CALL)) + { + ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok((char *)context->Sp == (char *)frame - 0x10, "Got unexpected frame %p / %p.\n", frame, (void *)context->Sp); + ok((char *)context->Fp == (char *)frame - 0x10, "Got unexpected frame %p / %p.\n", frame, (void *)context->Fp); + ok((char *)context->Lr == (char *)code_mem + 0x0c, "Got unexpected lr %p.\n", (void *)context->Lr); + ok((char *)context->Pc == (char *)code_mem + 0x1c, "Got unexpected pc %p.\n", (void *)context->Pc); + + nested_exception_initial_frame = frame; + RaiseException(0xdeadbeef, 0, 0, 0); + context->Pc += 4; + return ExceptionContinueExecution; + } + + if (rec->ExceptionCode == 0xdeadbeef && + (rec->ExceptionFlags == EXCEPTION_NESTED_CALL || + rec->ExceptionFlags == (EXCEPTION_NESTED_CALL | EXCEPTION_SOFTWARE_ORIGINATE))) + { + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + got_nested_exception = TRUE; + ok(frame == nested_exception_initial_frame, "Got unexpected frame %p / %p.\n", + frame, nested_exception_initial_frame); + return ExceptionContinueSearch; + } + + ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE), + "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags); + ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters); + ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "Got unexpected frame %p / %p.\n", + frame, nested_exception_initial_frame); + got_prev_frame_exception = TRUE; + return ExceptionContinueExecution; +} + +static const DWORD nested_except_code[] = +{ + 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */ + 0x910003fd, /* 04: mov x29, sp */ + 0x94000003, /* 08: bl 1f */ + 0xa8c17bfd, /* 0c: ldp x29, x30, [sp], #16 */ + 0xd65f03c0, /* 10: ret */ + + 0xa9bf7bfd, /* 14: stp x29, x30, [sp, #-16]! */ + 0x910003fd, /* 18: mov x29, sp */ + 0xd43e0000, /* 1c: brk #0xf000 */ + 0xd503201f, /* 20: nop */ + 0xa8c17bfd, /* 24: ldp x29, x30, [sp], #16 */ + 0xd65f03c0, /* 28: ret */ +}; + +static void test_nested_exception(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), + 5 * sizeof(DWORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, 0, 0); + ok(got_nested_exception, "Did not get nested exception.\n"); + ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n"); +} + +static unsigned int collided_unwind_exception_count; + +static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + CONTEXT ctx; + + trace("collided_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n", + (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame); + + switch(collided_unwind_exception_count++) + { + case 0: + /* Initial exception from nested_except_code. */ + ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode); + nested_exception_initial_frame = frame; + /* Start unwind. */ + pRtlUnwindEx((char *)frame + 0x10, (char *)code_mem + 0x0c, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 1: + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)context->Pc == (char *)code_mem + 0x1c, "got %p.\n", (void *)context->Pc); + /* generate exception in unwind handler. */ + RaiseException(0xdeadbeef, 0, 0, 0); + ok(0, "shouldn't be reached\n"); + break; + case 2: + /* Inner call frame, continue search. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 3: + /* Top level call frame, handle exception by unwinding. */ + ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode); + ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + pRtlUnwindEx((char *)nested_exception_initial_frame + 0x10, (char *)code_mem + 0x0c, NULL, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + break; + case 4: + /* Collided unwind. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_COLLIDED_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + case 5: + /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */ + ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode); + ok(rec->ExceptionFlags == (EXCEPTION_UNWINDING | EXCEPTION_TARGET_UNWIND), "got %#lx.\n", rec->ExceptionFlags); + ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "got %p, expected %p.\n", frame, nested_exception_initial_frame); + break; + } + return ExceptionContinueSearch; +} + +static void test_collided_unwind(void) +{ + got_nested_exception = got_prev_frame_exception = FALSE; + collided_unwind_exception_count = 0; + run_exception_test(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), + 5 * sizeof(DWORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER, 0, 0); + ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count); +} + + +static int rtlraiseexception_unhandled_handler_called; +static int rtlraiseexception_teb_handler_called; +static int rtlraiseexception_handler_called; + +static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, + void *dispatcher, BOOL unhandled_handler ) +{ + void *addr = rec->ExceptionAddress; + + trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n", + rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc ); + + ok( addr == (char *)code_mem + 0x0c, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0x0c ); + ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_UNWOUND_TO_CALL) || + context->ContextFlags == (CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_UNWOUND_TO_CALL), + "wrong context flags %lx\n", context->ContextFlags ); + ok( context->Pc == (UINT_PTR)addr, + "%d: Pc at %Ix instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr ); + + ok( context->X0 == 0xf00f00f0, "context->X0 is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->X0 ); +} + +static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + rtlraiseexception_unhandled_handler_called = 1; + rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE); + if (test_stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE) return EXCEPTION_CONTINUE_SEARCH; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec, + EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, + EXCEPTION_REGISTRATION_RECORD **dispatcher ) +{ + rtlraiseexception_teb_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + DISPATCHER_CONTEXT_NONVOLREG_ARM64 *nonvol_regs = (void *)dispatcher->NonVolatileRegisters; + int i; + + for (i = 0; i < NONVOL_INT_NUMREG_ARM64; i++) + ok( nonvol_regs->GpNvRegs[i] == ((DWORD64 *)&context->X19)[i], + "wrong non volatile reg x%u %I64x / %I64x\n", i + 19, + nonvol_regs->GpNvRegs[i] , ((DWORD64 *)&context->X19)[i] ); + for (i = 0; i < NONVOL_FP_NUMREG_ARM64; i++) + ok( nonvol_regs->FpNvRegs[i] == context->V[i + 8].D[0], + "wrong non volatile reg d%u %g / %g\n", i + 8, + nonvol_regs->FpNvRegs[i] , context->V[i + 8].D[0] ); + + rtlraiseexception_handler_called = 1; + rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE); + return ExceptionContinueSearch; +} + +static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PCONTEXT context = ExceptionInfo->ContextRecord; + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + void *addr = rec->ExceptionAddress; + + ok( addr == (char *)code_mem + 0xc, + "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0xc ); + ok( context->Pc == (UINT_PTR)addr, + "%d: Pc at %Ix instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr ); + + context->X0 = 0xf00f00f0; + + return EXCEPTION_CONTINUE_SEARCH; +} + +static const DWORD call_one_arg_code[] = +{ + 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */ + 0x910003fd, /* 04: mov x29, sp */ + 0xd63f0020, /* 08: blr x1 */ + 0xd503201f, /* 0c: nop */ + 0xa8c17bfd, /* 10: ldp x29, x30, [sp], #16 */ + 0xd65f03c0, /* 14: ret */ +}; + +static void run_rtlraiseexception_test(DWORD exceptioncode) +{ + EXCEPTION_REGISTRATION_RECORD frame; + EXCEPTION_RECORD record; + PVOID vectored_handler = NULL; + + record.ExceptionCode = exceptioncode; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; /* does not matter, copied return address */ + record.NumberParameters = 0; + + frame.Handler = rtlraiseexception_teb_handler; + frame.Prev = NtCurrentTeb()->Tib.ExceptionList; + + NtCurrentTeb()->Tib.ExceptionList = &frame; + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler); + + rtlraiseexception_handler_called = 0; + rtlraiseexception_teb_handler_called = 0; + rtlraiseexception_unhandled_handler_called = 0; + + run_exception_test( rtlraiseexception_handler, NULL, call_one_arg_code, + sizeof(call_one_arg_code), sizeof(call_one_arg_code), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + &record, pRtlRaiseException); + + ok( record.ExceptionAddress == (char *)code_mem + 0x0c, + "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0c ); + + todo_wine + ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" ); + ok( rtlraiseexception_handler_called, "Frame handler called\n" ); + ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" ); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); + + if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL); + NtCurrentTeb()->Tib.ExceptionList = frame.Prev; +} + +static void test_rtlraiseexception(void) +{ + run_rtlraiseexception_test(0x12345); + run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); + run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); +} + +static DWORD brk_exception_handler_code; + +static DWORD WINAPI brk_exception_handler( EXCEPTION_RECORD *rec, void *frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) +{ + ok( rec->ExceptionCode == brk_exception_handler_code, "got: %08lx\n", rec->ExceptionCode ); + ok( rec->NumberParameters == 0, "got: %ld\n", rec->NumberParameters ); + ok( rec->ExceptionAddress == (void *)context->Pc, "got addr: %p, pc: %p\n", rec->ExceptionAddress, (void *)context->Pc ); + context->Pc += 4; + return ExceptionContinueExecution; +} + + +static void test_brk(void) +{ + DWORD call_brk[] = + { + 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */ + 0x910003fd, /* 04: mov x29, sp */ + 0x00000000, /* 08: */ + 0xd503201f, /* 0c: nop */ + 0xa8c17bfd, /* 10: ldp x29, x30, [sp], #16 */ + 0xd65f03c0, /* 14: ret */ + }; + + /* brk #0xf000 is tested as part of breakpoint tests */ + + brk_exception_handler_code = STATUS_ASSERTION_FAILURE; + call_brk[2] = 0xd43e0020; /* 08: brk #0xf001 */ + run_exception_test( brk_exception_handler, NULL, call_brk, + sizeof(call_brk), sizeof(call_brk), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + 0, 0 ); + + /* FIXME: brk #0xf002 needs debug service tests */ + + /* brk #0xf003 is tested as part of fastfail tests*/ + + brk_exception_handler_code = EXCEPTION_INT_DIVIDE_BY_ZERO; + call_brk[2] = 0xd43e0080; /* 08: brk #0xf004 */ + run_exception_test( brk_exception_handler, NULL, call_brk, + sizeof(call_brk), sizeof(call_brk), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + 0, 0 ); + + /* Any unknown immediate raises EXCEPTION_ILLEGAL_INSTRUCTION */ + + brk_exception_handler_code = EXCEPTION_ILLEGAL_INSTRUCTION; + call_brk[2] = 0xd43e00a0; /* 08: brk #0xf005 */ + run_exception_test( brk_exception_handler, NULL, call_brk, + sizeof(call_brk), sizeof(call_brk), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + 0, 0 ); + + brk_exception_handler_code = EXCEPTION_ILLEGAL_INSTRUCTION; + call_brk[2] = 0xd4200000; /* 08: brk #0x0 */ + run_exception_test( brk_exception_handler, NULL, call_brk, + sizeof(call_brk), sizeof(call_brk), + PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, + 0, 0 ); +} + +static LONG consolidate_dummy_called; +static LONG pass; + +static const DWORD call_rtlunwind[] = +{ + 0xa88150f3, /* stp x19, x20, [x7], #0x10 */ + 0xa88158f5, /* stp x21, x22, [x7], #0x10 */ + 0xa88160f7, /* stp x23, x24, [x7], #0x10 */ + 0xa88168f9, /* stp x25, x26, [x7], #0x10 */ + 0xa88170fb, /* stp x27, x28, [x7], #0x10 */ + 0xf80084fd, /* str x29, [x7], #0x8 */ + 0x6c8124e8, /* stp d8, d9, [x7], #0x10 */ + 0x6c812cea, /* stp d10, d11, [x7], #0x10 */ + 0x6c8134ec, /* stp d12, d13, [x7], #0x10 */ + 0x6c813cee, /* stp d14, d15, [x7], #0x10 */ + 0xd61f00c0, /* br x6 */ +}; + +static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec) +{ + CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1]; + DWORD64 *saved_regs = (DWORD64 *)rec->ExceptionInformation[3]; + DISPATCHER_CONTEXT_NONVOLREG_ARM64 *regs; + int i; + + switch (InterlockedIncrement(&consolidate_dummy_called)) + { + case 1: /* RtlRestoreContext */ + ok(ctx->Pc == 0xdeadbeef, "RtlRestoreContext wrong Pc, expected: 0xdeadbeef, got: %Ix\n", ctx->Pc); + ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + break; + case 2: /* RtlUnwindEx */ + ok(ctx->Pc != 0xdeadbeef, "RtlUnwindEx wrong Pc, got: %Ix\n", ctx->Pc ); + ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] ); + regs = (DISPATCHER_CONTEXT_NONVOLREG_ARM64 *)rec->ExceptionInformation[10]; + for (i = 0; i < 11; i++) + ok( saved_regs[i] == regs->GpNvRegs[i], "wrong reg X%u, expected: %Ix, got: %Ix\n", + 19 + i, saved_regs[i], regs->GpNvRegs[i] ); + for (i = 0; i < 8; i++) + ok( saved_regs[i + 11] == *(DWORD64 *)®s->FpNvRegs[i], + "wrong reg D%u, expected: %Ix, got: %Ix\n", + i + 8, saved_regs[i + 11], *(DWORD64 *)®s->FpNvRegs[i] ); + break; + } + return (PVOID)rec->ExceptionInformation[2]; +} + +static void test_restore_context(void) +{ + EXCEPTION_RECORD rec; + _JUMP_BUFFER buf; + CONTEXT ctx; + int i; + + if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext) + { + skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n"); + return; + } + + /* test simple case of capture and restore context */ + pass = 0; + InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */ + pRtlCaptureContext(&ctx); + if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */ + { + pRtlRestoreContext(&ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass < 4, "unexpected pass %ld\n", pass); + + /* test with jmp using RtlRestoreContext */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + setjmp((_JBTYPE *)&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + /* uses buf.Pc instead of ctx.Pc */ + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 4) + { + ok(buf.X19 == ctx.X19, "longjmp failed for X19, expected: %Ix, got: %Ix\n", buf.X19, ctx.X19); + ok(buf.X20 == ctx.X20, "longjmp failed for X20, expected: %Ix, got: %Ix\n", buf.X20, ctx.X20); + ok(buf.X21 == ctx.X21, "longjmp failed for X21, expected: %Ix, got: %Ix\n", buf.X21, ctx.X21); + ok(buf.X22 == ctx.X22, "longjmp failed for X22, expected: %Ix, got: %Ix\n", buf.X22, ctx.X22); + ok(buf.X23 == ctx.X23, "longjmp failed for X23, expected: %Ix, got: %Ix\n", buf.X23, ctx.X23); + ok(buf.X24 == ctx.X24, "longjmp failed for X24, expected: %Ix, got: %Ix\n", buf.X24, ctx.X24); + ok(buf.X25 == ctx.X25, "longjmp failed for X25, expected: %Ix, got: %Ix\n", buf.X25, ctx.X25); + ok(buf.X26 == ctx.X26, "longjmp failed for X26, expected: %Ix, got: %Ix\n", buf.X26, ctx.X26); + ok(buf.X27 == ctx.X27, "longjmp failed for X27, expected: %Ix, got: %Ix\n", buf.X27, ctx.X27); + ok(buf.X28 == ctx.X28, "longjmp failed for X28, expected: %Ix, got: %Ix\n", buf.X28, ctx.X28); + ok(buf.Fp == ctx.Fp, "longjmp failed for Fp, expected: %Ix, got: %Ix\n", buf.Fp, ctx.Fp); + for (i = 0; i < 8; i++) + ok(buf.D[i] == ctx.V[i + 8].D[0], "longjmp failed for D%u, expected: %g, got: %g\n", + i + 8, buf.D[i], ctx.V[i + 8].D[0]); + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 5, "unexpected pass %ld\n", pass); + + /* test with jmp through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); /* only called once */ + setjmp((_JBTYPE *)&buf); + InterlockedIncrement(&pass); + if (pass == 3) + { + rec.ExceptionCode = STATUS_LONGJUMP; + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD64)&buf; + + /* uses buf.Pc instead of bogus 0xdeadbeef */ + pRtlUnwindEx((void*)buf.Sp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL); + ok(0, "shouldn't be reached\n"); + } + else + ok(pass == 4, "unexpected pass %ld\n", pass); + + + /* test with consolidate */ + pass = 0; + InterlockedIncrement(&pass); + RtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + if (pass == 2) + { + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 3; + rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD64)&ctx; + rec.ExceptionInformation[2] = ctx.Pc; + rec.ExceptionInformation[10] = -1; + ctx.Pc = 0xdeadbeef; + + pRtlRestoreContext(&ctx, &rec); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %ld\n", pass); + + /* test with consolidate through RtlUnwindEx */ + pass = 0; + InterlockedIncrement(&pass); + pRtlCaptureContext(&ctx); + InterlockedIncrement(&pass); + setjmp((_JBTYPE *)&buf); + if (pass == 2) + { + void (*func)(DWORD64,DWORD64,EXCEPTION_RECORD*,DWORD64,CONTEXT*,void*,void*,void*) = code_mem; + DWORD64 nonvol_regs[19]; + + rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE; + rec.NumberParameters = 4; + rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy; + rec.ExceptionInformation[1] = (DWORD64)&ctx; + rec.ExceptionInformation[2] = ctx.Pc; + rec.ExceptionInformation[3] = (DWORD64)nonvol_regs; + rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */ + ctx.Pc = 0xdeadbeef; + /* uses consolidate callback Pc instead of bogus 0xdeadbeef */ + memcpy( code_mem, call_rtlunwind, sizeof(call_rtlunwind) ); + FlushInstructionCache( GetCurrentProcess(), code_mem, sizeof(call_rtlunwind) ); + func( buf.Frame, 0xdeadbeef, &rec, 0, &ctx, NULL, pRtlUnwindEx, nonvol_regs ); + ok(0, "shouldn't be reached\n"); + } + else if (pass == 3) + ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n"); + else + ok(0, "unexpected pass %ld\n", pass); +} + +static void test_mrs_currentel(void) +{ + DWORD64 (*func_ptr)(void) = code_mem; + DWORD64 result; + + static const DWORD call_func[] = + { + 0xd5384240, /* mrs x0, CurrentEL */ + 0xd538425f, /* mrs xzr, CurrentEL */ + 0xd65f03c0, /* ret */ + }; + + memcpy( func_ptr, call_func, sizeof(call_func) ); + FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) ); + result = func_ptr(); + ok( result == 0, "expected 0, got %llx\n", result ); +} + + +#endif /* __aarch64__ */ #if defined(__i386__) || defined(__x86_64__) @@ -2199,13 +8380,13 @@ static DWORD WINAPI register_check_thread(void *arg) ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; status = pNtGetContextThread(GetCurrentThread(), &ctx); - ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status); - ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0); - ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1); - ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2); - ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3); - ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6); - ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status); + ok(!ctx.Dr0, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr3); + ok(!ctx.Dr6, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr6); + ok(!ctx.Dr7, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr7); return 0; } @@ -2226,7 +8407,7 @@ static void test_debug_registers(void) HANDLE thread; int i; - for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) + for (i = 0; i < ARRAY_SIZE(tests); i++) { memset(&ctx, 0, sizeof(ctx)); ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; @@ -2238,19 +8419,31 @@ static void test_debug_registers(void) ctx.Dr7 = tests[i].dr7; status = pNtSetContextThread(GetCurrentThread(), &ctx); - ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %08lx\n", status); - memset(&ctx, 0, sizeof(ctx)); + memset(&ctx, 0xcc, sizeof(ctx)); ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; status = pNtGetContextThread(GetCurrentThread(), &ctx); - ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status); - ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0); - ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1); - ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2); - ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3); - ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6); - ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08lx\n", status); + if (is_arm64ec) /* setting debug registers is silently ignored */ + { + ok(!ctx.Dr0, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr0); + ok(!ctx.Dr1, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr1); + ok(!ctx.Dr2, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr2); + ok(!ctx.Dr3, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr3); + ok(!ctx.Dr6, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr6); + ok(!ctx.Dr7, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr7); + } + else + { + ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0); + ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1); + ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2); + ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3); + ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6); + ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7); + } } memset(&ctx, 0, sizeof(ctx)); @@ -2262,147 +8455,195 @@ static void test_debug_registers(void) ctx.Dr6 = 0xffffffff; ctx.Dr7 = 0x00000400; status = pNtSetContextThread(GetCurrentThread(), &ctx); - ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status); + ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", status); thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL); - ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError()); + ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %ld\n", GetLastError()); ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; status = pNtGetContextThread(thread, &ctx); - ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status); - ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0); - ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1); - ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2); - ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3); - ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6); - ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7); + ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status); + ok(!ctx.Dr0, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr3); + ok(!ctx.Dr6, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr6); + ok(!ctx.Dr7, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr7); ResumeThread(thread); WaitForSingleObject(thread, 10000); CloseHandle(thread); } -static DWORD outputdebugstring_exceptions; +#if defined(__x86_64__) -static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +static void test_debug_registers_wow64(void) { - PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); + char cmdline[] = "C:\\windows\\syswow64\\msinfo32.exe"; + PROCESS_INFORMATION pi; + STARTUPINFOA si = {0}; + WOW64_CONTEXT wow64_ctx; + CONTEXT ctx; + BOOL is_wow64; + NTSTATUS ret; + BOOL bret; - ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n", - rec->ExceptionCode, DBG_PRINTEXCEPTION_C); - ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters); - ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]); - ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), - "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + si.cb = sizeof(si); + bret = CreateProcessA(cmdline, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(bret, "CreateProcessA failed\n"); - outputdebugstring_exceptions++; - return EXCEPTION_CONTINUE_SEARCH; -} + bret = pIsWow64Process(pi.hProcess, &is_wow64); + ok(bret && is_wow64, "expected Wow64 process\n"); -static void test_outputdebugstring(DWORD numexc) -{ - PVOID vectored_handler; + SuspendThread(pi.hThread); - if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) - { - skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); - return; + ZeroMemory(&ctx, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_ALL; + bret = GetThreadContext(pi.hThread, &ctx); + ok(bret, "GetThreadContext failed\n"); + + ctx.Dr0 = 0x12340000; + ctx.Dr1 = 0x12340001; + ctx.Dr2 = 0x12340002; + ctx.Dr3 = 0x12340003; + ctx.Dr7 = 0x155; /* enable all breakpoints (local) */ + bret = SetThreadContext(pi.hThread, &ctx); + ok(bret, "SetThreadContext failed\n"); + + if (bret) { + memset(&ctx, 0xcc, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_ALL; + bret = GetThreadContext(pi.hThread, &ctx); + ok(bret, "GetThreadContext failed\n"); + if (bret) + { + if (is_arm64ec) + { + ok(!ctx.Dr0, "expected 0, got %Ix\n", ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %Ix\n", ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %Ix\n", ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %Ix\n", ctx.Dr3); + ok(!ctx.Dr7, "expected 0, got %Ix\n", ctx.Dr7); + } + else + { + ok(ctx.Dr0 == 0x12340000, "expected 0x12340000, got %Ix\n", ctx.Dr0); + ok(ctx.Dr1 == 0x12340001, "expected 0x12340001, got %Ix\n", ctx.Dr1); + ok(ctx.Dr2 == 0x12340002, "expected 0x12340002, got %Ix\n", ctx.Dr2); + ok(ctx.Dr3 == 0x12340003, "expected 0x12340003, got %Ix\n", ctx.Dr3); + ok(ctx.Dr7 == 0x155, "expected 0x155, got %Ix\n", ctx.Dr7); + } + } + + memset(&wow64_ctx, 0xcc, sizeof(wow64_ctx)); + wow64_ctx.ContextFlags = WOW64_CONTEXT_ALL; + ret = pRtlWow64GetThreadContext(pi.hThread, &wow64_ctx); + ok(ret == STATUS_SUCCESS, "Wow64GetThreadContext failed with %lx\n", ret); + if (ret == STATUS_SUCCESS) + { + if (is_arm64ec) + { + ok(!wow64_ctx.Dr0, "expected 0, got %lx\n", wow64_ctx.Dr0); + ok(!wow64_ctx.Dr1, "expected 0, got %lx\n", wow64_ctx.Dr1); + ok(!wow64_ctx.Dr2, "expected 0, got %lx\n", wow64_ctx.Dr2); + ok(!wow64_ctx.Dr3, "expected 0, got %lx\n", wow64_ctx.Dr3); + ok(!wow64_ctx.Dr7, "expected 0, got %lx\n", wow64_ctx.Dr7); + } + else + { + ok(wow64_ctx.Dr0 == 0x12340000, "expected 0x12340000, got %lx\n", wow64_ctx.Dr0); + ok(wow64_ctx.Dr1 == 0x12340001, "expected 0x12340001, got %lx\n", wow64_ctx.Dr1); + ok(wow64_ctx.Dr2 == 0x12340002, "expected 0x12340002, got %lx\n", wow64_ctx.Dr2); + ok(wow64_ctx.Dr3 == 0x12340003, "expected 0x12340003, got %lx\n", wow64_ctx.Dr3); + ok(wow64_ctx.Dr7 == 0x155, "expected 0x155, got %lx\n", wow64_ctx.Dr7); + } + } } - vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler); - ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + wow64_ctx.Dr0 = 0x56780000; + wow64_ctx.Dr1 = 0x56780001; + wow64_ctx.Dr2 = 0x56780002; + wow64_ctx.Dr3 = 0x56780003; + wow64_ctx.Dr7 = 0x101; /* enable only the first breakpoint */ + ret = pRtlWow64SetThreadContext(pi.hThread, &wow64_ctx); + ok(ret == STATUS_SUCCESS, "Wow64SetThreadContext failed with %lx\n", ret); - outputdebugstring_exceptions = 0; - OutputDebugStringA("Hello World"); - - ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n", - outputdebugstring_exceptions, numexc); - - pRtlRemoveVectoredExceptionHandler(vectored_handler); -} - -static DWORD ripevent_exceptions; - -static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) -{ - PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); - - ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n", - rec->ExceptionCode, DBG_RIPEXCEPTION); - ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters); - ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n", - (NTSTATUS)rec->ExceptionInformation[0], 0x11223344); - ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n", - (NTSTATUS)rec->ExceptionInformation[1], 0x55667788); - - ripevent_exceptions++; - return (rec->ExceptionCode == DBG_RIPEXCEPTION) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; -} - -static void test_ripevent(DWORD numexc) -{ - EXCEPTION_RECORD record; - PVOID vectored_handler; - - if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException) + memset(&wow64_ctx, 0xcc, sizeof(wow64_ctx)); + wow64_ctx.ContextFlags = WOW64_CONTEXT_ALL; + ret = pRtlWow64GetThreadContext(pi.hThread, &wow64_ctx); + ok(ret == STATUS_SUCCESS, "Wow64GetThreadContext failed with %lx\n", ret); + if (ret == STATUS_SUCCESS) { - skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n"); - return; + ok(wow64_ctx.Dr0 == 0x56780000, "expected 0x56780000, got %lx\n", wow64_ctx.Dr0); + ok(wow64_ctx.Dr1 == 0x56780001, "expected 0x56780001, got %lx\n", wow64_ctx.Dr1); + ok(wow64_ctx.Dr2 == 0x56780002, "expected 0x56780002, got %lx\n", wow64_ctx.Dr2); + ok(wow64_ctx.Dr3 == 0x56780003, "expected 0x56780003, got %lx\n", wow64_ctx.Dr3); + ok(wow64_ctx.Dr7 == 0x101, "expected 0x101, got %lx\n", wow64_ctx.Dr7); } - vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler); - ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + memset(&ctx, 0xcc, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_ALL; + bret = GetThreadContext(pi.hThread, &ctx); + ok(bret, "GetThreadContext failed\n"); + if (bret) + { + if (is_arm64ec) + { + ok(!ctx.Dr0, "expected 0, got %Ix\n", ctx.Dr0); + ok(!ctx.Dr1, "expected 0, got %Ix\n", ctx.Dr1); + ok(!ctx.Dr2, "expected 0, got %Ix\n", ctx.Dr2); + ok(!ctx.Dr3, "expected 0, got %Ix\n", ctx.Dr3); + ok(!ctx.Dr7, "expected 0, got %Ix\n", ctx.Dr7); + } + else + { + ok(ctx.Dr0 == 0x56780000, "expected 0x56780000, got %Ix\n", ctx.Dr0); + ok(ctx.Dr1 == 0x56780001, "expected 0x56780001, got %Ix\n", ctx.Dr1); + ok(ctx.Dr2 == 0x56780002, "expected 0x56780002, got %Ix\n", ctx.Dr2); + ok(ctx.Dr3 == 0x56780003, "expected 0x56780003, got %Ix\n", ctx.Dr3); + ok(ctx.Dr7 == 0x101, "expected 0x101, got %Ix\n", ctx.Dr7); + } + } - record.ExceptionCode = DBG_RIPEXCEPTION; - record.ExceptionFlags = 0; - record.ExceptionRecord = NULL; - record.ExceptionAddress = NULL; - record.NumberParameters = 2; - record.ExceptionInformation[0] = 0x11223344; - record.ExceptionInformation[1] = 0x55667788; - - ripevent_exceptions = 0; - pRtlRaiseException(&record); - ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n", - ripevent_exceptions, numexc); - - pRtlRemoveVectoredExceptionHandler(vectored_handler); + ResumeThread(pi.hThread); + TerminateProcess(pi.hProcess, 0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); } +#endif + static DWORD debug_service_exceptions; static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo) { EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); - ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n", + ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08lx instead of %08lx\n", rec->ExceptionCode, EXCEPTION_BREAKPOINT); #ifdef __i386__ ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c, - "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip); + "expected Eip = %lx, got %lx\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip); ok(rec->NumberParameters == (is_wow64 ? 1 : 3), - "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); + "ExceptionParameters is %ld instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax, - "expected ExceptionInformation[0] = %x, got %lx\n", + "expected ExceptionInformation[0] = %lx, got %Ix\n", ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]); if (!is_wow64) { ok(rec->ExceptionInformation[1] == 0x11111111, - "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]); + "got ExceptionInformation[1] = %Ix\n", rec->ExceptionInformation[1]); ok(rec->ExceptionInformation[2] == 0x22222222, - "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]); + "got ExceptionInformation[2] = %Ix\n", rec->ExceptionInformation[2]); } #else ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f, - "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip); + "expected Rip = %Ix, got %Ix\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip); ok(rec->NumberParameters == 1, - "ExceptionParameters is %d instead of 1\n", rec->NumberParameters); + "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters); ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax, - "expected ExceptionInformation[0] = %lx, got %lx\n", + "expected ExceptionInformation[0] = %Ix, got %Ix\n", ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]); #endif @@ -2481,28 +8722,28 @@ static void test_debug_service(DWORD numexc) debug_service_exceptions = 0; ret = func(0); ok(debug_service_exceptions == expected_exc, - "BREAKPOINT_BREAK generated %u exceptions, expected %u\n", + "BREAKPOINT_BREAK generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_BREAK returned %lu, expected %lu\n", ret, expected_ret); /* BREAKPOINT_PROMPT */ debug_service_exceptions = 0; ret = func(2); ok(debug_service_exceptions == expected_exc, - "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n", + "BREAKPOINT_PROMPT generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_PROMPT returned %lu, expected %lu\n", ret, expected_ret); /* invalid debug service */ debug_service_exceptions = 0; ret = func(6); ok(debug_service_exceptions == expected_exc, - "invalid debug service generated %u exceptions, expected %u\n", + "invalid debug service generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "invalid debug service returned %u, expected %u\n", ret, expected_ret); + "invalid debug service returned %lu, expected %lu\n", ret, expected_ret); expected_exc = (is_wow64 ? numexc : 0); expected_ret = (is_wow64 && numexc); @@ -2511,77 +8752,383 @@ static void test_debug_service(DWORD numexc) debug_service_exceptions = 0; ret = func(1); ok(debug_service_exceptions == expected_exc, - "BREAKPOINT_PRINT generated %u exceptions, expected %u\n", + "BREAKPOINT_PRINT generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_PRINT returned %lu, expected %lu\n", ret, expected_ret); /* BREAKPOINT_LOAD_SYMBOLS */ debug_service_exceptions = 0; ret = func(3); ok(debug_service_exceptions == expected_exc, - "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n", + "BREAKPOINT_LOAD_SYMBOLS generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_LOAD_SYMBOLS returned %lu, expected %lu\n", ret, expected_ret); /* BREAKPOINT_UNLOAD_SYMBOLS */ debug_service_exceptions = 0; ret = func(4); ok(debug_service_exceptions == expected_exc, - "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n", + "BREAKPOINT_UNLOAD_SYMBOLS generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret, - "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_UNLOAD_SYMBOLS returned %lu, expected %lu\n", ret, expected_ret); /* BREAKPOINT_COMMAND_STRING */ debug_service_exceptions = 0; ret = func(5); ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc), - "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n", + "BREAKPOINT_COMMAND_STRING generated %lu exceptions, expected %lu\n", debug_service_exceptions, expected_exc); ok(ret == expected_ret || broken(ret == (numexc != 0)), - "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret); + "BREAKPOINT_COMMAND_STRING returned %lu, expected %lu\n", ret, expected_ret); pRtlRemoveVectoredExceptionHandler(vectored_handler); } +#endif /* defined(__i386__) || defined(__x86_64__) */ + +static DWORD outputdebugstring_exceptions_ansi; +static DWORD outputdebugstring_exceptions_unicode; + +static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + switch (rec->ExceptionCode) + { + case DBG_PRINTEXCEPTION_C: + ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + outputdebugstring_exceptions_ansi++; + break; + case DBG_PRINTEXCEPTION_WIDE_C: + ok(outputdebugstring_exceptions_ansi == 0, "Unicode exception should come first\n"); + ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]); + ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"), + "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]); + outputdebugstring_exceptions_unicode++; + break; + default: + ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode); + break; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi, + DWORD numexc_unicode_low, DWORD numexc_unicode_high) +{ + PVOID vectored_handler; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + outputdebugstring_exceptions_ansi = outputdebugstring_exceptions_unicode = 0; + + if (unicode) + OutputDebugStringW(L"Hello World"); + else + OutputDebugStringA("Hello World"); + + todo_wine_if(todo_ansi) + ok(outputdebugstring_exceptions_ansi == numexc_ansi, + "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n", + unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi); + ok(outputdebugstring_exceptions_unicode >= numexc_unicode_low && + outputdebugstring_exceptions_unicode <= numexc_unicode_high, + "OutputDebugString%c generated %lu unicode exceptions, expected %ld-%ld\n", + unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode_low, numexc_unicode_high); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static DWORD outputdebugstring_exceptions_newmodel_order; +static DWORD outputdebugstring_newmodel_return; + +static LONG CALLBACK outputdebugstring_new_model_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + switch (rec->ExceptionCode) + { + case DBG_PRINTEXCEPTION_C: + ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + outputdebugstring_exceptions_newmodel_order = + (outputdebugstring_exceptions_newmodel_order << 8) | 'A'; + break; + case DBG_PRINTEXCEPTION_WIDE_C: + ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]); + ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"), + "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]); + ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]); + ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"), + "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]); + outputdebugstring_exceptions_newmodel_order = + (outputdebugstring_exceptions_newmodel_order << 8) | 'W'; + break; + default: + ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode); + break; + } + + return outputdebugstring_newmodel_return; +} + +static void test_outputdebugstring_newmodel(void) +{ + PVOID vectored_handler; + struct + { + /* input */ + BOOL unicode; + DWORD ret_code; + /* expected output */ + DWORD exceptions_order; + } + tests[] = + { + {FALSE, EXCEPTION_CONTINUE_EXECUTION, 'A'}, + {FALSE, EXCEPTION_CONTINUE_SEARCH, 'A'}, + {TRUE, EXCEPTION_CONTINUE_EXECUTION, 'W'}, + {TRUE, EXCEPTION_CONTINUE_SEARCH, ('W' << 8) | 'A'}, + }; + int i; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_new_model_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + outputdebugstring_exceptions_newmodel_order = 0; + outputdebugstring_newmodel_return = tests[i].ret_code; + + if (tests[i].unicode) + OutputDebugStringW(L"Hello World"); + else + OutputDebugStringA("Hello World"); + + ok(outputdebugstring_exceptions_newmodel_order == tests[i].exceptions_order, + "OutputDebugString%c/%u generated exceptions %04lxs, expected %04lx\n", + tests[i].unicode ? 'W' : 'A', i, + outputdebugstring_exceptions_newmodel_order, tests[i].exceptions_order); + } + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static DWORD ripevent_exceptions; + +static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; + + ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08lx instead of %08lx\n", + rec->ExceptionCode, DBG_RIPEXCEPTION); + ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08lx instead of %08x\n", + (NTSTATUS)rec->ExceptionInformation[0], 0x11223344); + ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08lx instead of %08x\n", + (NTSTATUS)rec->ExceptionInformation[1], 0x55667788); + + ripevent_exceptions++; + return (rec->ExceptionCode == DBG_RIPEXCEPTION) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; +} + +static void test_ripevent(DWORD numexc) +{ + EXCEPTION_RECORD record; + PVOID vectored_handler; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler); + ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); + + record.ExceptionCode = DBG_RIPEXCEPTION; + record.ExceptionFlags = 0; + record.ExceptionRecord = NULL; + record.ExceptionAddress = NULL; + record.NumberParameters = 2; + record.ExceptionInformation[0] = 0x11223344; + record.ExceptionInformation[1] = 0x55667788; + + ripevent_exceptions = 0; + pRtlRaiseException(&record); + ok(ripevent_exceptions == numexc, "RtlRaiseException generated %ld exceptions, expected %ld\n", + ripevent_exceptions, numexc); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static void subtest_fastfail(unsigned int code) +{ + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + DEBUG_EVENT de; + DWORD continuestatus; + BOOL ret; + BOOL had_ff = FALSE, had_se = FALSE; + + sprintf(cmdline, "%s %s %s %u", my_argv[0], my_argv[1], "fastfail", code); + si.cb = sizeof(si); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "could not create child process error: %lu\n", GetLastError()); + if (!ret) + return; + + do + { + continuestatus = DBG_CONTINUE; + ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + + if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) + { + if (de.u.Exception.ExceptionRecord.ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) + { + ok(!de.u.Exception.dwFirstChance, "must be a second chance exception\n"); + ok(de.u.Exception.ExceptionRecord.NumberParameters == 1 || broken(is_arm64ec), + "expected exactly one parameter, got %lu\n", + de.u.Exception.ExceptionRecord.NumberParameters); + if (de.u.Exception.ExceptionRecord.NumberParameters >= 1) + ok(de.u.Exception.ExceptionRecord.ExceptionInformation[0] == code, + "expected %u for code, got %Iu\n", + code, de.u.Exception.ExceptionRecord.ExceptionInformation[0]); + had_ff = TRUE; + } + + if (de.u.Exception.dwFirstChance) + { + continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } + else + { + had_se = TRUE; + pNtTerminateProcess(pi.hProcess, 0); + } + } + + ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus); + + } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + ok(had_ff || broken(had_se) /* Win7 */, "fast fail did not occur\n"); + + wait_child_process( pi.hProcess ); + ret = CloseHandle(pi.hThread); + ok(ret, "error %lu\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "error %lu\n", GetLastError()); + + return; +} + +static void test_fastfail(void) +{ + unsigned int codes[] = { + FAST_FAIL_LEGACY_GS_VIOLATION, + FAST_FAIL_VTGUARD_CHECK_FAILURE, + FAST_FAIL_STACK_COOKIE_CHECK_FAILURE, + FAST_FAIL_CORRUPT_LIST_ENTRY, + FAST_FAIL_INCORRECT_STACK, + FAST_FAIL_INVALID_ARG, + FAST_FAIL_GS_COOKIE_INIT, + FAST_FAIL_FATAL_APP_EXIT, + FAST_FAIL_INVALID_FAST_FAIL_CODE, + 0xdeadbeefUL, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(codes); i++) + { + winetest_push_context("__fastfail(%#x)", codes[i]); + subtest_fastfail(codes[i]); + winetest_pop_context(); + } +} static DWORD breakpoint_exceptions; static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo) { EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); - ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n", + ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08lx instead of %08lx\n", rec->ExceptionCode, EXCEPTION_BREAKPOINT); #ifdef __i386__ ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1, - "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip); + "expected Eip = %lx, got %lx\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip); ok(rec->NumberParameters == (is_wow64 ? 1 : 3), - "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); + "ExceptionParameters is %ld instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3); ok(rec->ExceptionInformation[0] == 0, - "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]); + "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]); ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2; -#else +#elif defined(__x86_64__) ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1, - "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip); + "expected Rip = %Ix, got %Ix\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip); ok(rec->NumberParameters == 1, - "ExceptionParameters is %d instead of 1\n", rec->NumberParameters); + "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters); ok(rec->ExceptionInformation[0] == 0, - "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]); + "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]); ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2; +#elif defined(__arm__) + ok(ExceptionInfo->ContextRecord->Pc == (DWORD)code_mem + 1, + "expected pc = %lx, got %lx\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Pc); + ok(rec->NumberParameters == 1, + "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 0, + "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]); + ExceptionInfo->ContextRecord->Pc += 2; +#elif defined(__aarch64__) + ok(ExceptionInfo->ContextRecord->Pc == (DWORD_PTR)code_mem, + "expected pc = %p, got %p\n", code_mem, (void *)ExceptionInfo->ContextRecord->Pc); + ok(rec->NumberParameters == 1, + "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters); + ok(rec->ExceptionInformation[0] == 0, + "got ExceptionInformation[0] = %p\n", (void *)rec->ExceptionInformation[0]); + ExceptionInfo->ContextRecord->Pc += 4; #endif breakpoint_exceptions++; return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; } -static const BYTE breakpoint_code[] = { - 0xcd, 0x03, /* int $0x3 */ - 0xc3, /* ret */ -}; +#if defined(__i386__) || defined(__x86_64__) +static const BYTE breakpoint_code[] = { 0xcd, 0x03, 0xc3 }; /* int $0x3; ret */ +#elif defined(__arm__) +static const DWORD breakpoint_code[] = { 0xdefe, 0x4770 }; /* udf #0xfe; bx lr */ +#elif defined(__aarch64__) +static const DWORD breakpoint_code[] = { 0xd43e0000, 0xd65f03c0 }; /* brk #0xf000; ret */ +#endif static void test_breakpoint(DWORD numexc) { @@ -2589,38 +9136,130 @@ static void test_breakpoint(DWORD numexc) void *vectored_handler; memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code)); - +#ifdef __arm__ + func = (void *)((char *)code_mem + 1); /* thumb */ +#endif vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler); ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); breakpoint_exceptions = 0; func(); - ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n", + ok(breakpoint_exceptions == numexc, "int $0x3 generated %lu exceptions, expected %lu\n", breakpoint_exceptions, numexc); pRtlRemoveVectoredExceptionHandler(vectored_handler); } +#if defined(__i386__) || defined(__x86_64__) +static BYTE except_code_set_ymm0[] = +{ +#ifdef __x86_64__ + 0x48, +#endif + 0xb8, /* mov imm,%ax */ + 0x00, 0x00, 0x00, 0x00, +#ifdef __x86_64__ + 0x00, 0x00, 0x00, 0x00, +#endif + + 0xc5, 0xfc, 0x10, 0x00, /* vmovups (%ax),%ymm0 */ + 0xcc, /* int3 */ + 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */ + 0xc3, /* ret */ +}; + +static void test_debuggee_xstate(void) +{ + void (CDECL *func)(void) = code_mem; + unsigned int address_offset, i; + unsigned int data[8]; + + if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX)) + { + memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code)); + func(); + return; + } + + memcpy(code_mem, except_code_set_ymm0, sizeof(except_code_set_ymm0)); + address_offset = sizeof(void *) == 8 ? 2 : 1; + *(void **)((BYTE *)code_mem + address_offset) = data; + + for (i = 0; i < ARRAY_SIZE(data); ++i) + data[i] = i + 1; + + func(); + + for (i = 0; i < 4; ++i) + ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x28282828), + "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i); + + for ( ; i < ARRAY_SIZE(data); ++i) + ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x48484848) + || broken(test_stage == STAGE_XSTATE_LEGACY_SSE && data[i] == i + 1) /* Win7 */, + "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i); +} + +static BYTE except_code_segments[] = +{ + 0x8c, 0xc0, /* mov %es,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xd8, /* mov %ds,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xe0, /* mov %fs,%eax */ + 0x50, /* push %rax */ + 0x8c, 0xe8, /* mov %gs,%eax */ + 0x50, /* push %rax */ + 0x31, 0xc0, /* xor %eax,%eax */ + 0x8e, 0xc0, /* mov %eax,%es */ + 0x8e, 0xd8, /* mov %eax,%ds */ + 0x8e, 0xe0, /* mov %eax,%fs */ + 0x8e, 0xe8, /* mov %eax,%gs */ + 0xcc, /* int3 */ + 0x58, /* pop %rax */ + 0x8e, 0xe8, /* mov %eax,%gs */ + 0x58, /* pop %rax */ + 0x8e, 0xe0, /* mov %eax,%fs */ + 0x58, /* pop %rax */ + 0x8e, 0xd8, /* mov %eax,%ds */ + 0x58, /* pop %rax */ + 0x8e, 0xc0, /* mov %eax,%es */ + 0xc3, /* retq */ +}; + +static void test_debuggee_segments(void) +{ + void (CDECL *func)(void) = code_mem; + + memcpy( code_mem, except_code_segments, sizeof(except_code_segments)); + func(); +} +#endif + static DWORD invalid_handle_exceptions; static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) { PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord; - trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress); - ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08x instead of %08x\n", + ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08lx instead of %08lx\n", rec->ExceptionCode, EXCEPTION_INVALID_HANDLE); - ok(rec->NumberParameters == 0, "ExceptionParameters is %d instead of 0\n", rec->NumberParameters); + ok(rec->NumberParameters == 0, "ExceptionParameters is %ld instead of 0\n", rec->NumberParameters); invalid_handle_exceptions++; return (rec->ExceptionCode == EXCEPTION_INVALID_HANDLE) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; } -static void test_closehandle(DWORD numexc) +static inline BOOL is_magic_handle(HANDLE handle) { + return HandleToLong(handle) >= ~5 && HandleToLong(handle) <= ~0; +} + +static void test_closehandle(DWORD numexc, HANDLE handle) +{ + NTSTATUS status, expect; PVOID vectored_handler; - NTSTATUS status; - DWORD res; + BOOL ret, expectret; if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException) { @@ -2632,18 +9271,24 @@ static void test_closehandle(DWORD numexc) ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n"); invalid_handle_exceptions = 0; - res = CloseHandle((HANDLE)0xdeadbeef); - ok(!res, "CloseHandle(0xdeadbeef) unexpectedly succeeded\n"); - ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error code %d instead of %d\n", - GetLastError(), ERROR_INVALID_HANDLE); - ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n", - invalid_handle_exceptions, numexc); + expectret = is_magic_handle(handle) || broken(numexc && sizeof(handle) == 4); /* < Win10 */ + ret = CloseHandle(handle); + ok(ret || (GetLastError() == ERROR_INVALID_HANDLE), + "CloseHandle had wrong GetLastError(), got %lu for %p\n", GetLastError(), handle); + ok(ret == expectret || broken(HandleToLong(handle) < 0) /* < Win10 */, + "CloseHandle expected %d, got %d for %p\n", expectret, ret, handle); + ok(invalid_handle_exceptions == numexc || broken(!numexc && is_magic_handle(handle)), /* < Win10 */ + "CloseHandle generated %ld exceptions, expected %ld for %p\n", + invalid_handle_exceptions, numexc, handle); invalid_handle_exceptions = 0; - status = pNtClose((HANDLE)0xdeadbeef); - ok(status == STATUS_INVALID_HANDLE, "NtClose(0xdeadbeef) returned status %08x\n", status); - ok(invalid_handle_exceptions == numexc, "NtClose generated %d exceptions, expected %d\n", - invalid_handle_exceptions, numexc); + expect = expectret ? STATUS_SUCCESS : STATUS_INVALID_HANDLE; + status = pNtClose(handle); + ok(status == expect || broken(HandleToLong(handle) < 0), /* < Win10 */ + "NtClose returned unexpected status %#lx, expected %#lx for %p\n", status, expect, handle); + ok(invalid_handle_exceptions == numexc || broken(!numexc && is_magic_handle(handle)), /* < Win10 */ + "CloseHandle generated %ld exceptions, expected %ld for %p\n", + invalid_handle_exceptions, numexc, handle); pRtlRemoveVectoredExceptionHandler(vectored_handler); } @@ -2684,53 +9329,2621 @@ static void test_vectored_continue_handler(void) ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344); ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n"); } -#endif /* defined(__i386__) || defined(__x86_64__) */ + +static void test_user_apc(void) +{ + NTSTATUS status; + CONTEXT context; + LONG pass; + int ret; + + if (!pNtQueueApcThread) + { + win_skip("NtQueueApcThread is not available.\n"); + return; + } + + pass = 0; + InterlockedIncrement(&pass); +#ifdef __i386__ + { + /* RtlCaptureContext puts the return address of the caller's stack + * frame into %eip, so we need a thunk to get it to return here */ + static const BYTE code[] = + { + 0x55, /* pushl %ebp */ + 0x89, 0xe5, /* movl %esp, %ebp */ + 0xff, 0x75, 0x0c, /* pushl 0xc(%ebp) */ + 0xff, 0x55, 0x08, /* call *0x8(%ebp) */ + 0xc9, /* leave */ + 0xc3, /* ret */ + }; + int (__cdecl *func)(void *capture, CONTEXT *context) = code_mem; + + memcpy(code_mem, code, sizeof(code)); + ret = func(RtlCaptureContext, &context); + /* work around broken RtlCaptureContext on Windows < 7 which doesn't set + * ContextFlags */ + context.ContextFlags = CONTEXT_FULL; + } +#else + { + int (WINAPI *func)(CONTEXT *context) = (void *)RtlCaptureContext; + + ret = func(&context); + } +#endif + InterlockedIncrement(&pass); + + if (pass == 2) + { + /* Try to make sure context data is far enough below context.Esp. */ + CONTEXT c[4]; + +#ifdef __i386__ + context.Eax = 0xabacab; +#elif defined(__x86_64__) + context.Rax = 0xabacab; +#elif defined(__arm__) + context.R0 = 0xabacab; +#elif defined(__aarch64__) + context.X0 = 0xabacab; +#endif + + c[0] = context; + + apc_count = 0; + status = pNtQueueApcThread(GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef); + ok(!status, "Got unexpected status %#lx.\n", status); + SleepEx(0, TRUE); + ok(apc_count == 1, "Test user APC was not called.\n"); + apc_count = 0; + status = pNtQueueApcThread(GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef); + ok(!status, "Got unexpected status %#lx.\n", status); + status = NtContinue(&c[0], TRUE ); + + ok(0, "Should not get here, status %#lx.\n", status); + return; + } + ok(ret == 0xabacab, "Got return value %#x.\n", ret); + ok(pass == 3, "Got unexpected pass %ld.\n", pass); + ok(apc_count > 0, "Test user APC was not called.\n"); +} + +static void test_user_callback(void) +{ + NTSTATUS status = NtCallbackReturn( NULL, 0, STATUS_SUCCESS ); + ok( status == STATUS_NO_CALLBACK_ACTIVE, "failed %lx\n", status ); +} + +static DWORD WINAPI suspend_thread_test( void *arg ) +{ + HANDLE event = arg; + WaitForSingleObject(event, INFINITE); + return 0; +} + +static void test_suspend_count(HANDLE hthread, ULONG expected_count, int line) +{ + static BOOL supported = TRUE; + NTSTATUS status; + ULONG count; + + if (!supported) + return; + + count = ~0u; + status = pNtQueryInformationThread(hthread, ThreadSuspendCount, &count, sizeof(count), NULL); + if (status) + { + win_skip("ThreadSuspendCount is not supported.\n"); + supported = FALSE; + return; + } + + ok_(__FILE__, line)(!status, "Failed to get suspend count, status %#lx.\n", status); + ok_(__FILE__, line)(count == expected_count, "Unexpected suspend count %lu.\n", count); +} + +static void test_suspend_thread(void) +{ +#define TEST_SUSPEND_COUNT(thread, count) test_suspend_count((thread), (count), __LINE__) + HANDLE thread, event; + ULONG count, len; + NTSTATUS status; + DWORD ret; + + status = NtSuspendThread(0, NULL); + ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#lx.\n", status); + + status = NtResumeThread(0, NULL); + ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#lx.\n", status); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + thread = CreateThread(NULL, 0, suspend_thread_test, event, 0, NULL); + ok(thread != NULL, "Failed to create a thread.\n"); + + ret = WaitForSingleObject(thread, 0); + ok(ret == WAIT_TIMEOUT, "Unexpected status %ld.\n", ret); + + status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count), NULL); + if (!status) + { + status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, sizeof(count), NULL); + ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#lx.\n", status); + + status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, NULL); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status); + + len = 123; + status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status); + ok(len == 123, "Unexpected info length %lu.\n", len); + + len = 123; + status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, 0, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status); + ok(len == 123, "Unexpected info length %lu.\n", len); + + count = 10; + status = pNtQueryInformationThread(0, ThreadSuspendCount, &count, sizeof(count), NULL); + ok(status, "Unexpected status %#lx.\n", status); + ok(count == 10, "Unexpected suspend count %lu.\n", count); + } + + status = NtResumeThread(thread, NULL); + ok(!status, "Unexpected status %#lx.\n", status); + + status = NtResumeThread(thread, &count); + ok(!status, "Unexpected status %#lx.\n", status); + ok(count == 0, "Unexpected suspended count %lu.\n", count); + + TEST_SUSPEND_COUNT(thread, 0); + + status = NtSuspendThread(thread, NULL); + ok(!status, "Failed to suspend a thread, status %#lx.\n", status); + + TEST_SUSPEND_COUNT(thread, 1); + + status = NtSuspendThread(thread, &count); + ok(!status, "Failed to suspend a thread, status %#lx.\n", status); + ok(count == 1, "Unexpected suspended count %lu.\n", count); + + TEST_SUSPEND_COUNT(thread, 2); + + status = NtResumeThread(thread, &count); + ok(!status, "Failed to resume a thread, status %#lx.\n", status); + ok(count == 2, "Unexpected suspended count %lu.\n", count); + + TEST_SUSPEND_COUNT(thread, 1); + + status = NtResumeThread(thread, NULL); + ok(!status, "Failed to resume a thread, status %#lx.\n", status); + + TEST_SUSPEND_COUNT(thread, 0); + + SetEvent(event); + WaitForSingleObject(thread, INFINITE); + + CloseHandle(thread); +#undef TEST_SUSPEND_COUNT +} + +static const char *suspend_process_event_name = "suspend_process_event"; +static const char *suspend_process_event2_name = "suspend_process_event2"; + +static DWORD WINAPI dummy_thread_proc( void *arg ) +{ + return 0; +} + +static void suspend_process_proc(void) +{ + HANDLE event = OpenEventA(SYNCHRONIZE, FALSE, suspend_process_event_name); + HANDLE event2 = OpenEventA(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, suspend_process_event2_name); + DWORD count; + NTSTATUS status; + HANDLE thread; + + ok(event != NULL, "Failed to open event handle.\n"); + ok(event2 != NULL, "Failed to open event handle.\n"); + + thread = CreateThread(NULL, 0, dummy_thread_proc, 0, CREATE_SUSPENDED, NULL); + ok(thread != NULL, "Failed to create auxiliary thread.\n"); + + /* Suspend up to limit. */ + while (!(status = NtSuspendThread(thread, NULL))) + ; + ok(status == STATUS_SUSPEND_COUNT_EXCEEDED, "Unexpected status %#lx.\n", status); + + for (;;) + { + SetEvent(event2); + if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0) + break; + } + + status = NtSuspendThread(thread, &count); + ok(!status, "Failed to suspend a thread, status %#lx.\n", status); + ok(count == 125, "Unexpected suspend count %lu.\n", count); + + status = NtResumeThread(thread, NULL); + ok(!status, "Failed to resume a thread, status %#lx.\n", status); + + CloseHandle(event); + CloseHandle(event2); +} + +static void test_suspend_process(void) +{ + PROCESS_INFORMATION info; + char path_name[MAX_PATH]; + STARTUPINFOA startup; + HANDLE event, event2; + NTSTATUS status; + char **argv; + DWORD ret; + + event = CreateEventA(NULL, FALSE, FALSE, suspend_process_event_name); + ok(event != NULL, "Failed to create event.\n"); + + event2 = CreateEventA(NULL, FALSE, FALSE, suspend_process_event2_name); + ok(event2 != NULL, "Failed to create event.\n"); + + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf(path_name, "%s exception suspend_process", argv[0]); + + ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); + ok(ret, "Failed to create target process.\n"); + + /* New process signals this event. */ + ResetEvent(event2); + ret = WaitForSingleObject(event2, INFINITE); + ok(ret == WAIT_OBJECT_0, "Wait failed, %#lx.\n", ret); + + /* Suspend main thread */ + status = NtSuspendThread(info.hThread, &ret); + ok(!status && !ret, "Failed to suspend main thread, status %#lx.\n", status); + + /* Process wasn't suspended yet. */ + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#lx.\n", status); + + status = pNtSuspendProcess(0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#lx.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#lx.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#lx.\n", status); + + status = NtSuspendThread(info.hThread, &ret); + ok(!status && ret == 1, "Failed to suspend main thread, status %#lx.\n", status); + status = NtResumeThread(info.hThread, &ret); + ok(!status && ret == 2, "Failed to resume main thread, status %#lx.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#lx.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#lx.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#lx.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 1000); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + SetEvent(event); + + wait_child_process(info.hProcess); + + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + + CloseHandle(event); + CloseHandle(event2); +} + +static void test_unload_trace(void) +{ + static const WCHAR imageW[] = {'m','s','x','m','l','3','.','d','l','l',0}; + RTL_UNLOAD_EVENT_TRACE *unload_trace, **unload_trace_ex = NULL, *ptr; + ULONG *element_size, *element_count, size; + HMODULE hmod; + BOOL found; + + unload_trace = pRtlGetUnloadEventTrace(); + ok(unload_trace != NULL, "Failed to get unload events pointer.\n"); + + if (pRtlGetUnloadEventTraceEx) + { + pRtlGetUnloadEventTraceEx(&element_size, &element_count, (void **)&unload_trace_ex); + ok(*element_size >= sizeof(*ptr), "Unexpected element size.\n"); + ok(*element_count == RTL_UNLOAD_EVENT_TRACE_NUMBER, "Unexpected trace element count %lu.\n", *element_count); + ok(unload_trace_ex != NULL, "Unexpected pointer %p.\n", unload_trace_ex); + size = *element_size; + } + else + size = sizeof(*unload_trace); + + hmod = LoadLibraryA("msxml3.dll"); + ok(hmod != NULL, "Failed to load library.\n"); + FreeLibrary(hmod); + + found = FALSE; + ptr = unload_trace; + while (ptr->BaseAddress != NULL) + { + if (!lstrcmpW(imageW, ptr->ImageName)) + { + found = TRUE; + break; + } + ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size); + } + ok(found, "Unloaded module wasn't found.\n"); + + if (unload_trace_ex) + { + found = FALSE; + ptr = *unload_trace_ex; + while (ptr->BaseAddress != NULL) + { + if (!lstrcmpW(imageW, ptr->ImageName)) + { + found = TRUE; + break; + } + ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size); + } + ok(found, "Unloaded module wasn't found.\n"); + } +} + +#if defined(__i386__) || defined(__x86_64__) + +static const unsigned int test_extended_context_data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; +static const unsigned test_extended_context_spoil_data1[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; +static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85}; + +static BOOL test_extended_context_modified_state; +static BOOL xsaveopt_enabled, compaction_enabled; +static ULONG64 xstate_supported_features; + +static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame, + CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher) +{ + const ULONG64 expected_compaction_mask = (0x8000000000000000 | xstate_supported_features) & ~(ULONG64)3; + CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1); + unsigned int *context_ymm_data; + DWORD expected_min_offset; + XSTATE *xs; + + ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE), + "Got unexpected ContextFlags %#lx.\n", context->ContextFlags); + + if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE)) + goto done; + +#ifdef __x86_64__ + { + /* Unwind contexts do not inherit xstate information. */ + DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher; + + ok(!(dispatch->ContextRecord->ContextFlags & 0x40), "Got unexpected ContextRecord->ContextFlags %#lx.\n", + dispatch->ContextRecord->ContextFlags); + } +#endif + + ok(xctx->Legacy.Offset == -(int)(sizeof(CONTEXT)), "Got unexpected Legacy.Offset %ld.\n", xctx->Legacy.Offset); + ok(xctx->Legacy.Length == sizeof(CONTEXT), "Got unexpected Legacy.Length %ld.\n", xctx->Legacy.Length); + ok(xctx->All.Offset == -(int)sizeof(CONTEXT), "Got unexpected All.Offset %ld.\n", xctx->All.Offset); + ok(xctx->All.Length == sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length, + "Got unexpected All.Offset %ld.\n", xctx->All.Offset); + expected_min_offset = sizeof(void *) == 8 ? sizeof(CONTEXT_EX) + sizeof(EXCEPTION_RECORD) : sizeof(CONTEXT_EX); + ok(xctx->XState.Offset >= expected_min_offset, + "Got unexpected XState.Offset %ld.\n", xctx->XState.Offset); + ok(xctx->XState.Length >= sizeof(XSTATE), "Got unexpected XState.Length %ld.\n", xctx->XState.Length); + + xs = (XSTATE *)((char *)xctx + xctx->XState.Offset); + context_ymm_data = (unsigned int *)&xs->YmmContext; + ok(!((ULONG_PTR)xs % 64), "Got unexpected xs %p.\n", xs); + + if (compaction_enabled) + ok((xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask, + "Got compaction mask %#I64x, expected %#I64x.\n", xs->CompactionMask, expected_compaction_mask); + else + ok(!xs->CompactionMask, "Got compaction mask %#I64x.\n", xs->CompactionMask); + + if (test_extended_context_modified_state) + { + ok((xs->Mask & 7) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(!memcmp(context_ymm_data, test_extended_context_data + 4, sizeof(M128A)), + "Got unexpected context data.\n"); + } + else + { + ok((xs->Mask & 7) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + /* The save area has garbage if xsaveopt is available, so we can't test + * its contents. */ + + /* Clear the mask; the state should be restored to INIT_STATE without + * using this data. */ + xs->Mask = 0; + memcpy(context_ymm_data, test_extended_context_spoil_data1 + 4, sizeof(M128A)); + } + +done: +#ifdef __GNUC__ + __asm__ volatile("vmovups (%0),%%ymm0" : : "r"(test_extended_context_spoil_data2)); +#endif +#ifdef __x86_64__ + ++context->Rip; +#else + if (*(BYTE *)context->Eip == 0xcc) + ++context->Eip; +#endif + return ExceptionContinueExecution; +} + +struct call_func_offsets +{ + unsigned int func_addr; + unsigned int func_param1; + unsigned int func_param2; + unsigned int ymm0_save; +}; +#ifdef __x86_64__ +static BYTE call_func_code_set_ymm0[] = +{ + 0x55, /* pushq %rbp */ + 0x48, 0xb8, /* mov imm,%rax */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xb9, /* mov imm,%rcx */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xba, /* mov imm,%rdx */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xbd, /* mov imm,%rbp */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%rbp),%ymm0 */ + 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */ + 0xff, 0xd0, /* call *rax */ + 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */ + 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */ + 0x5d, /* popq %rbp */ + 0xc3, /* ret */ +}; +static BYTE call_func_code_reset_ymm_state[] = +{ + 0x55, /* pushq %rbp */ + 0x48, 0xb8, /* mov imm,%rax */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xb9, /* mov imm,%rcx */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xba, /* mov imm,%rdx */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x48, 0xbd, /* mov imm,%rbp */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xc5, 0xf8, 0x77, /* vzeroupper */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */ + 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */ + 0xff, 0xd0, /* call *rax */ + 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */ + 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */ + 0x5d, /* popq %rbp */ + 0xc3, /* ret */ +}; +static const struct call_func_offsets call_func_offsets = {3, 13, 23, 33}; +#else +static BYTE call_func_code_set_ymm0[] = +{ + 0x55, /* pushl %ebp */ + 0xb8, /* mov imm,%eax */ + 0x00, 0x00, 0x00, 0x00, + + 0xb9, /* mov imm,%ecx */ + 0x00, 0x00, 0x00, 0x00, + + 0xba, /* mov imm,%edx */ + 0x00, 0x00, 0x00, 0x00, + + 0xbd, /* mov imm,%ebp */ + 0x00, 0x00, 0x00, 0x00, + + 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde, + /* cmpl $0xdeadbeef, %edx */ + 0x74, 0x01, /* je 1f */ + 0x52, /* pushl %edx */ + 0x51, /* 1: pushl %ecx */ + 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%ebp),%ymm0 */ + 0xff, 0xd0, /* call *eax */ + 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */ + 0x5d, /* popl %ebp */ + 0xc3, /* ret */ +}; +static BYTE call_func_code_reset_ymm_state[] = +{ + 0x55, /* pushl %ebp */ + 0xb8, /* mov imm,%eax */ + 0x00, 0x00, 0x00, 0x00, + + 0xb9, /* mov imm,%ecx */ + 0x00, 0x00, 0x00, 0x00, + + 0xba, /* mov imm,%edx */ + 0x00, 0x00, 0x00, 0x00, + + 0xbd, /* mov imm,%ebp */ + 0x00, 0x00, 0x00, 0x00, + + 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde, + /* cmpl $0xdeadbeef, %edx */ + 0x74, 0x01, /* je 1f */ + 0x52, /* pushl %edx */ + 0x51, /* 1: pushl %ecx */ + 0xc5, 0xf8, 0x77, /* vzeroupper */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */ + 0xff, 0xd0, /* call *eax */ + 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */ + 0x5d, /* popl %ebp */ + 0xc3, /* ret */ +}; +static const struct call_func_offsets call_func_offsets = {2, 7, 12, 17}; +#endif + +static DWORD WINAPI test_extended_context_thread(void *arg) +{ + ULONG (WINAPI* func)(void) = code_mem; + static unsigned int data[8]; + unsigned int i; + + memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state)); + *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread; + *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef; + *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data; + func(); + + for (i = 0; i < 4; ++i) + ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + for (; i < 8; ++i) + ok(data[i] == 0x48484848, "Got unexpected data %#x, i %u.\n", data[i], i); + memset(data, 0x68, sizeof(data)); + + memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0)); + *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread; + *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef; + *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data; + func(); + + memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state)); + *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread; + *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef; + *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data; + func(); + return 0; +} + +static void wait_for_thread_next_suspend(HANDLE thread) +{ + DWORD result; + + result = ResumeThread(thread); + ok(result == 1, "Got unexpected suspend count %lu.\n", result); + + /* NtQueryInformationThread(ThreadSuspendCount, ...) is not supported on older Windows. */ + while (!(result = SuspendThread(thread))) + { + ResumeThread(thread); + Sleep(1); + } + ok(result == 1, "Got unexpected suspend count %lu.\n", result); + result = ResumeThread(thread); + ok(result == 2, "Got unexpected suspend count %lu.\n", result); +} + +#define CONTEXT_NATIVE (CONTEXT_XSTATE & CONTEXT_CONTROL) + +struct context_parameters +{ + ULONG flag; + ULONG supported_flags; + ULONG broken_flags; + ULONG context_length; + ULONG legacy_length; + ULONG context_ex_length; + ULONG align; + ULONG flags_offset; + ULONG xsavearea_offset; + ULONG vector_reg_count; +}; + +static void test_extended_context(void) +{ + static BYTE except_code_reset_ymm_state[] = + { +#ifdef __x86_64__ + 0x48, +#endif + 0xb8, /* mov imm,%ax */ + 0x00, 0x00, 0x00, 0x00, +#ifdef __x86_64__ + 0x00, 0x00, 0x00, 0x00, +#endif + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xc5, 0xf8, 0x77, /* vzeroupper */ + 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */ + 0xcc, /* int3 */ + 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */ + 0xc3, /* ret */ + }; + + static const struct context_parameters context_arch_old[] = + { + { + 0x00100000, /* CONTEXT_AMD64 */ + 0xd800005f, + 0xd8000000, + 0x4d0, /* sizeof(CONTEXT) */ + 0x4d0, /* sizeof(CONTEXT) */ + 0x20, /* sizeof(CONTEXT_EX) */ + 7, + 0x30, + 0x100, /* offsetof(CONTEXT, FltSave) */ + 16, + }, + { + 0x00010000, /* CONTEXT_X86 */ + 0xd800007f, + 0xd8000000, + 0x2cc, /* sizeof(CONTEXT) */ + 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */ + 0x18, /* sizeof(CONTEXT_EX) */ + 3, + 0, + 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */ + 8, + }, + }; + + static const struct context_parameters context_arch_new[] = + { + { + 0x00100000, /* CONTEXT_AMD64 */ + 0xf800005f, + 0xf8000000, + 0x4d0, /* sizeof(CONTEXT) */ + 0x4d0, /* sizeof(CONTEXT) */ + 0x20, /* sizeof(CONTEXT_EX) */ + 15, + 0x30, + 0x100, /* offsetof(CONTEXT, FltSave) */ + 16, + }, + { + 0x00010000, /* CONTEXT_X86 */ + 0xf800007f, + 0xf8000000, + 0x2cc, /* sizeof(CONTEXT) */ + 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */ + 0x20, /* sizeof(CONTEXT_EX) */ + 3, + 0, + 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */ + 8, + }, + }; + const struct context_parameters *context_arch; + + const ULONG64 supported_features = 0xff; + const ULONG64 supported_compaction_mask = supported_features | ((ULONG64)1 << 63); + ULONG expected_length, expected_length_xstate, context_flags, expected_offset, max_xstate_length; + ULONG64 enabled_features, expected_compaction; + DECLSPEC_ALIGN(64) BYTE context_buffer2[4096]; + DECLSPEC_ALIGN(64) BYTE context_buffer[4096]; + unsigned int i, j, address_offset, test; + ULONG ret, ret2, length, length2, align; + ULONG flags, flags_fpx, expected_flags; + ULONG (WINAPI* func)(void) = code_mem; + CONTEXT_EX *context_ex; + CONTEXT *context; + unsigned data[8]; + HANDLE thread; + ULONG64 mask; + XSTATE *xs; + BOOL bret; + void *p; + + address_offset = sizeof(void *) == 8 ? 2 : 1; + *(void **)(except_code_set_ymm0 + address_offset) = data; + *(void **)(except_code_reset_ymm_state + address_offset) = data; + + if (!pRtlGetEnabledExtendedFeatures) + { + win_skip("RtlGetEnabledExtendedFeatures is not available.\n"); + return; + } + + enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0); + +#ifndef __arm64ec__ + if (enabled_features) + { + int regs[4]; + + __cpuidex(regs, 0xd, 1); + xsaveopt_enabled = regs[0] & 1; + compaction_enabled = regs[0] & 2; + } +#endif + xstate_supported_features = enabled_features & supported_features; + + /* Test context manipulation functions. */ + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength(0, &length); + ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#lx, length %#lx.\n", + ret, length); + + ret = pRtlGetExtendedContextLength(context_arch_new[0].flag, &length); + ok(!ret, "Got %#lx.\n", ret); + if (length == context_arch_new[0].context_length + context_arch_new[0].context_ex_length + + context_arch_new[0].align) + context_arch = context_arch_new; + else + context_arch = context_arch_old; + + for (test = 0; test < 2; ++test) + { + expected_length = context_arch[test].context_length + context_arch[test].context_ex_length + + context_arch[test].align; + expected_length_xstate = context_arch[test].context_length + context_arch[test].context_ex_length + + sizeof(XSTATE) + 63; + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength(context_arch[test].flag, &length); + ok(!ret && length == expected_length, "Got unexpected result ret %#lx, length %#lx.\n", + ret, length); + + for (i = 0; i < 32; ++i) + { + if (i == 6) /* CONTEXT_XSTATE */ + continue; + + flags = context_arch[test].flag | (1 << i); + length = length2 = 0xdeadbeef; + ret = pRtlGetExtendedContextLength(flags, &length); + + if ((context_arch[test].supported_flags & flags) || flags == context_arch[test].flag) + { + ok((!ret && length == expected_length) + || broken((context_arch[test].broken_flags & (1 << i)) + && ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef), + "Got unexpected result ret %#lx, length %#lx, flags 0x%08lx.\n", + ret, length, flags); + } + else + { + ok((ret == STATUS_INVALID_PARAMETER || ret == STATUS_NOT_SUPPORTED) && length == 0xdeadbeef, + "Got unexpected result ret %#lx, length %#lx, flags 0x%08lx.\n", ret, length, flags); + } + + SetLastError(0xdeadbeef); + bret = pInitializeContext(NULL, flags, NULL, &length2); + ok(!bret && length2 == length && GetLastError() + == (!ret ? ERROR_INSUFFICIENT_BUFFER + : (ret == STATUS_INVALID_PARAMETER ? ERROR_INVALID_PARAMETER : ERROR_NOT_SUPPORTED)), + "Got unexpected bret %#x, length2 %#lx, GetLastError() %lu, flags %#lx.\n", + bret, length2, GetLastError(), flags); + + if (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_NOT_SUPPORTED) + continue; + + SetLastError(0xdeadbeef); + context = (void *)0xdeadbeef; + length2 = expected_length - 1; + bret = pInitializeContext(context_buffer, flags, &context, &length2); + ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(context == (void *)0xdeadbeef, "Got unexpected context %p.\n", context); + + SetLastError(0xdeadbeef); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + length2 = expected_length; + bret = pInitializeContext(context_buffer, flags, &context, &length2); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(length2 == expected_length, "Got unexpected length %#lx.\n", length); + ok((BYTE *)context == context_buffer, "Got unexpected context %p, flags %#lx.\n", context, flags); + + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length); + ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags); + ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length + : context_arch[test].legacy_length), + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags); + ok(context_ex->All.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags); + + /* No extra 8 bytes in x64 CONTEXT_EX here (before Win11). */ + ok(context_ex->All.Length == context_arch[test].context_length + context_arch[1].context_ex_length, + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags); + + ok(context_ex->XState.Offset == context_arch[1].context_ex_length + 1, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags); + ok(!context_ex->XState.Length, + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags); + + if (0) + { + /* Crashes on Windows. */ + pRtlLocateLegacyContext(NULL, NULL); + } + p = pRtlLocateLegacyContext(context_ex, NULL); + ok(p == context, "Got unexpected p %p, flags %#lx.\n", p, flags); + length2 = 0xdeadbeef; + p = pRtlLocateLegacyContext(context_ex, &length2); + ok(p == context && length2 == context_ex->Legacy.Length, + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + length2 = expected_length; + + if (0) + { + /* Crashes on Windows. */ + pGetXStateFeaturesMask(context, NULL); + pRtlGetExtendedFeaturesMask(context_ex); + pRtlSetExtendedFeaturesMask(context_ex, 0); + } + + flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8; + + mask = 0xdeadbeef; + bret = pGetXStateFeaturesMask(context, &mask); + SetLastError(0xdeadbeef); + if (flags & CONTEXT_NATIVE) + ok(bret && mask == ((flags & flags_fpx) == flags_fpx ? 0x3 : 0), + "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); + else + ok(!bret && mask == 0xdeadbeef && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, mask %s, GetLastError() %#lx, flags %#lx.\n", + bret, wine_dbgstr_longlong(mask), GetLastError(), flags); + + bret = pSetXStateFeaturesMask(context, 0); + ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + bret = pSetXStateFeaturesMask(context, 1); + ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (bret ? flags_fpx : flags), + "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + bret = pSetXStateFeaturesMask(context, 2); + ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (bret ? flags_fpx : flags), + "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + bret = pSetXStateFeaturesMask(context, 4); + ok(!bret, "Got unexpected bret %#x.\n", bret); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (flags & CONTEXT_NATIVE ? flags_fpx : flags), + "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + *(DWORD *)(context_buffer + context_arch[test].flags_offset) = flags; + + for (j = 0; j < context_arch[test].flags_offset; ++j) + { + if (context_buffer[j] != 0xcc) + { + ok(0, "Buffer data changed at offset %#x.\n", j); + break; + } + } + for (j = context_arch[test].flags_offset + sizeof(context_flags); + j < context_arch[test].context_length; ++j) + { + if (context_buffer[j] != 0xcc) + { + ok(0, "Buffer data changed at offset %#x.\n", j); + break; + } + } + for (j = context_arch[test].context_length + context_arch[test].context_ex_length; + j < sizeof(context_buffer); ++j) + { + if (context_buffer[j] != 0xcc) + { + ok(0, "Buffer data changed at offset %#x.\n", j); + break; + } + } + + memset(context_buffer2, 0xcc, sizeof(context_buffer2)); + ret2 = pRtlInitializeExtendedContext(context_buffer2, flags, &context_ex); + ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags); + ok(!memcmp(context_buffer2, context_buffer, sizeof(context_buffer2)), + "Context data do not match, flags %#lx.\n", flags); + + memset(context_buffer2, 0xcc, sizeof(context_buffer2)); + ret2 = pRtlInitializeExtendedContext(context_buffer2 + 2, flags, &context_ex); + ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags); + + /* Buffer gets aligned to 16 bytes on x64, while returned context length suggests it should be 8. */ + align = test ? 4 : 16; + ok(!memcmp(context_buffer2 + align, context_buffer, + sizeof(context_buffer2) - align), + "Context data do not match, flags %#lx.\n", flags); + + SetLastError(0xdeadbeef); + memset(context_buffer2, 0xcc, sizeof(context_buffer2)); + bret = pInitializeContext(context_buffer2 + 2, flags, &context, &length2); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(length2 == expected_length, "Got unexpected length %#lx.\n", length); + ok(!memcmp(context_buffer2 + align, context_buffer, + sizeof(context_buffer2) - align), + "Context data do not match, flags %#lx.\n", flags); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 0, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + && length2 == offsetof(XSAVE_FORMAT, XmmRegisters), + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 1, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters) + && length2 == sizeof(M128A) * context_arch[test].vector_reg_count, + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 2, &length2); + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + if (!pRtlInitializeExtendedContext2 || !pInitializeContext2) + { + static int once; + + if (!once++) + win_skip("InitializeContext2 is not available.\n"); + continue; + } + + length2 = expected_length; + memset(context_buffer2, 0xcc, sizeof(context_buffer2)); + ret2 = pRtlInitializeExtendedContext2(context_buffer2 + 2, flags, &context_ex, ~(ULONG64)0); + ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags); + ok(!memcmp(context_buffer2 + align, context_buffer, + sizeof(context_buffer2) - align), + "Context data do not match, flags %#lx.\n", flags); + + memset(context_buffer2, 0xcc, sizeof(context_buffer2)); + bret = pInitializeContext2(context_buffer2 + 2, flags, &context, &length2, 0); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(length2 == expected_length, "Got unexpected length %#lx.\n", length); + ok(!memcmp(context_buffer2 + align, context_buffer, + sizeof(context_buffer2) - align), + "Context data do not match, flags %#lx.\n", flags); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 0, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + && length2 == offsetof(XSAVE_FORMAT, XmmRegisters), + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + } + + flags = context_arch[test].flag | 0x40; + flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8; + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength(flags, &length); + + if (!enabled_features) + { + ok(ret == STATUS_NOT_SUPPORTED && length == 0xdeadbeef, + "Got unexpected result ret %#lx, length %#lx.\n", ret, length); + + context_ex = (void *)0xdeadbeef; + ret2 = pRtlInitializeExtendedContext(context_buffer, flags, &context_ex); + ok(ret2 == STATUS_NOT_SUPPORTED, "Got unexpected result ret %#lx, test %u.\n", ret2, test); + + SetLastError(0xdeadbeef); + length2 = sizeof(context_buffer); + bret = pInitializeContext(context_buffer, flags, &context, &length2); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#lx, flags %#lx.\n", + context_flags, flags); + + if (pInitializeContext2) + { + SetLastError(0xdeadbeef); + length2 = sizeof(context_buffer); + bret = pInitializeContext2(context_buffer, flags, &context, &length2, ~(ULONG64)0); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#lx, flags %#lx.\n", + context_flags, flags); + } + continue; + } + + ok(!ret && length >= expected_length_xstate, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + + if (!pRtlGetExtendedContextLength2) + { + win_skip("RtlGetExtendedContextLength2 is not available.\n"); + } + else + { + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength2(flags, &length, 7); + ok(!ret && length == expected_length_xstate, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength2(flags, &length, ~0); + ok(!ret && length >= expected_length_xstate, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength2(flags, &length, 0); + ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT)) + || broken(!ret && length == expected_length_xstate) /* win10pro */, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength2(flags, &length, 3); + ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT)) + || broken(!ret && length == expected_length_xstate) /* win10pro */, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength2(flags, &length, 4); + ok(!ret && length == expected_length_xstate, + "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test); + } + + pRtlGetExtendedContextLength(flags, &length); + SetLastError(0xdeadbeef); + bret = pInitializeContext(NULL, flags, NULL, &length2); + ok(!bret && length2 == length && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Got unexpected bret %#x, length2 %#lx, GetLastError() %lu, flags %#lx.\n", + bret, length2, GetLastError(), flags); + + SetLastError(0xdeadbeef); + context = (void *)0xdeadbeef; + length2 = length - 1; + bret = pInitializeContext(context_buffer, flags, &context, &length2); + ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && length2 == length && context == (void *)0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, length2 %#lx, flags %#lx.\n", + bret, GetLastError(), length2, flags); + + SetLastError(0xdeadbeef); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + length2 = length + 1; + bret = pInitializeContext(context_buffer, flags, &context, &length2); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(length2 == length, "Got unexpected length %#lx.\n", length); + ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context); + + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length); + ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags); + ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length + : context_arch[test].legacy_length), + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags); + + expected_offset = (((ULONG_PTR)context + context_arch[test].context_length + + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context + - context_arch[test].context_length; + ok(context_ex->XState.Offset == expected_offset, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags); + ok(context_ex->XState.Length >= sizeof(XSTATE), + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags); + + ok(context_ex->All.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags); + /* No extra 8 bytes in x64 CONTEXT_EX here. */ + ok(context_ex->All.Length == context_arch[test].context_length + + context_ex->XState.Offset + context_ex->XState.Length, + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags); + + xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); + length2 = 0xdeadbeef; + for (i = 0; i < 2; ++i) + { + p = pRtlLocateExtendedFeature(context_ex, i, &length2); + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx.\n", p, length2); + } + + p = pRtlLocateExtendedFeature(context_ex, XSTATE_AVX, &length2); + ok(length2 == sizeof(YMMCONTEXT), "Got unexpected length %#lx.\n", length2); + ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p); + p = pRtlLocateExtendedFeature(context_ex, XSTATE_AVX, NULL); + ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 0, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + && length2 == offsetof(XSAVE_FORMAT, XmmRegisters), + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 1, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters) + && length2 == sizeof(M128A) * context_arch[test].vector_reg_count, + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 2, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == &xs->YmmContext && length2 == sizeof(YMMCONTEXT), + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + mask = 0xdeadbeef; + bret = pGetXStateFeaturesMask(context, &mask); + if (flags & CONTEXT_NATIVE) + ok(bret && !mask, + "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); + else + ok(!bret && mask == 0xdeadbeef, + "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); + + expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | enabled_features : 0; + ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + mask = pRtlGetExtendedFeaturesMask(context_ex); + ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask)); + ok(xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0])); + + xs->Mask = 0xdeadbeef; + xs->CompactionMask = 0xdeadbeef; + pRtlSetExtendedFeaturesMask(context_ex, 0); + ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + mask = pRtlGetExtendedFeaturesMask(context_ex); + ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask)); + ok(xs->CompactionMask == 0xdeadbeef, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context->ContextFlags, flags); + + xs->Mask = 0xdeadbeef; + xs->CompactionMask = 0; + pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0); + ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + mask = pRtlGetExtendedFeaturesMask(context_ex); + ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask)); + ok(!xs->CompactionMask, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context->ContextFlags, flags); + + xs->Mask = 0xdeadbeef; + xs->CompactionMask = 0xdeadbeef; + bret = pSetXStateFeaturesMask(context, xstate_supported_features); + ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x.\n", bret); + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == (bret ? flags_fpx : flags), + "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + ok(xs->Mask == bret ? 4 : 0xdeadbeef, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + mask = pRtlGetExtendedFeaturesMask(context_ex); + ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask)); + ok(xs->CompactionMask == bret ? expected_compaction : 0xdeadbeef, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + + mask = 0xdeadbeef; + bret = pGetXStateFeaturesMask(context, &mask); + if (flags & CONTEXT_NATIVE) + ok(bret && mask == xstate_supported_features, + "Got unexpected bret %#x, mask %s, flags %#lx (enabled_features & supported_features %#I64x).\n", bret, wine_dbgstr_longlong(mask), flags, xstate_supported_features); + else + ok(!bret && mask == 0xdeadbeef, + "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); + + if (pRtlGetExtendedContextLength2) + { + memset(context_buffer, 0xcc, sizeof(context_buffer)); + pRtlGetExtendedContextLength2(flags, &length, 0); + SetLastError(0xdeadbeef); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + length2 = length; + bret = pInitializeContext2(context_buffer, flags, &context, &length2, 0); + ok(bret && GetLastError() == 0xdeadbeef, + "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags); + ok(length2 == length, "Got unexpected length %#lx.\n", length); + ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 0, &length2); + if (flags & CONTEXT_NATIVE) + ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + && length2 == offsetof(XSAVE_FORMAT, XmmRegisters), + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + else + ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + length2 = 0xdeadbeef; + p = pRtlLocateExtendedFeature(context_ex, 2, &length2); + ok((!p && length2 == sizeof(YMMCONTEXT)) + || broken(p && length2 == sizeof(YMMCONTEXT)) /* win10pro */, + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + length2 = 0xdeadbeef; + p = pLocateXStateFeature(context, 2, &length2); + ok(!p && length2 == (flags & CONTEXT_NATIVE) ? sizeof(YMMCONTEXT) : 0xdeadbeef, + "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags); + + context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset); + ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags); + + context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length); + ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags); + ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length + : context_arch[test].legacy_length), + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags); + + expected_offset = (((ULONG_PTR)context + context_arch[test].context_length + + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context + - context_arch[test].context_length; + ok(context_ex->XState.Offset == expected_offset, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags); + ok(context_ex->XState.Length == sizeof(XSTATE) - sizeof(YMMCONTEXT) + || broken(context_ex->XState.Length == sizeof(XSTATE)) /* win10pro */, + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags); + + ok(context_ex->All.Offset == -(int)context_arch[test].context_length, + "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags); + /* No extra 8 bytes in x64 CONTEXT_EX here. */ + ok(context_ex->All.Length == context_arch[test].context_length + + context_ex->XState.Offset + context_ex->XState.Length, + "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags); + + expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0; + xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); + ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0])); + + pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0); + ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + } + } + + length = 0xdeadbeef; + ret = pRtlGetExtendedContextLength(context_arch[0].flag | context_arch[1].flag, &length); + ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#lx, length %#lx.\n", + ret, length); + + if (0) + { + /* Crashes on Windows. */ + pRtlGetExtendedContextLength(CONTEXT_FULL, NULL); + length = sizeof(context_buffer); + pInitializeContext(context_buffer, CONTEXT_FULL, NULL, &length); + pInitializeContext(context_buffer, CONTEXT_FULL, &context, NULL); + } + + if (!(enabled_features & (1 << XSTATE_AVX))) + { + skip("AVX is not supported.\n"); + return; + } + + /* Test RtlCaptureContext (doesn't support xstates). */ + length = sizeof(context_buffer); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + bret = pInitializeContext(context_buffer, CONTEXT_XSTATE, &context, &length); + ok(bret, "Got unexpected bret %#x.\n", bret); + context_ex = (CONTEXT_EX *)(context + 1); + xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); + + max_xstate_length = context_ex->XState.Length; + ok(max_xstate_length >= sizeof(XSTATE), "XSTATE size: %#lx; min: %#Ix.\n", max_xstate_length, sizeof(XSTATE)); + + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = RtlCaptureContext; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = context; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = (void *)0xdeadbeef; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data; + memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0)); + + memcpy(data, test_extended_context_data, sizeof(data)); + func(); + ok(context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS), "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + for (i = 0; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + /* Test GetThreadContext (current thread, ymm0 set). */ + length = sizeof(context_buffer); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, + &context, &length); + ok(bret, "Got unexpected bret %#x.\n", bret); + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + + expected_flags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT; +#ifdef __i386__ + expected_flags |= CONTEXT_EXTENDED_REGISTERS; +#endif + pSetXStateFeaturesMask(context, ~(ULONG64)0); + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = GetThreadContext; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data; + memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0)); + xs->CompactionMask = 2; + xs->Mask = compaction_enabled ? 2 : 0; + context_ex->XState.Length = sizeof(XSTATE); + + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0; + + ok(!xs->Mask || broken(xs->Mask == 4) /* win10pro */, + "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + + for (i = 4; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == (xs->Mask == 4 ? test_extended_context_data[i + 4] : 0xcccccccc), + "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0; + + xs->CompactionMask = 4; + xs->Mask = compaction_enabled ? 0 : 4; + context_ex->XState.Length = max_xstate_length + 64; + bret = func(); + ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER, + "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + ok(xs->Mask == (compaction_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok(xs->CompactionMask == 4, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc + || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */, + "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + xs->CompactionMask = 4; + xs->Mask = compaction_enabled ? 0 : 4; + context_ex->XState.Length = offsetof(XSTATE, YmmContext); + bret = func(); + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + ok(!bret && GetLastError() == ERROR_MORE_DATA, + "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc + || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */, + "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + context_ex->XState.Length = sizeof(XSTATE); + xs->CompactionMask = 4; + xs->Mask = compaction_enabled ? 0 : 4; + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + + ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + + for (i = 4; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4], + "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + /* Test GetThreadContext (current thread, ymm state cleared). */ + length = sizeof(context_buffer); + memset(context_buffer, 0xcc, sizeof(context_buffer)); + bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, + &context, &length); + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + ok(bret, "Got unexpected bret %#x.\n", bret); + + /* clear potentially leftover xstate */ + pSetXStateFeaturesMask(context, 0); + context->ContextFlags = CONTEXT_XSTATE; + SetThreadContext(GetCurrentThread(), context); + + context->ContextFlags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT; + + pSetXStateFeaturesMask(context, ~(ULONG64)0); + *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_addr) = GetThreadContext; + *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param2) = context; + *(void **)(call_func_code_reset_ymm_state + call_func_offsets.ymm0_save) = data; + memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state)); + + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + expected_flags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT; +#ifdef __i386__ + expected_flags |= CONTEXT_EXTENDED_REGISTERS; +#endif + ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n", + context->ContextFlags); + + expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | (xstate_supported_features & ~(UINT64)3) : 0; + + xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->CompactionMask & (supported_features | ((ULONG64)1 << 63))) == expected_compaction, + "Got unexpected CompactionMask %s (expected %#I64x).\n", wine_dbgstr_longlong(xs->CompactionMask), expected_compaction); + + for (i = 4; i < 8; ++i) + ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc) + || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]), + "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + /* Test setting context which has only part of xstate in CompactionMask. */ + if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK)) + { + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = SetThreadContext; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread(); + *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context; + *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data; + memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0)); + context->ContextFlags = CONTEXT_XSTATE; + xs->CompactionMask = 0x8000000000000000 | ((ULONG64)1 << XSTATE_AVX512_KMASK); + xs->Mask = 0; + memcpy(data, test_extended_context_data, sizeof(data)); + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + /* Setting a context with only part of xstate in CompactionMask doesn't change missing parts. */ + for (i = 4; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + memcpy(data, test_extended_context_data, sizeof(data)); + xs->CompactionMask |= XSTATE_MASK_GSSE; + bret = func(); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + for (i = 4; i < 8; ++i) + ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + } + else + { + skip("avx512 is not available, skipping test.\n"); + } + + /* Test fault exception context. */ + memset(data, 0xff, sizeof(data)); + xs->Mask = 0; + test_extended_context_modified_state = FALSE; + run_exception_test(test_extended_context_handler, NULL, except_code_reset_ymm_state, + ARRAY_SIZE(except_code_reset_ymm_state), PAGE_EXECUTE_READ); + for (i = 0; i < 8; ++i) + { + /* Older Windows version do not reset AVX context to INIT_STATE on x86. */ + ok(!data[i] || broken(i >= 4 && sizeof(void *) == 4 && data[i] == test_extended_context_spoil_data2[i]), + "Got unexpected data %#x, i %u.\n", data[i], i); + } + + memcpy(data, test_extended_context_data, sizeof(data)); + test_extended_context_modified_state = TRUE; + run_exception_test(test_extended_context_handler, NULL, except_code_set_ymm0, + ARRAY_SIZE(except_code_set_ymm0), PAGE_EXECUTE_READ); + + for (i = 0; i < 8; ++i) + ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i); + + /* Test GetThreadContext for the other thread. */ + thread = CreateThread(NULL, 0, test_extended_context_thread, 0, CREATE_SUSPENDED, NULL); + ok(!!thread, "Failed to create thread.\n"); + + bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT, + &context, &length); + ok(bret, "Got unexpected bret %#x.\n", bret); + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + context_ex = (CONTEXT_EX *)(context + 1); + xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); + pSetXStateFeaturesMask(context, 4); + + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + todo_wine_if (!xsaveopt_enabled) + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, + "Got unexpected CompactionMask %I64x, expected %I64x.\n", xs->CompactionMask, + expected_compaction); + + for (i = 0; i < 16 * 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc), + "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + pSetXStateFeaturesMask(context, 4); + memset(&xs->YmmContext, 0, sizeof(xs->YmmContext)); + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + ok(!(xs->Mask & supported_features) || broken((xs->Mask & supported_features) == 4), "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(xs->Mask)); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + for (i = 0; i < 16 * 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc || broken(xs->Mask == 4 && !((ULONG *)&xs->YmmContext)[i]), + "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + + pSetXStateFeaturesMask(context, 4); + memset(&xs->YmmContext, 0x28, sizeof(xs->YmmContext)); + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(xs->CompactionMask)); + for (i = 0; i < 16 * 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0x28282828, "Got unexpected value %#lx, i %u.\n", + ((ULONG *)&xs->YmmContext)[i], i); + + wait_for_thread_next_suspend(thread); + + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + pSetXStateFeaturesMask(context, 4); + memset(&xs->YmmContext, 0x48, sizeof(xs->YmmContext)); + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + wait_for_thread_next_suspend(thread); + + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + + for (i = 0; i < 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0x68686868, "Got unexpected value %#lx, i %u.\n", + ((ULONG *)&xs->YmmContext)[i], i); + + wait_for_thread_next_suspend(thread); + + memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + todo_wine_if (!xsaveopt_enabled && sizeof(void *) != 4) + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4) + || (sizeof(void *) == 4 && (xs->Mask & supported_features) == 4), + "Got unexpected Mask %#I64x, supported_features.\n", xs->Mask); + if ((xs->Mask & supported_features) == 4) + { + for (i = 0; i < 8 * sizeof(void *); ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0, + "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + for (; i < 16 * 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0x48484848, + "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + } + else + { + for (i = 0; i < 16 * 4; ++i) + ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc, + "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); + } + + if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK)) + { + ULONG64 saved_mask; + ULONG *d; + + saved_mask = xs->CompactionMask; + xs->Mask = XSTATE_MASK_GSSE; + xs->CompactionMask = 0x8000000000000000 | xs->Mask; + *(ULONG *)&xs->YmmContext = 0x11111111; + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + xs->Mask = (ULONG64)1 << XSTATE_AVX512_KMASK; + xs->CompactionMask = 0x8000000000000000 | xs->Mask; + *(ULONG *)&xs->YmmContext = 0x22222222; + bret = SetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + xs->CompactionMask = saved_mask; + bret = GetThreadContext(thread, context); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + todo_wine_if(xs->Mask == XSTATE_MASK_GSSE) + ok((xs->Mask & (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK))) + == (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK)), "got Mask %#I64x.\n", xs->Mask); + d = pLocateXStateFeature(context, XSTATE_AVX, NULL); + ok(!!d, "Got NULL.\n"); + ok(*d == 0x11111111, "got %#lx.\n", *d); + + d = pLocateXStateFeature(context, XSTATE_AVX512_KMASK, NULL); + ok(!!d, "Got NULL.\n"); + todo_wine ok(*d == 0x22222222, "got %#lx.\n", *d); + } + else + { + skip("avx512 is not available, skipping test.\n"); + } + + bret = ResumeThread(thread); + ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + +struct modified_range +{ + ULONG start; + ULONG flag; +}; + +#define check_changes_in_range(a, b, c, d) check_changes_in_range_(__FILE__, __LINE__, a, b, c, d) +static void check_changes_in_range_(const char *file, unsigned int line, const BYTE *p, + const struct modified_range *range, ULONG flags, unsigned int length) +{ + ULONG range_flag, flag; + unsigned int once = 0; + unsigned int i; + + range_flag = 0; + for (i = 0; i < length; i++) + { + if (i == range->start) + { + range_flag = range->flag; + ++range; + } + + if ((flag = range_flag) == ~0) + continue; + + if (flag & 0x80000000) + { + if (flag & flags && p[i] == 0xcc) + { + if (!once++) + ok(broken(1), "Matched broken result at %#x, flags %#lx.\n", i, flags); + continue; + } + flag = 0; + } + + if (flag & flags && p[i] != 0xcc) + { + ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#lx.\n", p[i], i, flags); + return; + } + else if (!(flag & flags) && p[i] != 0xdd) + { + ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#lx.\n", p[i], i, flags); + return; + } + } + ok_(file, line)(1, "Range matches.\n"); +} + +static void test_copy_context(void) +{ + static struct modified_range ranges_amd64[] = + { + {0x30, ~0}, {0x38, 0x1}, {0x3a, 0x4}, {0x42, 0x1}, {0x48, 0x10}, {0x78, 0x2}, {0x98, 0x1}, + {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0x80000008}, {0x4b0, 0x10}, {0x4d0, ~0}, + {0x4e8, 0}, {0x500, ~0}, {0x640, 0}, {0x1000, 0}, + }; + static struct modified_range ranges_x86[] = + { + {0x0, ~0}, {0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0x80000020}, + {0x2cc, ~0}, {0x440, 0}, {0x1000, 0}, + }; + static const struct modified_range single_range[] = + { + {0x0, 0x1}, {0x1000, 0}, + }; + + static const ULONG tests[] = + { + /* AMD64 */ + CONTEXT_AMD64_CONTROL, + CONTEXT_AMD64_INTEGER, + CONTEXT_AMD64_SEGMENTS, + CONTEXT_AMD64_FLOATING_POINT, + CONTEXT_AMD64_DEBUG_REGISTERS, + CONTEXT_AMD64_FULL, + CONTEXT_AMD64_XSTATE, + CONTEXT_AMD64_ALL, + /* X86 */ + CONTEXT_I386_CONTROL, + CONTEXT_I386_INTEGER, + CONTEXT_I386_SEGMENTS, + CONTEXT_I386_FLOATING_POINT, + CONTEXT_I386_DEBUG_REGISTERS, + CONTEXT_I386_EXTENDED_REGISTERS, + CONTEXT_I386_XSTATE, + CONTEXT_I386_ALL + }; + static const ULONG arch_flags[] = {CONTEXT_AMD64, CONTEXT_i386}; + + DECLSPEC_ALIGN(64) BYTE src_context_buffer[4096]; + DECLSPEC_ALIGN(64) BYTE dst_context_buffer[4096]; + ULONG64 enabled_features, expected_compaction; + unsigned int context_length, flags_offset, i; + CONTEXT_EX *src_ex, *dst_ex; + XSTATE *dst_xs, *src_xs; + BOOL compaction, bret; + CONTEXT *src, *dst; + NTSTATUS status; + DWORD length; + ULONG flags; + + if (!pRtlCopyExtendedContext) + { + win_skip("RtlCopyExtendedContext is not available.\n"); + return; + } + + if (!pRtlGetEnabledExtendedFeatures) + { + win_skip("RtlGetEnabledExtendedFeatures is not available.\n"); + return; + } + + enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0); + + memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer)); + memset(src_context_buffer, 0xcc, sizeof(src_context_buffer)); + + status = pRtlInitializeExtendedContext(src_context_buffer, CONTEXT_ALL | CONTEXT_XSTATE, &src_ex); + if (!status) + { + src = pRtlLocateLegacyContext(src_ex, NULL); + dst = (CONTEXT *)dst_context_buffer; + dst->ContextFlags = CONTEXT_ALL; + status = pRtlCopyContext(dst, dst->ContextFlags, src); + ok(!status, "Got status %#lx.\n", status); + check_changes_in_range((BYTE *)dst, CONTEXT_ALL & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + CONTEXT_ALL, sizeof(CONTEXT)); + } + else + { + ok(status == STATUS_NOT_SUPPORTED, "Got status %#lx.\n", status); + skip("Extended context is not supported.\n"); + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + flags = tests[i]; + flags_offset = (flags & CONTEXT_AMD64) ? offsetof(AMD64_CONTEXT,ContextFlags) + : offsetof(I386_CONTEXT,ContextFlags); + + memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer)); + memset(src_context_buffer, 0xcc, sizeof(src_context_buffer)); + + status = pRtlInitializeExtendedContext(src_context_buffer, flags, &src_ex); + if (enabled_features || !(flags & 0x40)) + { + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + } + else + { + ok(status == STATUS_NOT_SUPPORTED, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + continue; + } + status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + + src = pRtlLocateLegacyContext(src_ex, NULL); + dst = pRtlLocateLegacyContext(dst_ex, NULL); + + *(DWORD *)((BYTE *)dst + flags_offset) = 0; + *(DWORD *)((BYTE *)src + flags_offset) = 0; + + context_length = dst_ex->All.Length; + + if (flags & 0x40) + { + src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset); + memset(src_xs, 0xcc, src_ex->XState.Length); + src_xs->Mask = enabled_features & ~(ULONG64)4; + src_xs->CompactionMask = ~(ULONG64)0; + if (flags & CONTEXT_AMD64) + ranges_amd64[ARRAY_SIZE(ranges_amd64) - 2].start = 0x640 + src_ex->XState.Length - sizeof(XSTATE); + else + ranges_x86[ARRAY_SIZE(ranges_x86) - 2].start = 0x440 + src_ex->XState.Length - sizeof(XSTATE); + } + + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + + check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + flags, context_length); + + ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + + memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer)); + status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + *(DWORD *)((BYTE *)src + flags_offset) = 0; + *(DWORD *)((BYTE *)dst + flags_offset) = 0; + SetLastError(0xdeadbeef); + status = pRtlCopyContext(dst, flags | 0x40, src); + ok(status == (enabled_features ? STATUS_INVALID_PARAMETER : STATUS_NOT_SUPPORTED) + || broken(status == STATUS_INVALID_PARAMETER), + "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(*(DWORD *)((BYTE *)dst + flags_offset) == 0, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + 0, context_length); + + *(DWORD *)((BYTE *)dst + flags_offset) = flags & (CONTEXT_AMD64 | CONTEXT_i386); + *(DWORD *)((BYTE *)src + flags_offset) = flags; + status = pRtlCopyContext(dst, flags, src); + if (flags & 0x40) + ok((status == STATUS_BUFFER_OVERFLOW) + || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER), + "Got unexpected status %#lx, flags %#lx.\n", status, flags); + else + ok(!status || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER), + "Got unexpected status %#lx, flags %#lx.\n", status, flags); + if (!status) + { + ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + flags, context_length); + } + else + { + ok(*(DWORD *)((BYTE *)dst + flags_offset) == (flags & 0x110000), + "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + 0, context_length); + } + } + + for (i = 0; i < ARRAY_SIZE(arch_flags); ++i) + { + flags = arch_flags[i] | 0x42; + flags_offset = (flags & CONTEXT_AMD64) ? offsetof(AMD64_CONTEXT,ContextFlags) + : offsetof(I386_CONTEXT,ContextFlags); + context_length = (flags & CONTEXT_AMD64) ? sizeof(AMD64_CONTEXT) : sizeof(I386_CONTEXT); + + memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer)); + memset(src_context_buffer, 0xcc, sizeof(src_context_buffer)); + length = sizeof(src_context_buffer); + bret = pInitializeContext(src_context_buffer, flags, &src, &length); + ok(bret, "Got unexpected bret %#x, flags %#lx.\n", bret, flags); + + length = sizeof(dst_context_buffer); + bret = pInitializeContext(dst_context_buffer, flags, &dst, &length); + ok(bret, "Got unexpected bret %#x, flags %#lx.\n", bret, flags); + + dst_ex = (CONTEXT_EX *)((BYTE *)dst + context_length); + src_ex = (CONTEXT_EX *)((BYTE *)src + context_length); + + dst_xs = (XSTATE *)((BYTE *)dst_ex + dst_ex->XState.Offset); + src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset); + + *(DWORD *)((BYTE *)dst + flags_offset) = 0; + *(DWORD *)((BYTE *)src + flags_offset) = 0; + + compaction = !!(src_xs->CompactionMask & ((ULONG64)1 << 63)); + expected_compaction = (compaction ? ((ULONG64)1 << (ULONG64)63) | enabled_features : 0); + + memset(&src_xs->YmmContext, 0xcc, sizeof(src_xs->YmmContext)); + src_xs->CompactionMask = ~(ULONG64)0; + + src_xs->Mask = 0; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + dst_ex->XState.Length = 0; + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(status == (enabled_features ? STATUS_BUFFER_OVERFLOW : STATUS_NOT_SUPPORTED), + "Got unexpected status %#lx, flags %#lx.\n", status, flags); + + if (!enabled_features) + continue; + + ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + + src_xs->Mask = ~(ULONG64)0; + + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + dst_ex->XState.Length = 0; + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(status == STATUS_BUFFER_OVERFLOW, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + + ok(dst_xs->Mask == 0xdddddddddddddddd, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == 0xdddddddddddddddd, "Got unexpected CompactionMask %s.\n", + wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext)); + + src_xs->Mask = 3; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + dst_ex->XState.Length = offsetof(XSTATE, YmmContext); + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", + *(DWORD *)((BYTE *)dst + flags_offset), flags); + ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext)); + + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + dst_ex->XState.Length = sizeof(XSTATE); + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext)); + + src_xs->Mask = 4; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext)); + + src_xs->Mask = 3; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext)); + + + *(DWORD *)((BYTE *)src + flags_offset) = arch_flags[i]; + + src_xs->Mask = 7; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); + ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(dst_xs->Mask)); + ok(dst_xs->CompactionMask == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask)); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext)); + + src_xs->Mask = 7; + memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext)); + dst_xs->CompactionMask = 0xdddddddddddddddd; + dst_xs->Mask = 0xdddddddddddddddd; + status = pRtlCopyContext(dst, flags, src); + ok(!status || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER), + "Got unexpected status %#lx, flags %#lx.\n", status, flags); + ok(dst_xs->Mask == 0xdddddddddddddddd || broken(dst_xs->Mask == 4), "Got unexpected Mask %s, flags %#lx.\n", + wine_dbgstr_longlong(dst_xs->Mask), flags); + ok(dst_xs->CompactionMask == 0xdddddddddddddddd || broken(dst_xs->CompactionMask == expected_compaction), + "Got unexpected CompactionMask %s, flags %#lx.\n", wine_dbgstr_longlong(dst_xs->CompactionMask), flags); + check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, + dst_xs->Mask == 4, sizeof(dst_xs->YmmContext)); + } +} + +#if defined(__i386__) +# define IP_REG(ctx) ctx.Eip +#else +# define IP_REG(ctx) ctx.Rip +#endif + +static volatile int exit_ip_test; +static DWORD WINAPI ip_test_thread_proc( void *param ) +{ + SetEvent( param ); + while (!exit_ip_test); + return ERROR_SUCCESS; +} + +static void test_set_live_context(void) +{ + UINT_PTR old_ip, target; + HANDLE thread, event; + char *target_ptr; + CONTEXT ctx; + DWORD res; + int i; + + /* jmp to self at offset 0 and 4 */ + static const char target_code[] = {0xeb, 0xfe, 0x90, 0x90, 0xeb, 0xfe}; + + target_ptr = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + memcpy( target_ptr, target_code, sizeof(target_code) ); + target = (UINT_PTR)target_ptr; + + event = CreateEventW( NULL, TRUE, FALSE, NULL ); + thread = CreateThread( NULL, 65536, ip_test_thread_proc, event, 0, NULL ); + ok( thread != NULL, "Failed to create thread: %lx\n", GetLastError() ); + res = WaitForSingleObject( event, 1000 ); + ok( !res, "wait returned: %ld\n", res ); + CloseHandle( event ); + + memset( &ctx, 0, sizeof(ctx) ); + ctx.ContextFlags = CONTEXT_ALL; + res = GetThreadContext( thread, &ctx ); + ok( res, "Failed to get thread context: %lx\n", GetLastError() ); + old_ip = IP_REG(ctx); + + IP_REG(ctx) = target; + res = SetThreadContext( thread, &ctx ); + ok( res, "Failed to set thread context: %lx\n", GetLastError() ); + + for (i = 0; i < 10; i++) + { + IP_REG(ctx) = target; + res = SetThreadContext( thread, &ctx ); + ok( res, "Failed to set thread context: %lx\n", GetLastError() ); + + ctx.ContextFlags = CONTEXT_ALL; + res = GetThreadContext( thread, &ctx ); + ok( res, "Failed to get thread context: %lx\n", GetLastError() ); + ok( IP_REG(ctx) == target, "IP = %p, expected %p\n", (void *)IP_REG(ctx), target_ptr ); + + IP_REG(ctx) = target + 4; + res = SetThreadContext( thread, &ctx ); + ok( res, "Failed to set thread context: %lx\n", GetLastError()) ; + + ctx.ContextFlags = CONTEXT_ALL; + res = GetThreadContext( thread, &ctx ); + ok( res, "Failed to get thread context: %lx\n", GetLastError() ); + ok( IP_REG(ctx) == target + 4, "IP = %p, expected %p\n", (void *)IP_REG(ctx), target_ptr + 4 ); + } + + exit_ip_test = 1; + ctx.ContextFlags = CONTEXT_ALL; + IP_REG(ctx) = old_ip; + res = SetThreadContext( thread, &ctx ); + ok( res, "Failed to restore thread context: %lx\n", GetLastError() ); + + res = WaitForSingleObject( thread, 1000 ); + ok( !res, "wait returned: %ld\n", res ); + + VirtualFree( target_ptr, 0, MEM_RELEASE ); +} +#endif + +static void test_backtrace(void) +{ + void *buffer[1024]; + WCHAR name[MAX_PATH]; + void *module; + ULONG hash, hash_expect; + int i, count = RtlCaptureStackBackTrace( 0, 1024, buffer, &hash ); + + ok( count > 0, "got %u entries\n", count ); + for (i = hash_expect = 0; i < count; i++) hash_expect += (ULONG_PTR)buffer[i]; + ok( hash == hash_expect, "hash mismatch %lx / %lx\n", hash, hash_expect ); + pRtlPcToFileHeader( buffer[0], &module ); + if (is_arm64ec && module == hntdll) /* Windows arm64ec has an extra frame for the entry thunk */ + { + ok( count > 1, "wrong count %u\n", count ); + pRtlPcToFileHeader( buffer[1], &module ); + } + GetModuleFileNameW( module, name, ARRAY_SIZE(name) ); + ok( module == GetModuleHandleA(0), "wrong module %p %s / %p for %p\n", + module, debugstr_w(name), GetModuleHandleA(0), buffer[0]); + + if (pRtlGetCallersAddress) + { + void *caller, *parent; + + caller = parent = (void *)0xdeadbeef; + pRtlGetCallersAddress( &caller, &parent ); + ok( caller == (count > 1 ? buffer[1] : NULL) || broken(is_arm64ec), /* caller is entry thunk */ + "wrong caller %p / %p\n", caller, buffer[1] ); + ok( parent == (count > 2 ? buffer[2] : NULL), "wrong parent %p / %p\n", parent, buffer[2] ); + } + else win_skip( "RtlGetCallersAddress not supported\n" ); + + if (count && !buffer[count - 1]) count--; /* win11 32-bit */ + if (count <= 1) return; + pRtlPcToFileHeader( buffer[count - 1], &module ); + GetModuleFileNameW( module, name, ARRAY_SIZE(name) ); + ok( module == hntdll, "wrong module %p %s for frame %u %p\n", + module, debugstr_w(name), count - 1, buffer[count - 1] ); +} + +struct context_exception_request_thread_param +{ + LONG volatile sync; + HANDLE event; +}; +static volatile int *p_context_exception_request_value; +struct context_exception_request_thread_param *context_exception_request_param; + +static LONG CALLBACK test_context_exception_request_handler( EXCEPTION_POINTERS *info ) +{ + PEXCEPTION_RECORD rec = info->ExceptionRecord; + CONTEXT *c = info->ContextRecord; + DWORD old_prot; + + ok( !(c->ContextFlags & (CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE)), "got %#lx.\n", c->ContextFlags ); + + ok( rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, "got %#lx.\n", rec->ExceptionCode ); + VirtualProtect( (void *)p_context_exception_request_value, sizeof(*p_context_exception_request_value), + PAGE_READWRITE, &old_prot ); + + WriteRelease( &context_exception_request_param->sync, 5 ); + while (ReadAcquire( &context_exception_request_param->sync ) != 6) + ; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +#ifdef __i386__ +static const BYTE call_func64_code[] = +{ + 0x58, /* pop %eax */ + 0x0e, /* push %cs */ + 0x50, /* push %eax */ + 0x6a, 0x33, /* push $0x33 */ + 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 1f */ + 0x83, 0x04, 0x24, 0x05, /* 1: addl $0x5,(%esp) */ + 0xcb, /* lret */ + /* in 64-bit mode: */ + 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */ + 0x55, /* push %rbp */ + 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */ + 0x56, /* push %rsi */ + 0x57, /* push %rdi */ + 0x41, 0x8b, 0x4e, 0x10, /* mov 0x10(%r14),%ecx */ + 0x41, 0x8b, 0x76, 0x14, /* mov 0x14(%r14),%esi */ + 0x67, 0x8d, 0x04, 0xcd, 0, 0, 0, 0, /* lea 0x0(,%ecx,8),%eax */ + 0x83, 0xf8, 0x20, /* cmp $0x20,%eax */ + 0x7d, 0x05, /* jge 1f */ + 0xb8, 0x20, 0x00, 0x00, 0x00, /* mov $0x20,%eax */ + 0x48, 0x29, 0xc4, /* 1: sub %rax,%rsp */ + 0x48, 0x83, 0xe4, 0xf0, /* and $~15,%rsp */ + 0x48, 0x89, 0xe7, /* mov %rsp,%rdi */ + 0xf3, 0x48, 0xa5, /* rep movsq */ + 0x48, 0x8b, 0x0c, 0x24, /* mov (%rsp),%rcx */ + 0x48, 0x8b, 0x54, 0x24, 0x08, /* mov 0x8(%rsp),%rdx */ + 0x4c, 0x8b, 0x44, 0x24, 0x10, /* mov 0x10(%rsp),%r8 */ + 0x4c, 0x8b, 0x4c, 0x24, 0x18, /* mov 0x18(%rsp),%r9 */ + 0x41, 0xff, 0x56, 0x08, /* callq *0x8(%r14) */ + 0x48, 0x8d, 0x65, 0xf0, /* lea -0x10(%rbp),%rsp */ + 0x5f, /* pop %rdi */ + 0x5e, /* pop %rsi */ + 0x5d, /* pop %rbp */ + 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */ + 0xcb, /* lret */ +}; + +static NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem ) +{ + NTSTATUS (WINAPI *func)( ULONG64 func64, int nb_args, ULONG64 *args ) = code_mem; + + memcpy( code_mem, call_func64_code, sizeof(call_func64_code) ); + return func( func64, nb_args, args ); +} +#endif + +static DWORD WINAPI test_context_exception_request_thread( void *arg ) +{ +#ifdef __i386__ + static BYTE wait_sync_x64_code[] = + { + 0x89, 0x11, /* mov %edx,(%rcx) */ + 0x83, 0xc2, 0x01, /* add $0x1,%edx */ + 0x0f, 0x1f, 0x00, /* 1: nopl (%rax) */ + 0x8b, 0x01, /* mov (%rcx),%eax */ + 0x39, 0xd0, /* cmp %edx,%eax */ + 0x75, 0xfa, /* jne 1b */ + 0xc3, /* ret */ + }; + ULONG64 args[2]; +#endif + struct context_exception_request_thread_param *p = arg; + void *vectored_handler; + + context_exception_request_param = p; + vectored_handler = pRtlAddVectoredExceptionHandler( TRUE, test_context_exception_request_handler ); + ok( !!vectored_handler, "failed.\n" ); + + WriteRelease( &p->sync, 1 ); + while (ReadAcquire( &p->sync ) != 2) + ; + + WaitForSingleObject( p->event, INFINITE ); + +#ifdef __i386__ + memcpy( (char *)code_mem + 1024, wait_sync_x64_code, sizeof(wait_sync_x64_code) ); + args[0] = (ULONG_PTR)&p->sync; + args[1] = 3; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + p_context_exception_request_value = VirtualAlloc( NULL, sizeof(*p_context_exception_request_value), + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( !!p_context_exception_request_value, "got NULL.\n" ); + *p_context_exception_request_value = 1; + ok( *p_context_exception_request_value == 1, "got %d.\n", *p_context_exception_request_value ); + VirtualFree( (void *)p_context_exception_request_value, 0, MEM_RELEASE ); + pRtlRemoveVectoredExceptionHandler( vectored_handler ); + +#ifdef __i386__ + args[1] = 7; + if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem ); +#endif + + return 0; +} + +static void test_context_exception_request(void) +{ + struct context_exception_request_thread_param p; + DWORD expected_flags; + HANDLE thread; + CONTEXT c; + BOOL ret; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler) + { + skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found.\n" ); + return; + } + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */, + "got %#lx.\n", c.ContextFlags ); + if (c.ContextFlags == 0x10001) + { + win_skip( "Old WoW64 behaviour, skipping tests.\n" ); + return; + } + + ret = DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread, 0, TRUE, DUPLICATE_SAME_ACCESS ); + ok( ret, "got error %lu.\n", GetLastError() ); + c.ContextFlags = expected_flags | CONTEXT_EXCEPTION_REQUEST; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + CloseHandle( thread ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( GetCurrentThread(), &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + p.event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL ); + ok( !!thread, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL + | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags ); + + p.sync = 0; + ResumeThread(thread); + + while (ReadAcquire( &p.sync ) != 1) + SwitchToThread(); + /* thread is in user code. */ + SuspendThread( thread ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + ResumeThread(thread); + WriteRelease( &p.sync, 2 ); + /* Try to make sure the thread entered WaitForSingleObject(). */ + Sleep(30); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL; + ret = SetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + SetEvent( p.event ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 3) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 4 ); + } + + while (ReadAcquire( &p.sync ) != 5) + SwitchToThread(); + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE + | CONTEXT_EXCEPTION_ACTIVE; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags ); + + WriteRelease( &p.sync, 6 ); + + if (is_wow64 && !old_wow64) + { + while (ReadAcquire( &p.sync ) != 7) + SwitchToThread(); + /* thread is in x64 code. */ + + expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE; + + c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST; + ret = GetThreadContext( thread, &c ); + ok( ret, "got error %lu.\n", GetLastError() ); + ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags ); + + WriteRelease( &p.sync, 8 ); + } + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( p.event ); +} START_TEST(exception) { - HMODULE hntdll = GetModuleHandleA("ntdll.dll"); -#if defined(__x86_64__) - HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll"); -#endif + HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); + hntdll = GetModuleHandleA("ntdll.dll"); -#if defined(__REACTOS__) && !defined(_M_AMD64) - if (!winetest_interactive && - !strcmp(winetest_platform, "windows")) + my_argc = winetest_get_mainargs( &my_argv ); + + if (my_argc >= 3 && !strcmp(my_argv[2], "suspend_process")) { - skip("ROSTESTS-240: Skipping ntdll_winetest:exception because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n"); + suspend_process_proc(); return; } -#endif + code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!code_mem) { trace("VirtualAlloc failed\n"); return; } - pNtGetContextThread = (void *)GetProcAddress( hntdll, "NtGetContextThread" ); - pNtSetContextThread = (void *)GetProcAddress( hntdll, "NtSetContextThread" ); - pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" ); - pNtClose = (void *)GetProcAddress( hntdll, "NtClose" ); - pRtlUnwind = (void *)GetProcAddress( hntdll, "RtlUnwind" ); - pRtlRaiseException = (void *)GetProcAddress( hntdll, "RtlRaiseException" ); - pRtlCaptureContext = (void *)GetProcAddress( hntdll, "RtlCaptureContext" ); - pNtTerminateProcess = (void *)GetProcAddress( hntdll, "NtTerminateProcess" ); - pRtlAddVectoredExceptionHandler = (void *)GetProcAddress( hntdll, - "RtlAddVectoredExceptionHandler" ); - pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll, - "RtlRemoveVectoredExceptionHandler" ); - pRtlAddVectoredContinueHandler = (void *)GetProcAddress( hntdll, - "RtlAddVectoredContinueHandler" ); - pRtlRemoveVectoredContinueHandler = (void *)GetProcAddress( hntdll, - "RtlRemoveVectoredContinueHandler" ); - pNtQueryInformationProcess = (void*)GetProcAddress( hntdll, - "NtQueryInformationProcess" ); - pNtSetInformationProcess = (void*)GetProcAddress( hntdll, - "NtSetInformationProcess" ); - pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); +#define X(f) p##f = (void*)GetProcAddress(hntdll, #f) + X(NtGetContextThread); + X(NtSetContextThread); + X(NtQueueApcThread); + X(NtContinueEx); + X(NtReadVirtualMemory); + X(NtClose); + X(RtlUnwind); + X(RtlRaiseException); + X(RtlCaptureContext); + X(NtTerminateProcess); + X(RtlAddVectoredExceptionHandler); + X(RtlRemoveVectoredExceptionHandler); + X(RtlAddVectoredContinueHandler); + X(RtlRemoveVectoredContinueHandler); + X(RtlSetUnhandledExceptionFilter); + X(NtQueryInformationThread); + X(NtSetInformationProcess); + X(NtSuspendProcess); + X(NtRaiseException); + X(NtResumeProcess); + X(RtlGetUnloadEventTrace); + X(RtlGetUnloadEventTraceEx); + X(RtlGetEnabledExtendedFeatures); + X(RtlGetExtendedContextLength); + X(RtlGetExtendedContextLength2); + X(RtlInitializeExtendedContext); + X(RtlInitializeExtendedContext2); + X(RtlLocateExtendedFeature); + X(RtlLocateLegacyContext); + X(RtlSetExtendedFeaturesMask); + X(RtlGetExtendedFeaturesMask); + X(RtlPcToFileHeader); + X(RtlGetCallersAddress); + X(RtlCopyContext); + X(RtlCopyExtendedContext); + X(KiUserApcDispatcher); + X(KiUserCallbackDispatcher); + X(KiUserExceptionDispatcher); +#ifndef __i386__ + X(RtlRestoreContext); + X(RtlUnwindEx); + X(RtlAddFunctionTable); + X(RtlDeleteFunctionTable); + X(RtlGetNativeSystemInformation); +#endif -#ifdef __i386__ +#ifdef __x86_64__ + if (pRtlGetNativeSystemInformation) + { + SYSTEM_CPU_INFORMATION info; + ULONG len; + if (!pRtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len )) + is_arm64ec = (info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64); + } +#endif +#undef X + +#define X(f) p##f = (void*)GetProcAddress(hkernel32, #f) + X(IsWow64Process); if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + if (is_wow64) + { + TEB64 *teb64 = ULongToPtr( NtCurrentTeb()->GdiBatchCount ); + + if (teb64) + { + PEB64 *peb64 = ULongToPtr(teb64->Peb); + old_wow64 = !peb64->LdrData; + } + } + + X(InitializeContext); + X(InitializeContext2); + X(LocateXStateFeature); + X(SetXStateFeaturesMask); + X(GetXStateFeaturesMask); + X(WaitForDebugEventEx); +#undef X if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler) have_vectored_api = TRUE; @@ -2741,6 +11954,13 @@ START_TEST(exception) if (my_argc >= 4) { void *addr; + + if (strcmp(my_argv[2], "fastfail") == 0) + { + __fastfail(strtoul(my_argv[3], NULL, 0)); + return; + } + sscanf( my_argv[3], "%p", &addr ); if (addr != &test_stage) @@ -2758,96 +11978,168 @@ START_TEST(exception) if (pRtlRaiseException) { - test_stage = 1; + test_stage = STAGE_RTLRAISE_NOT_HANDLED; run_rtlraiseexception_test(0x12345); run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); - test_stage = 2; + test_stage = STAGE_RTLRAISE_HANDLE_LAST_CHANCE; run_rtlraiseexception_test(0x12345); run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); - test_stage = 3; - test_outputdebugstring(0); - test_stage = 4; - test_outputdebugstring(2); - test_stage = 5; - test_ripevent(0); - test_stage = 6; - test_ripevent(1); - test_stage = 7; - test_debug_service(0); - test_stage = 8; - test_debug_service(1); - test_stage = 9; - test_breakpoint(0); - test_stage = 10; - test_breakpoint(1); - test_stage = 11; - test_closehandle(0); - test_stage = 12; - test_closehandle(1); } - else - skip( "RtlRaiseException not found\n" ); + else skip( "RtlRaiseException not found\n" ); + + test_stage = STAGE_OUTPUTDEBUGSTRINGA_CONTINUE; + + test_outputdebugstring(FALSE, 0, FALSE, 0, 0); + test_stage = STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED; + test_outputdebugstring(FALSE, 2, TRUE, 0, 0); /* is 2 a Windows bug? */ + test_stage = STAGE_OUTPUTDEBUGSTRINGW_CONTINUE; + /* depending on value passed DebugContinue we can get the unicode exception or not */ + test_outputdebugstring(TRUE, 0, FALSE, 0, 1); + test_stage = STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED; + /* depending on value passed DebugContinue we can get the unicode exception or not */ + test_outputdebugstring(TRUE, 2, TRUE, 0, 1); /* is 2 a Windows bug? */ + test_stage = STAGE_RIPEVENT_CONTINUE; + test_ripevent(0); + test_stage = STAGE_RIPEVENT_NOT_HANDLED; + test_ripevent(1); + test_stage = STAGE_SERVICE_CONTINUE; + test_debug_service(0); + test_stage = STAGE_SERVICE_NOT_HANDLED; + test_debug_service(1); + test_stage = STAGE_BREAKPOINT_CONTINUE; + test_breakpoint(0); + test_stage = STAGE_BREAKPOINT_NOT_HANDLED; + test_breakpoint(1); + test_stage = STAGE_EXCEPTION_INVHANDLE_CONTINUE; + test_closehandle(0, (HANDLE)0xdeadbeef); + test_closehandle(0, (HANDLE)0x7fffffff); + test_stage = STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED; + test_closehandle(1, (HANDLE)0xdeadbeef); + test_closehandle(1, (HANDLE)~(ULONG_PTR)6); + test_stage = STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED; /* special cases */ + test_closehandle(0, 0); + test_closehandle(0, INVALID_HANDLE_VALUE); + test_closehandle(0, GetCurrentProcess()); + test_closehandle(0, GetCurrentThread()); + test_closehandle(0, (HANDLE)~(ULONG_PTR)2); + test_closehandle(0, GetCurrentProcessToken()); + test_closehandle(0, GetCurrentThreadToken()); + test_closehandle(0, GetCurrentThreadEffectiveToken()); +#if defined(__i386__) || defined(__x86_64__) + test_stage = STAGE_XSTATE; + test_debuggee_xstate(); + test_stage = STAGE_XSTATE_LEGACY_SSE; + test_debuggee_xstate(); + test_stage = STAGE_SEGMENTS; + test_debuggee_segments(); +#endif /* rest of tests only run in parent */ return; } +#ifdef __i386__ + test_unwind(); test_exceptions(); - test_rtlraiseexception(); test_debug_registers(); - test_outputdebugstring(1); - test_ripevent(1); test_debug_service(1); - test_breakpoint(1); - test_closehandle(0); - test_vectored_continue_handler(); - test_debugger(); test_simd_exceptions(); test_fpu_exceptions(); test_dpe_exceptions(); test_prot_fault(); - test_thread_context(); + test_extended_context(); + test_copy_context(); + test_set_live_context(); + test_hwbpt_in_syscall(); + test_instrumentation_callback(); #elif defined(__x86_64__) - pRtlAddFunctionTable = (void *)GetProcAddress( hntdll, - "RtlAddFunctionTable" ); - pRtlDeleteFunctionTable = (void *)GetProcAddress( hntdll, - "RtlDeleteFunctionTable" ); - pRtlInstallFunctionTableCallback = (void *)GetProcAddress( hntdll, - "RtlInstallFunctionTableCallback" ); - pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll, - "RtlLookupFunctionEntry" ); - p__C_specific_handler = (void *)GetProcAddress( hntdll, - "__C_specific_handler" ); - pRtlCaptureContext = (void *)GetProcAddress( hntdll, - "RtlCaptureContext" ); - pRtlRestoreContext = (void *)GetProcAddress( hntdll, - "RtlRestoreContext" ); - pRtlUnwindEx = (void *)GetProcAddress( hntdll, - "RtlUnwindEx" ); - p_setjmp = (void *)GetProcAddress( hmsvcrt, - "_setjmp" ); +#define X(f) p##f = (void*)GetProcAddress(hntdll, #f) + X(__C_specific_handler); + X(RtlWow64GetThreadContext); + X(RtlWow64SetThreadContext); + X(RtlWow64GetCpuAreaInfo); +#undef X + + test_exceptions(); test_debug_registers(); - test_outputdebugstring(1); - test_ripevent(1); + test_debug_registers_wow64(); test_debug_service(1); - test_breakpoint(1); - test_closehandle(0); - test_vectored_continue_handler(); - test_virtual_unwind(); + test_simd_exceptions(); + test_continue(); test___C_specific_handler(); test_restore_context(); + test_prot_fault(); + test_dpe_exceptions(); + test_wow64_context(); + test_nested_exception(); + test_collided_unwind(); + test_extended_context(); + test_copy_context(); + test_set_live_context(); + test_unwind_from_apc(); + test_syscall_clobbered_regs(); + test_raiseexception_regs(); + test_hwbpt_in_syscall(); + test_instrumentation_callback(); + test_direct_syscalls(); - if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) - test_dynamic_unwind(); - else - skip( "Dynamic unwind functions not found\n" ); +#elif defined(__aarch64__) + + test_continue(); + test_brk(); + test_nested_exception(); + test_collided_unwind(); + test_restore_context(); + test_mrs_currentel(); + +#elif defined(__arm__) + + test_nested_exception(); + test_collided_unwind(); + test_restore_context(); #endif + test_KiUserExceptionDispatcher(); + test_KiUserApcDispatcher(); + test_KiUserCallbackDispatcher(); + test_rtlraiseexception(); + test_debugger(DBG_EXCEPTION_HANDLED, FALSE); + test_debugger(DBG_CONTINUE, FALSE); + test_debugger(DBG_EXCEPTION_HANDLED, TRUE); + test_debugger(DBG_CONTINUE, TRUE); + test_thread_context(); + test_outputdebugstring(FALSE, 1, FALSE, 0, 0); + if (pWaitForDebugEventEx) + { + test_outputdebugstring(TRUE, 1, FALSE, 1, 1); + test_outputdebugstring_newmodel(); + } + else + skip("Unsupported new unicode debug string model\n"); + + test_ripevent(1); + test_fastfail(); + test_breakpoint(1); + test_closehandle(0, (HANDLE)0xdeadbeef); + /* Call of Duty WWII writes to BeingDebugged then closes an invalid handle, + * crashing the game if an exception is raised. */ + NtCurrentTeb()->Peb->BeingDebugged = 0x98; + test_closehandle(0, (HANDLE)0xdeadbeef); + NtCurrentTeb()->Peb->BeingDebugged = 0; + + test_user_apc(); + test_user_callback(); + test_vectored_continue_handler(); + test_suspend_thread(); + test_suspend_process(); + test_unload_trace(); + test_backtrace(); + test_context_exception_request(); VirtualFree(code_mem, 0, MEM_RELEASE); } diff --git a/modules/rostests/winetests/ntdll/file.c b/modules/rostests/winetests/ntdll/file.c index eb80ae52bd7..edba01b4c96 100644 --- a/modules/rostests/winetests/ntdll/file.c +++ b/modules/rostests/winetests/ntdll/file.c @@ -37,36 +37,7 @@ #include "winternl.h" #include "winuser.h" #include "winioctl.h" -#ifndef __REACTOS__ -#include "ntifs.h" -#else -/* FIXME: Inspect */ -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - _ANONYMOUS_UNION union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - } DUMMYUNIONNAME; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; -#endif +#include "winnls.h" #ifndef IO_COMPLETION_ALL_ACCESS #define IO_COMPLETION_ALL_ACCESS 0x001F0003 @@ -80,6 +51,7 @@ static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR ); static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* ); static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG, ULONG * ); +static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG, ULONG, PLARGE_INTEGER ); static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG); @@ -103,8 +75,11 @@ static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_AT static NTSTATUS (WINAPI *pNtOpenIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); static NTSTATUS (WINAPI *pNtQueryIoCompletion)(HANDLE, IO_COMPLETION_INFORMATION_CLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER); +static NTSTATUS (WINAPI *pNtRemoveIoCompletionEx)(HANDLE,FILE_IO_COMPLETION_INFORMATION*,ULONG,ULONG*,LARGE_INTEGER*,BOOLEAN); static NTSTATUS (WINAPI *pNtSetIoCompletion)(HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, SIZE_T); +static NTSTATUS (WINAPI *pNtSetIoCompletionEx)(HANDLE, HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, SIZE_T); static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS); +static NTSTATUS (WINAPI *pNtQueryAttributesFile)(const OBJECT_ATTRIBUTES*,FILE_BASIC_INFORMATION*); static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS); static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK, PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN); @@ -113,6 +88,8 @@ static NTSTATUS (WINAPI *pNtQueryFullAttributesFile)(const OBJECT_ATTRIBUTES*, F static NTSTATUS (WINAPI *pNtFlushBuffersFile)(HANDLE, IO_STATUS_BLOCK*); static NTSTATUS (WINAPI *pNtQueryEaFile)(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,BOOLEAN,PVOID,ULONG,PULONG,BOOLEAN); +static WCHAR fooW[] = {'f','o','o',0}; + static inline BOOL is_signaled( HANDLE obj ) { return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0; @@ -137,65 +114,42 @@ static HANDLE create_temp_file( ULONG flags ) #define CKEY_FIRST 0x1030341 #define CKEY_SECOND 0x132E46 -static ULONG_PTR completionKey; -static IO_STATUS_BLOCK ioSb; -static ULONG_PTR completionValue; - static ULONG get_pending_msgs(HANDLE h) { NTSTATUS res; ULONG a, req; res = pNtQueryIoCompletion( h, IoCompletionBasicInformation, &a, sizeof(a), &req ); - ok( res == STATUS_SUCCESS, "NtQueryIoCompletion failed: %x\n", res ); + ok( res == STATUS_SUCCESS, "NtQueryIoCompletion failed: %lx\n", res ); if (res != STATUS_SUCCESS) return -1; - ok( req == sizeof(a), "Unexpected response size: %x\n", req ); + ok( req == sizeof(a), "Unexpected response size: %lx\n", req ); return a; } -static BOOL get_msg(HANDLE h) -{ - LARGE_INTEGER timeout = {{-10000000*3}}; - DWORD res = pNtRemoveIoCompletion( h, &completionKey, &completionValue, &ioSb, &timeout); - ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %x\n", res ); - if (res != STATUS_SUCCESS) - { - completionKey = completionValue = 0; - memset(&ioSb, 0, sizeof(ioSb)); - return FALSE; - } - return TRUE; -} - - static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) { int *count = arg; - trace( "apc called block %p iosb.status %x iosb.info %lu\n", - iosb, U(*iosb).Status, iosb->Information ); + trace( "apc called block %p iosb.status %lx iosb.info %Iu\n", + iosb, iosb->Status, iosb->Information ); (*count)++; - ok( !reserved, "reserved is not 0: %x\n", reserved ); + ok( !reserved, "reserved is not 0: %lx\n", reserved ); } static void create_file_test(void) { - static const WCHAR notepadW[] = {'n','o','t','e','p','a','d','.','e','x','e',0}; static const WCHAR systemrootW[] = {'\\','S','y','s','t','e','m','R','o','o','t', '\\','f','a','i','l','i','n','g',0}; - static const WCHAR systemrootExplorerW[] = {'\\','S','y','s','t','e','m','R','o','o','t', - '\\','e','x','p','l','o','r','e','r','.','e','x','e',0}; static const WCHAR questionmarkInvalidNameW[] = {'a','f','i','l','e','?',0}; static const WCHAR pipeInvalidNameW[] = {'a','|','b',0}; static const WCHAR pathInvalidNtW[] = {'\\','\\','?','\\',0}; static const WCHAR pathInvalidNt2W[] = {'\\','?','?','\\',0}; static const WCHAR pathInvalidDosW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0}; static const char testdata[] = "Hello World"; - static const WCHAR sepW[] = {'\\',0}; FILE_NETWORK_OPEN_INFORMATION info; NTSTATUS status; HANDLE dir, file; - WCHAR path[MAX_PATH], temp[MAX_PATH]; + WCHAR path[MAX_PATH]; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; UNICODE_STRING nameW; @@ -215,31 +169,31 @@ static void create_file_test(void) /* try various open modes and options on directories */ status = pNtCreateFile( &dir, GENERIC_READ|GENERIC_WRITE, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; offset.QuadPart = 0; status = pNtReadFile( dir, NULL, NULL, NULL, &io, buf, sizeof(buf), &offset, NULL ); - ok( status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_PENDING, "NtReadFile error %08x\n", status ); + ok( status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_PENDING, "NtReadFile error %08lx\n", status ); if (status == STATUS_PENDING) { ret = WaitForSingleObject( dir, 1000 ); - ok( ret == WAIT_OBJECT_0, "WaitForSingleObject error %u\n", ret ); - ok( U(io).Status == STATUS_INVALID_DEVICE_REQUEST, - "expected STATUS_INVALID_DEVICE_REQUEST, got %08x\n", U(io).Status ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject error %lu\n", ret ); + ok( io.Status == STATUS_INVALID_DEVICE_REQUEST, + "expected STATUS_INVALID_DEVICE_REQUEST, got %08lx\n", io.Status ); } - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; offset.QuadPart = 0; status = pNtWriteFile( dir, NULL, NULL, NULL, &io, testdata, sizeof(testdata), &offset, NULL); todo_wine - ok( status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_PENDING, "NtWriteFile error %08x\n", status ); + ok( status == STATUS_INVALID_DEVICE_REQUEST || status == STATUS_PENDING, "NtWriteFile error %08lx\n", status ); if (status == STATUS_PENDING) { ret = WaitForSingleObject( dir, 1000 ); - ok( ret == WAIT_OBJECT_0, "WaitForSingleObject error %u\n", ret ); - ok( U(io).Status == STATUS_INVALID_DEVICE_REQUEST, - "expected STATUS_INVALID_DEVICE_REQUEST, got %08x\n", U(io).Status ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject error %lu\n", ret ); + ok( io.Status == STATUS_INVALID_DEVICE_REQUEST, + "expected STATUS_INVALID_DEVICE_REQUEST, got %08lx\n", io.Status ); } CloseHandle( dir ); @@ -247,54 +201,54 @@ static void create_file_test(void) status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( dir ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_SUPERSEDE, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( status == STATUS_INVALID_PARAMETER, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( status == STATUS_INVALID_PARAMETER, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, FILE_DIRECTORY_FILE, NULL, 0 ); - ok( status == STATUS_INVALID_PARAMETER, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( status == STATUS_INVALID_PARAMETER, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( dir ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, 0, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( dir ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_SUPERSEDE, 0, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE, 0, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtCreateFile( &dir, GENERIC_READ, &attr, &io, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OVERWRITE_IF, 0, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_ACCESS_DENIED, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); @@ -310,7 +264,7 @@ static void create_file_test(void) FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); todo_wine ok( status == STATUS_INVALID_PARAMETER, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); /* Invalid chars in file/dirnames */ pRtlDosPathNameToNtPathName_U(questionmarkInvalidNameW, &nameW, NULL, NULL); @@ -319,13 +273,13 @@ static void create_file_test(void) FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); ok(status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status); status = pNtCreateFile(&file, GENERIC_WRITE|SYNCHRONIZE, &attr, &io, NULL, 0, 0, FILE_CREATE, FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); ok(status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status); pRtlFreeUnicodeString(&nameW); pRtlDosPathNameToNtPathName_U(pipeInvalidNameW, &nameW, NULL, NULL); @@ -334,13 +288,13 @@ static void create_file_test(void) FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); ok(status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status); status = pNtCreateFile(&file, GENERIC_WRITE|SYNCHRONIZE, &attr, &io, NULL, 0, 0, FILE_CREATE, FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); ok(status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status); pRtlFreeUnicodeString(&nameW); pRtlInitUnicodeString( &nameW, pathInvalidNtW ); @@ -348,58 +302,39 @@ static void create_file_test(void) FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtQueryFullAttributesFile( &attr, &info ); todo_wine ok( status == STATUS_OBJECT_NAME_INVALID, - "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlInitUnicodeString( &nameW, pathInvalidNt2W ); status = pNtCreateFile( &dir, GENERIC_READ|SYNCHRONIZE, &attr, &io, NULL, 0, FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtQueryFullAttributesFile( &attr, &info ); ok( status == STATUS_OBJECT_NAME_INVALID, - "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlInitUnicodeString( &nameW, pathInvalidDosW ); status = pNtCreateFile( &dir, GENERIC_READ|SYNCHRONIZE, &attr, &io, NULL, 0, FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ok( status == STATUS_OBJECT_NAME_INVALID, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); status = pNtQueryFullAttributesFile( &attr, &info ); ok( status == STATUS_OBJECT_NAME_INVALID, - "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); - - GetWindowsDirectoryW( path, MAX_PATH ); - path[2] = 0; - ok( QueryDosDeviceW( path, temp, MAX_PATH ), - "QueryDosDeviceW failed with error %u\n", GetLastError() ); - lstrcatW( temp, sepW ); - lstrcatW( temp, path+3 ); - lstrcatW( temp, sepW ); - lstrcatW( temp, notepadW ); - - pRtlInitUnicodeString( &nameW, temp ); - status = pNtQueryFullAttributesFile( &attr, &info ); - ok( status == STATUS_SUCCESS, - "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); - - pRtlInitUnicodeString( &nameW, systemrootExplorerW ); - status = pNtQueryFullAttributesFile( &attr, &info ); - ok( status == STATUS_SUCCESS, - "query %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "query %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); } static void open_file_test(void) { + static const WCHAR testdirW[] = {'o','p','e','n','f','i','l','e','t','e','s','t',0}; static const char testdata[] = "Hello World"; - static WCHAR fooW[] = {'f','o','o',0}; NTSTATUS status; HANDLE dir, root, handle, file; WCHAR path[MAX_PATH], tmpfile[MAX_PATH]; @@ -416,19 +351,19 @@ static void open_file_test(void) attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.ObjectName = &nameW; - attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; status = pNtOpenFile( &dir, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); path[3] = 0; /* root of the drive */ pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); status = pNtOpenFile( &root, GENERIC_READ, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); /* test opening system dir with RootDirectory set to windows dir */ @@ -439,14 +374,14 @@ static void open_file_test(void) attr.RootDirectory = dir; status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( handle ); /* try uppercase name */ for (i = len; path[i]; i++) if (path[i] >= 'a' && path[i] <= 'z') path[i] -= 'a' - 'A'; status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( handle ); /* try with leading backslash */ @@ -457,15 +392,68 @@ static void open_file_test(void) ok( status == STATUS_INVALID_PARAMETER || status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_SYNTAX_BAD, - "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); if (!status) CloseHandle( handle ); /* try with empty name */ nameW.Length = 0; status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); CloseHandle( handle ); + CloseHandle( dir ); + + attr.RootDirectory = 0; + wcscat( path, L"\\cmd.exe" ); + pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); + status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); + ok( status == STATUS_NOT_A_DIRECTORY, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + CloseHandle( handle ); + status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + CloseHandle( handle ); + pRtlFreeUnicodeString( &nameW ); + + wcscat( path, L"\\" ); + pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); + status = NtOpenFile( &handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); + ok( status == STATUS_NOT_A_DIRECTORY, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + CloseHandle( handle ); + pRtlFreeUnicodeString( &nameW ); + + wcscat( path, L"\\cmd.exe" ); + pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); + status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + pRtlFreeUnicodeString( &nameW ); + + GetTempPathW( MAX_PATH, path ); + lstrcatW( path, testdirW ); + CreateDirectoryW( path, NULL ); + + pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL ); + attr.RootDirectory = NULL; + status = pNtOpenFile( &dir, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); + pRtlFreeUnicodeString( &nameW ); + + GetTempFileNameW( path, fooW, 0, tmpfile ); + file = CreateFileW( tmpfile, FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "CreateFile error %ld\n", GetLastError() ); + numbytes = 0xdeadbeef; + ret = WriteFile( file, testdata, sizeof(testdata) - 1, &numbytes, NULL ); + ok( ret, "WriteFile failed with error %lu\n", GetLastError() ); + ok( numbytes == sizeof(testdata) - 1, "failed to write all data\n" ); + CloseHandle( file ); /* try open by file id */ @@ -487,15 +475,7 @@ static void open_file_test(void) FILE_SHARE_READ, FILE_OPEN_BY_FILE_ID | ((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) ); - ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED || status == STATUS_NOT_IMPLEMENTED || status == STATUS_SHARING_VIOLATION, - "open %s failed %x\n", wine_dbgstr_w(info->FileName), status ); - if (status == STATUS_NOT_IMPLEMENTED) - { - win_skip( "FILE_OPEN_BY_FILE_ID not supported\n" ); - break; - } - if (status == STATUS_SHARING_VIOLATION) - trace( "%s is currently open\n", wine_dbgstr_w(info->FileName) ); + ok( status == STATUS_SUCCESS, "open %s failed %lx\n", wine_dbgstr_w(info->FileName), status ); if (!status) { BYTE buf[sizeof(FILE_ALL_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; @@ -523,7 +503,7 @@ static void open_file_test(void) FILE_OPEN_BY_FILE_ID | ((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) ); ok( status == STATUS_SUCCESS || status == STATUS_NOT_IMPLEMENTED, - "open %s failed %x\n", wine_dbgstr_w(info->FileName), status ); + "open %s failed %lx\n", wine_dbgstr_w(info->FileName), status ); if (!status) CloseHandle( handle ); } } @@ -531,18 +511,7 @@ static void open_file_test(void) CloseHandle( dir ); CloseHandle( root ); - GetTempPathW( MAX_PATH, path ); - GetTempFileNameW( path, fooW, 0, tmpfile ); pRtlDosPathNameToNtPathName_U( tmpfile, &nameW, NULL, NULL ); - - file = CreateFileW( tmpfile, FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, 0 ); - ok( file != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError() ); - numbytes = 0xdeadbeef; - ret = WriteFile( file, testdata, sizeof(testdata) - 1, &numbytes, NULL ); - ok( ret, "WriteFile failed with error %u\n", GetLastError() ); - ok( numbytes == sizeof(testdata) - 1, "failed to write all data\n" ); - CloseHandle( file ); - attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.ObjectName = &nameW; @@ -551,13 +520,13 @@ static void open_file_test(void) attr.SecurityQualityOfService = NULL; status = pNtOpenFile( &file, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); numbytes = 0xdeadbeef; memset( data, 0, sizeof(data) ); ret = ReadFile( file, data, sizeof(data), &numbytes, NULL ); - ok( ret, "ReadFile failed with error %u\n", GetLastError() ); + ok( ret, "ReadFile failed with error %lu\n", GetLastError() ); ok( numbytes == sizeof(testdata) - 1, "failed to read all data\n" ); ok( !memcmp( data, testdata, sizeof(testdata) - 1 ), "testdata doesn't match\n" ); @@ -568,7 +537,7 @@ static void open_file_test(void) status = pNtOpenFile( &root, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); ok( status == STATUS_OBJECT_PATH_NOT_FOUND, - "expected STATUS_OBJECT_PATH_NOT_FOUND, got %08x\n", status ); + "expected STATUS_OBJECT_PATH_NOT_FOUND, got %08lx\n", status ); nameW.Length = 0; nameW.Buffer = NULL; @@ -576,28 +545,29 @@ static void open_file_test(void) attr.ObjectName = &nameW; status = pNtOpenFile( &root, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(tmpfile), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(tmpfile), status ); numbytes = SetFilePointer( file, 0, 0, FILE_CURRENT ); - ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %u\n", numbytes ); + ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %lu\n", numbytes ); numbytes = SetFilePointer( root, 0, 0, FILE_CURRENT ); - ok( numbytes == 0, "SetFilePointer returned %u\n", numbytes ); + ok( numbytes == 0, "SetFilePointer returned %lu\n", numbytes ); numbytes = 0xdeadbeef; memset( data, 0, sizeof(data) ); ret = ReadFile( root, data, sizeof(data), &numbytes, NULL ); - ok( ret, "ReadFile failed with error %u\n", GetLastError() ); + ok( ret, "ReadFile failed with error %lu\n", GetLastError() ); ok( numbytes == sizeof(testdata) - 1, "failed to read all data\n" ); ok( !memcmp( data, testdata, sizeof(testdata) - 1 ), "testdata doesn't match\n" ); numbytes = SetFilePointer( file, 0, 0, FILE_CURRENT ); - ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %u\n", numbytes ); + ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %lu\n", numbytes ); numbytes = SetFilePointer( root, 0, 0, FILE_CURRENT ); - ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %u\n", numbytes ); + ok( numbytes == sizeof(testdata) - 1, "SetFilePointer returned %lu\n", numbytes ); CloseHandle( file ); CloseHandle( root ); DeleteFileW( tmpfile ); + RemoveDirectoryW( path ); } static void delete_file_test(void) @@ -616,7 +586,7 @@ static void delete_file_test(void) ok(0, "couldn't get temp dir\n"); return; } - if (ret + sizeof(testdirW)/sizeof(WCHAR)-1 + sizeof(subdirW)/sizeof(WCHAR)-1 >= MAX_PATH) + if (ret + ARRAY_SIZE(testdirW)-1 + ARRAY_SIZE(subdirW)-1 >= MAX_PATH) { ok(0, "MAX_PATH exceeded in constructing paths\n"); return; @@ -662,8 +632,11 @@ static void delete_file_test(void) pRtlFreeUnicodeString( &nameW ); } +#define TEST_OVERLAPPED_READ_SIZE 4096 + static void read_file_test(void) { + DECLSPEC_ALIGN(TEST_OVERLAPPED_READ_SIZE) static unsigned char aligned_buffer[TEST_OVERLAPPED_READ_SIZE]; const char text[] = "foobar"; HANDLE handle; IO_STATUS_BLOCK iosb; @@ -675,32 +648,33 @@ static void read_file_test(void) if (!(handle = create_temp_file( FILE_FLAG_OVERLAPPED ))) return; apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = 0; ResetEvent( event ); status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL ); - ok( status == STATUS_SUCCESS || status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, + "wrong status %lx.\n", status ); if (status == STATUS_PENDING) WaitForSingleObject( event, 1000 ); - ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == strlen(text), "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ ok( apc_count == 1, "apc was not called\n" ); apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = 0; ResetEvent( event ); status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, strlen(text) + 10, &offset, NULL ); - ok( status == STATUS_SUCCESS || - status == STATUS_PENDING, /* vista */ - "wrong status %x\n", status ); + ok(status == STATUS_PENDING + || broken(status == STATUS_SUCCESS) /* before Vista */, + "wrong status %lx.\n", status); if (status == STATUS_PENDING) WaitForSingleObject( event, 1000 ); - ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == strlen(text), "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -708,16 +682,17 @@ static void read_file_test(void) /* read beyond eof */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = strlen(text) + 2; status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, &offset, NULL ); - ok(status == STATUS_PENDING || status == STATUS_END_OF_FILE /* before Vista */, "expected STATUS_PENDING or STATUS_END_OF_FILE, got %#x\n", status); + ok(status == STATUS_PENDING || broken(status == STATUS_END_OF_FILE) /* before Vista */, + "expected STATUS_PENDING, got %#lx\n", status); if (status == STATUS_PENDING) /* vista */ { WaitForSingleObject( event, 1000 ); - ok( U(iosb).Status == STATUS_END_OF_FILE, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_END_OF_FILE, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -728,31 +703,31 @@ static void read_file_test(void) /* now a non-overlapped file */ if (!(handle = create_temp_file(0))) return; apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = 0; status = pNtWriteFile( handle, event, apc, &apc_count, &iosb, text, strlen(text), &offset, NULL ); ok( status == STATUS_END_OF_FILE || status == STATUS_SUCCESS || status == STATUS_PENDING, /* vista */ - "wrong status %x\n", status ); + "wrong status %lx\n", status ); if (status == STATUS_PENDING) WaitForSingleObject( event, 1000 ); - ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == strlen(text), "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ ok( apc_count == 1, "apc was not called\n" ); apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = 0; ResetEvent( event ); status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, strlen(text) + 10, &offset, NULL ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(iosb).Status == STATUS_SUCCESS, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == strlen(text), "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == strlen(text), "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -760,14 +735,14 @@ static void read_file_test(void) /* read beyond eof */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; offset.QuadPart = strlen(text) + 2; ResetEvent( event ); status = pNtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, &offset, NULL ); - ok( status == STATUS_END_OF_FILE, "wrong status %x\n", status ); - ok( U(iosb).Status == STATUS_END_OF_FILE, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_END_OF_FILE, "wrong status %lx\n", status ); + ok( iosb.Status == STATUS_END_OF_FILE, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -775,7 +750,46 @@ static void read_file_test(void) CloseHandle( handle ); - CloseHandle( event ); + if (!(handle = create_temp_file(FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING))) + return; + + apc_count = 0; + offset.QuadPart = 0; + iosb.Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + offset.QuadPart = 0; + ResetEvent(event); + status = pNtWriteFile(handle, event, apc, &apc_count, &iosb, + aligned_buffer, sizeof(aligned_buffer), &offset, NULL); + ok(status == STATUS_END_OF_FILE || status == STATUS_PENDING + || broken(status == STATUS_SUCCESS) /* before Vista */, + "Wrong status %lx.\n", status); + ok(iosb.Status == STATUS_SUCCESS, "Wrong status %lx.\n", iosb.Status); + ok(iosb.Information == sizeof(aligned_buffer), "Wrong info %Iu.\n", iosb.Information); + ok(is_signaled(event), "event is not signaled.\n"); + ok(!apc_count, "apc was called.\n"); + SleepEx(1, TRUE); /* alertable sleep */ + ok(apc_count == 1, "apc was not called.\n"); + + apc_count = 0; + offset.QuadPart = 0; + iosb.Status = 0xdeadbabe; + iosb.Information = 0xdeadbeef; + offset.QuadPart = 0; + ResetEvent(event); + status = pNtReadFile(handle, event, apc, &apc_count, &iosb, + aligned_buffer, sizeof(aligned_buffer), &offset, NULL); + ok(status == STATUS_PENDING, "Wrong status %lx.\n", status); + WaitForSingleObject(event, 1000); + ok(iosb.Status == STATUS_SUCCESS, "Wrong status %lx.\n", iosb.Status); + ok(iosb.Information == sizeof(aligned_buffer), "Wrong info %Iu.\n", iosb.Information); + ok(is_signaled(event), "event is not signaled.\n"); + ok(!apc_count, "apc was called.\n"); + SleepEx(1, TRUE); /* alertable sleep */ + ok(apc_count == 1, "apc was not called.\n"); + + CloseHandle(handle); + CloseHandle(event); } static void append_file_test(void) @@ -792,76 +806,76 @@ static void append_file_test(void) GetTempFileNameA( path, "foo", 0, buffer ); handle = CreateFileA(buffer, FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, 0); - ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %ld\n", GetLastError()); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text, 2, NULL, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 2, "expected 2, got %Iu\n", iosb.Information); CloseHandle(handle); /* It is possible to open a file with only FILE_APPEND_DATA access flags. It matches the O_WRONLY|O_APPEND open() posix behavior */ handle = CreateFileA(buffer, FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0, 0); - ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %ld\n", GetLastError()); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; offset.QuadPart = 1; status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 2, 2, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 2, "expected 2, got %Iu\n", iosb.Information); ret = SetFilePointer(handle, 0, NULL, FILE_CURRENT); - ok(ret == 4, "expected 4, got %u\n", ret); + ok(ret == 4, "expected 4, got %lu\n", ret); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; offset.QuadPart = 3; status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 4, 2, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 2, "expected 2, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 2, "expected 2, got %Iu\n", iosb.Information); ret = SetFilePointer(handle, 0, NULL, FILE_CURRENT); - ok(ret == 6, "expected 6, got %u\n", ret); + ok(ret == 6, "expected 6, got %lu\n", ret); CloseHandle(handle); handle = CreateFileA(buffer, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0, 0); - ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %d\n", GetLastError()); + ok(handle != INVALID_HANDLE_VALUE, "CreateFile error %ld\n", GetLastError()); memset(buf, 0, sizeof(buf)); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; offset.QuadPart = 0; status = pNtReadFile(handle, 0, NULL, NULL, &iosb, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 6, "expected 6, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 6, "expected 6, got %Iu\n", iosb.Information); buf[6] = 0; ok(memcmp(buf, text, 6) == 0, "wrong file contents: %s\n", buf); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; offset.QuadPart = 0; status = pNtWriteFile(handle, NULL, NULL, NULL, &iosb, text + 3, 3, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 3, "expected 3, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 3, "expected 3, got %Iu\n", iosb.Information); memset(buf, 0, sizeof(buf)); - U(iosb).Status = -1; + iosb.Status = -1; iosb.Information = -1; offset.QuadPart = 0; status = pNtReadFile(handle, 0, NULL, NULL, &iosb, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iosb).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iosb).Status); - ok(iosb.Information == 6, "expected 6, got %lu\n", iosb.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iosb.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iosb.Status); + ok(iosb.Information == 6, "expected 6, got %Iu\n", iosb.Information); buf[6] = 0; ok(memcmp(buf, "barbar", 6) == 0, "wrong file contents: %s\n", buf); @@ -900,7 +914,7 @@ static void nt_mailslot_test(void) &TimeOut); ok( rc == STATUS_ACCESS_VIOLATION || rc == STATUS_INVALID_PARAMETER, /* win2k3 */ - "rc = %x not STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER\n", rc); + "rc = %lx not STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER\n", rc); /* * Test to see if the Timeout can be NULL @@ -911,7 +925,7 @@ static void nt_mailslot_test(void) NULL); ok( rc == STATUS_SUCCESS || rc == STATUS_INVALID_PARAMETER, /* win2k3 */ - "rc = %x not STATUS_SUCCESS or STATUS_INVALID_PARAMETER\n", rc); + "rc = %lx not STATUS_SUCCESS or STATUS_INVALID_PARAMETER\n", rc); ok( hslot != 0, "Handle is invalid\n"); if ( rc == STATUS_SUCCESS ) pNtClose(hslot); @@ -923,234 +937,396 @@ static void nt_mailslot_test(void) rc = pNtCreateMailslotFile(&hslot, DesiredAccess, &attr, &IoStatusBlock, CreateOptions, MailslotQuota, MaxMessageSize, &TimeOut); - ok( rc == STATUS_SUCCESS, "Create MailslotFile failed rc = %x\n", rc); + ok( rc == STATUS_SUCCESS, "Create MailslotFile failed rc = %lx\n", rc); ok( hslot != 0, "Handle is invalid\n"); rc = pNtClose(hslot); ok( rc == STATUS_SUCCESS, "NtClose failed\n"); } -static void test_iocp_setcompletion(HANDLE h) +static void WINAPI user_apc_proc(ULONG_PTR arg) { + unsigned int *apc_count = (unsigned int *)arg; + ++*apc_count; +} + +static void test_set_io_completion(void) +{ + FILE_IO_COMPLETION_INFORMATION info[2] = {{0}}; + LARGE_INTEGER timeout = {{0}}; + unsigned int apc_count; + IO_STATUS_BLOCK iosb; + ULONG_PTR key, value; NTSTATUS res; ULONG count; SIZE_T size = 3; + HANDLE h, h2; if (sizeof(size) > 4) size |= (ULONGLONG)0x12345678 << 32; + res = pNtCreateIoCompletion( &h2, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( res == STATUS_SUCCESS, "NtCreateIoCompletion failed: %#lx\n", res ); + ok( h2 && h2 != INVALID_HANDLE_VALUE, "got invalid handle %p\n", h2 ); + res = pNtSetIoCompletion( h2, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + res = pNtRemoveIoCompletionEx( h2, info, 2, &count, &timeout, TRUE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + + res = pNtCreateIoCompletion( &h, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( res == STATUS_SUCCESS, "NtCreateIoCompletion failed: %#lx\n", res ); + ok( h && h != INVALID_HANDLE_VALUE, "got invalid handle %p\n", h ); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* APC goes first associated with completion port APC takes priority over pending completion. + * Even if the thread is associated with some other completion port. */ + ok( res == STATUS_USER_APC, "NtRemoveIoCompletionEx unexpected status %#lx\n", res ); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + + CloseHandle( h2 ); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* Previous call resulted in STATUS_USER_APC did not associate the thread with the port. */ + ok( res == STATUS_USER_APC, "NtRemoveIoCompletion unexpected status %#lx\n", res ); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* Now the thread is associated. */ + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* After a thread is associated with completion port existing completion is returned if APC is pending. */ + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( apc_count == 0, "wrong apc count %u\n", apc_count ); + SleepEx( 0, TRUE); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_TIMEOUT, "NtRemoveIoCompletion failed: %#lx\n", res ); + res = pNtSetIoCompletion( h, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size ); - ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %x\n", res ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %lx\n", res ); count = get_pending_msgs(h); - ok( count == 1, "Unexpected msg count: %d\n", count ); + ok( count == 1, "Unexpected msg count: %ld\n", count ); - if (get_msg(h)) + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( key == CKEY_FIRST, "Invalid completion key: %#Ix\n", key ); + ok( iosb.Information == size, "Invalid iosb.Information: %Iu\n", iosb.Information ); + ok( iosb.Status == STATUS_INVALID_DEVICE_REQUEST, "Invalid iosb.Status: %#lx\n", iosb.Status ); + ok( value == CVALUE_FIRST, "Invalid completion value: %#Ix\n", value ); + + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + + if (!pNtRemoveIoCompletionEx) { - ok( completionKey == CKEY_FIRST, "Invalid completion key: %lx\n", completionKey ); - ok( ioSb.Information == size, "Invalid ioSb.Information: %lu\n", ioSb.Information ); - ok( U(ioSb).Status == STATUS_INVALID_DEVICE_REQUEST, "Invalid ioSb.Status: %x\n", U(ioSb).Status); - ok( completionValue == CVALUE_FIRST, "Invalid completion value: %lx\n", completionValue ); + skip("NtRemoveIoCompletionEx() not present\n"); + pNtClose( h ); + return; } - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %d\n", count ); + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, FALSE ); + ok( res == STATUS_TIMEOUT, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count <= 1, "wrong count %lu\n", count ); + + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, FALSE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( info[0].CompletionKey == 123, "wrong key %#Ix\n", info[0].CompletionKey ); + ok( info[0].CompletionValue == 456, "wrong value %#Ix\n", info[0].CompletionValue ); + ok( info[0].IoStatusBlock.Information == size, "wrong information %#Ix\n", + info[0].IoStatusBlock.Information ); + ok( info[0].IoStatusBlock.Status == 789, "wrong status %#lx\n", info[0].IoStatusBlock.Status); + + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + res = pNtSetIoCompletion( h, 12, 34, 56, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, FALSE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 2, "wrong count %lu\n", count ); + ok( info[0].CompletionKey == 123, "wrong key %#Ix\n", info[0].CompletionKey ); + ok( info[0].CompletionValue == 456, "wrong value %#Ix\n", info[0].CompletionValue ); + ok( info[0].IoStatusBlock.Information == size, "wrong information %#Ix\n", + info[0].IoStatusBlock.Information ); + ok( info[0].IoStatusBlock.Status == 789, "wrong status %#lx\n", info[0].IoStatusBlock.Status); + ok( info[1].CompletionKey == 12, "wrong key %#Ix\n", info[1].CompletionKey ); + ok( info[1].CompletionValue == 34, "wrong value %#Ix\n", info[1].CompletionValue ); + ok( info[1].IoStatusBlock.Information == size, "wrong information %#Ix\n", + info[1].IoStatusBlock.Information ); + ok( info[1].IoStatusBlock.Status == 56, "wrong status %#lx\n", info[1].IoStatusBlock.Status); + + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + res = pNtSetIoCompletion( h, 12, 34, 56, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 1, &count, NULL, FALSE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( info[0].CompletionKey == 123, "wrong key %#Ix\n", info[0].CompletionKey ); + ok( info[0].CompletionValue == 456, "wrong value %#Ix\n", info[0].CompletionValue ); + ok( info[0].IoStatusBlock.Information == size, "wrong information %#Ix\n", + info[0].IoStatusBlock.Information ); + ok( info[0].IoStatusBlock.Status == 789, "wrong status %#lx\n", info[0].IoStatusBlock.Status); + + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 1, &count, NULL, FALSE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( info[0].CompletionKey == 12, "wrong key %#Ix\n", info[0].CompletionKey ); + ok( info[0].CompletionValue == 34, "wrong value %#Ix\n", info[0].CompletionValue ); + ok( info[0].IoStatusBlock.Information == size, "wrong information %#Ix\n", + info[0].IoStatusBlock.Information ); + ok( info[0].IoStatusBlock.Status == 56, "wrong status %#lx\n", info[0].IoStatusBlock.Status); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + + count = 0xdeadbeef; + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, FALSE ); + ok( res == STATUS_TIMEOUT, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count <= 1, "wrong count %lu\n", count ); + ok( !apc_count, "wrong apc count %d\n", apc_count ); + + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + ok( res == STATUS_USER_APC, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count <= 1, "wrong count %lu\n", count ); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( !apc_count, "wrong apc count %u\n", apc_count ); + + SleepEx( 1, TRUE ); + + pNtClose( h ); } -static void test_iocp_fileio(HANDLE h) +static void test_file_io_completion(void) { static const char pipe_name[] = "\\\\.\\pipe\\iocompletiontestnamedpipe"; IO_STATUS_BLOCK iosb; - FILE_COMPLETION_INFORMATION fci = {h, CKEY_SECOND}; - HANDLE hPipeSrv, hPipeClt; + BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; + FILE_COMPLETION_INFORMATION fci; + LARGE_INTEGER timeout = {{0}}; + HANDLE server, client; + ULONG_PTR key, value; + OVERLAPPED o = {0}; + int apc_count = 0; NTSTATUS res; + DWORD read; + long count; + HANDLE h; - hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL ); - ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" ); - if (hPipeSrv != INVALID_HANDLE_VALUE ) - { - hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); - ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" ); - if (hPipeClt != INVALID_HANDLE_VALUE) - { - U(iosb).Status = 0xdeadbeef; - res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation ); - ok( res == STATUS_INVALID_PARAMETER, "Unexpected NtSetInformationFile on non-overlapped handle: %x\n", res ); - ok( U(iosb).Status == STATUS_INVALID_PARAMETER /* 98 */ || U(iosb).Status == 0xdeadbeef /* NT4+ */, - "Unexpected iosb.Status on non-overlapped handle: %x\n", U(iosb).Status ); - CloseHandle(hPipeClt); - } - CloseHandle( hPipeSrv ); - } + res = pNtCreateIoCompletion( &h, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( res == STATUS_SUCCESS, "NtCreateIoCompletion failed: %#lx\n", res ); + ok( h && h != INVALID_HANDLE_VALUE, "got invalid handle %p\n", h ); + fci.CompletionPort = h; + fci.CompletionKey = CKEY_SECOND; - hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL ); - ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" ); - if (hPipeSrv == INVALID_HANDLE_VALUE ) - return; + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); - hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); - ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" ); - if (hPipeClt != INVALID_HANDLE_VALUE) - { - OVERLAPPED o = {0,}; - BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; - DWORD read; - long count; + iosb.Status = 0xdeadbeef; + res = pNtSetInformationFile( server, &iosb, &fci, sizeof(fci), FileCompletionInformation ); + ok( res == STATUS_INVALID_PARAMETER, "NtSetInformationFile failed: %#lx\n", res ); + todo_wine + ok( iosb.Status == 0xdeadbeef, "wrong status %#lx\n", iosb.Status ); + CloseHandle( client ); + CloseHandle( server ); - U(iosb).Status = 0xdeadbeef; - res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation ); - ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %x\n", res ); - ok( U(iosb).Status == STATUS_SUCCESS, "iosb.Status invalid: %x\n", U(iosb).Status ); + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); - memset( send_buf, 0, TEST_BUF_LEN ); - memset( recv_buf, 0xde, TEST_BUF_LEN ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); - ReadFile( hPipeSrv, recv_buf, TEST_BUF_LEN, &read, &o); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); - WriteFile( hPipeClt, send_buf, TEST_BUF_LEN, &read, NULL ); + iosb.Status = 0xdeadbeef; + res = pNtSetInformationFile( server, &iosb, &fci, sizeof(fci), FileCompletionInformation ); + ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %#lx\n", res ); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %#lx\n", iosb.Status ); - if (get_msg(h)) - { - ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey ); - ok( ioSb.Information == 3, "Invalid ioSb.Information: %ld\n", ioSb.Information ); - ok( U(ioSb).Status == STATUS_SUCCESS, "Invalid ioSb.Status: %x\n", U(ioSb).Status); - ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue ); - ok( !memcmp( send_buf, recv_buf, TEST_BUF_LEN ), "Receive buffer (%x %x %x) did not match send buffer (%x %x %x)\n", recv_buf[0], recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2] ); - } - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + memset( send_buf, 0, TEST_BUF_LEN ); + memset( recv_buf, 0xde, TEST_BUF_LEN ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + ReadFile( server, recv_buf, TEST_BUF_LEN, &read, &o); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + WriteFile( client, send_buf, TEST_BUF_LEN, &read, NULL ); - memset( send_buf, 0, TEST_BUF_LEN ); - memset( recv_buf, 0xde, TEST_BUF_LEN ); - WriteFile( hPipeClt, send_buf, 2, &read, NULL ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); - ReadFile( hPipeSrv, recv_buf, 2, &read, &o); - count = get_pending_msgs(h); - ok( count == 1, "Unexpected msg count: %ld\n", count ); - if (get_msg(h)) - { - ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey ); - ok( ioSb.Information == 2, "Invalid ioSb.Information: %ld\n", ioSb.Information ); - ok( U(ioSb).Status == STATUS_SUCCESS, "Invalid ioSb.Status: %x\n", U(ioSb).Status); - ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue ); - ok( !memcmp( send_buf, recv_buf, 2 ), "Receive buffer (%x %x) did not match send buffer (%x %x)\n", recv_buf[0], recv_buf[1], send_buf[0], send_buf[1] ); - } + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( key == CKEY_SECOND, "Invalid completion key: %#Ix\n", key ); + ok( iosb.Information == 3, "Invalid iosb.Information: %Id\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "Invalid iosb.Status: %#lx\n", iosb.Status ); + ok( value == (ULONG_PTR)&o, "Invalid completion value: %#Ix\n", value ); + ok( !memcmp( send_buf, recv_buf, TEST_BUF_LEN ), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", + recv_buf[0], recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2] ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - ReadFile( hPipeSrv, recv_buf, TEST_BUF_LEN, &read, &o); - CloseHandle( hPipeSrv ); - count = get_pending_msgs(h); - ok( count == 1, "Unexpected msg count: %ld\n", count ); - if (get_msg(h)) - { - ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey ); - ok( ioSb.Information == 0, "Invalid ioSb.Information: %ld\n", ioSb.Information ); - /* wine sends wrong status here */ - ok( U(ioSb).Status == STATUS_PIPE_BROKEN, "Invalid ioSb.Status: %x\n", U(ioSb).Status); - ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue ); - } - } + memset( send_buf, 0, TEST_BUF_LEN ); + memset( recv_buf, 0xde, TEST_BUF_LEN ); + WriteFile( client, send_buf, 2, &read, NULL ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + ReadFile( server, recv_buf, 2, &read, &o); + count = get_pending_msgs(h); + ok( count == 1, "Unexpected msg count: %ld\n", count ); - CloseHandle( hPipeClt ); + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( key == CKEY_SECOND, "Invalid completion key: %#Ix\n", key ); + ok( iosb.Information == 2, "Invalid iosb.Information: %Id\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "Invalid iosb.Status: %#lx\n", iosb.Status ); + ok( value == (ULONG_PTR)&o, "Invalid completion value: %#Ix\n", value ); + ok( !memcmp( send_buf, recv_buf, 2 ), + "Receive buffer (%02x %02x) did not match send buffer (%02x %02x)\n", + recv_buf[0], recv_buf[1], send_buf[0], send_buf[1] ); + + ReadFile( server, recv_buf, TEST_BUF_LEN, &read, &o); + CloseHandle( server ); + count = get_pending_msgs(h); + ok( count == 1, "Unexpected msg count: %ld\n", count ); + + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( key == CKEY_SECOND, "Invalid completion key: %Ix\n", key ); + ok( iosb.Information == 0, "Invalid iosb.Information: %Id\n", iosb.Information ); + ok( iosb.Status == STATUS_PIPE_BROKEN, "Invalid iosb.Status: %lx\n", iosb.Status ); + ok( value == (ULONG_PTR)&o, "Invalid completion value: %Ix\n", value ); + + CloseHandle( client ); /* test associating a completion port with a handle after an async is queued */ - hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL ); - ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" ); - if (hPipeSrv == INVALID_HANDLE_VALUE ) - return; - hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); - ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" ); - if (hPipeClt != INVALID_HANDLE_VALUE) - { - OVERLAPPED o = {0,}; - BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; - int apc_count = 0; - DWORD read; - long count; + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); - memset( send_buf, 0, TEST_BUF_LEN ); - memset( recv_buf, 0xde, TEST_BUF_LEN ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); - ReadFile( hPipeSrv, recv_buf, TEST_BUF_LEN, &read, &o); + memset( send_buf, 0, TEST_BUF_LEN ); + memset( recv_buf, 0xde, TEST_BUF_LEN ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); + ReadFile( server, recv_buf, TEST_BUF_LEN, &read, &o); - U(iosb).Status = 0xdeadbeef; - res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation ); - ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %x\n", res ); - ok( U(iosb).Status == STATUS_SUCCESS, "iosb.Status invalid: %x\n", U(iosb).Status ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + iosb.Status = 0xdeadbeef; + res = pNtSetInformationFile( server, &iosb, &fci, sizeof(fci), FileCompletionInformation ); + ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %lx\n", res ); + ok( iosb.Status == STATUS_SUCCESS, "iosb.Status invalid: %lx\n", iosb.Status ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - WriteFile( hPipeClt, send_buf, TEST_BUF_LEN, &read, NULL ); + WriteFile( client, send_buf, TEST_BUF_LEN, &read, NULL ); - if (get_msg(h)) - { - ok( completionKey == CKEY_SECOND, "Invalid completion key: %lx\n", completionKey ); - ok( ioSb.Information == 3, "Invalid ioSb.Information: %ld\n", ioSb.Information ); - ok( U(ioSb).Status == STATUS_SUCCESS, "Invalid ioSb.Status: %x\n", U(ioSb).Status); - ok( completionValue == (ULONG_PTR)&o, "Invalid completion value: %lx\n", completionValue ); - ok( !memcmp( send_buf, recv_buf, TEST_BUF_LEN ), "Receive buffer (%x %x %x) did not match send buffer (%x %x %x)\n", recv_buf[0], recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2] ); - } - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( key == CKEY_SECOND, "Invalid completion key: %#Ix\n", key ); + ok( iosb.Information == 3, "Invalid iosb.Information: %Id\n", iosb.Information ); + ok( iosb.Status == STATUS_SUCCESS, "Invalid iosb.Status: %#lx\n", iosb.Status ); + ok( value == (ULONG_PTR)&o, "Invalid completion value: %#Ix\n", value ); + ok( !memcmp( send_buf, recv_buf, TEST_BUF_LEN ), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", + recv_buf[0], recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2] ); - /* using APCs on handle with associated completion port is not allowed */ - res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); - ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res); - } + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - CloseHandle( hPipeSrv ); - CloseHandle( hPipeClt ); + /* using APCs on handle with associated completion port is not allowed */ + res = pNtReadFile( server, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %lx\n", res); + + CloseHandle( server ); + CloseHandle( client ); /* test associating a completion port with a handle after an async using APC is queued */ - hPipeSrv = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, 1000, NULL ); - ok( hPipeSrv != INVALID_HANDLE_VALUE, "Cannot create named pipe\n" ); - if (hPipeSrv == INVALID_HANDLE_VALUE ) - return; - hPipeClt = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); - ok( hPipeClt != INVALID_HANDLE_VALUE, "Cannot connect to pipe\n" ); - if (hPipeClt != INVALID_HANDLE_VALUE) - { - BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; - int apc_count = 0; - DWORD read; - long count; + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); - memset( send_buf, 0, TEST_BUF_LEN ); - memset( recv_buf, 0xde, TEST_BUF_LEN ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + apc_count = 0; + memset( send_buf, 0, TEST_BUF_LEN ); + memset( recv_buf, 0xde, TEST_BUF_LEN ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); - ok(res == STATUS_PENDING, "NtReadFile returned %x\n", res); + res = pNtReadFile( server, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + ok(res == STATUS_PENDING, "NtReadFile returned %lx\n", res); - U(iosb).Status = 0xdeadbeef; - res = pNtSetInformationFile( hPipeSrv, &iosb, &fci, sizeof(fci), FileCompletionInformation ); - ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %x\n", res ); - ok( U(iosb).Status == STATUS_SUCCESS, "iosb.Status invalid: %x\n", U(iosb).Status ); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + iosb.Status = 0xdeadbeef; + res = pNtSetInformationFile( server, &iosb, &fci, sizeof(fci), FileCompletionInformation ); + ok( res == STATUS_SUCCESS, "NtSetInformationFile failed: %lx\n", res ); + ok( iosb.Status == STATUS_SUCCESS, "iosb.Status invalid: %lx\n", iosb.Status ); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - WriteFile( hPipeClt, send_buf, TEST_BUF_LEN, &read, NULL ); + WriteFile( client, send_buf, TEST_BUF_LEN, &read, NULL ); - ok(!apc_count, "apc_count = %u\n", apc_count); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + ok(!apc_count, "apc_count = %u\n", apc_count); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - SleepEx(1, TRUE); /* alertable sleep */ - ok(apc_count == 1, "apc was not called\n"); - count = get_pending_msgs(h); - ok( !count, "Unexpected msg count: %ld\n", count ); + SleepEx(1, TRUE); /* alertable sleep */ + ok(apc_count == 1, "apc was not called\n"); + count = get_pending_msgs(h); + ok( !count, "Unexpected msg count: %ld\n", count ); - /* using APCs on handle with associated completion port is not allowed */ - res = NtReadFile( hPipeSrv, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); - ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %x\n", res); - } + /* using APCs on handle with associated completion port is not allowed */ + res = pNtReadFile( server, NULL, apc, &apc_count, &iosb, recv_buf, sizeof(recv_buf), NULL, NULL ); + ok(res == STATUS_INVALID_PARAMETER, "NtReadFile returned %lx\n", res); - CloseHandle( hPipeSrv ); - CloseHandle( hPipeClt ); + CloseHandle( server ); + CloseHandle( client ); + pNtClose( h ); } static void test_file_full_size_information(void) @@ -1168,9 +1344,9 @@ static void test_file_full_size_information(void) /* Assume No Quota Settings configured on Wine Testbot */ res = pNtQueryVolumeInformationFile(h, &io, &ffsi, sizeof ffsi, FileFsFullSizeInformation); - ok(res == STATUS_SUCCESS, "cannot get attributes, res %x\n", res); + ok(res == STATUS_SUCCESS, "cannot get attributes, res %lx\n", res); res = pNtQueryVolumeInformationFile(h, &io, &fsi, sizeof fsi, FileFsSizeInformation); - ok(res == STATUS_SUCCESS, "cannot get attributes, res %x\n", res); + ok(res == STATUS_SUCCESS, "cannot get attributes, res %lx\n", res); /* Test for FileFsSizeInformation */ ok(fsi.TotalAllocationUnits.QuadPart > 0, @@ -1181,8 +1357,8 @@ static void test_file_full_size_information(void) wine_dbgstr_longlong(fsi.AvailableAllocationUnits.QuadPart)); /* Assume file system is NTFS */ - ok(fsi.BytesPerSector == 512, "[fsi] BytesPerSector expected 512, got %d\n",fsi.BytesPerSector); - ok(fsi.SectorsPerAllocationUnit == 8, "[fsi] SectorsPerAllocationUnit expected 8, got %d\n",fsi.SectorsPerAllocationUnit); + ok(fsi.BytesPerSector == 512, "[fsi] BytesPerSector expected 512, got %ld\n",fsi.BytesPerSector); + ok(fsi.SectorsPerAllocationUnit == 8, "[fsi] SectorsPerAllocationUnit expected 8, got %ld\n",fsi.SectorsPerAllocationUnit); ok(ffsi.TotalAllocationUnits.QuadPart > 0, "[ffsi] TotalAllocationUnits expected positive, got negative value 0x%s\n", @@ -1197,22 +1373,18 @@ static void test_file_full_size_information(void) "[ffsi] TotalAllocationUnits error fsi:0x%s, ffsi:0x%s\n", wine_dbgstr_longlong(fsi.TotalAllocationUnits.QuadPart), wine_dbgstr_longlong(ffsi.TotalAllocationUnits.QuadPart)); - ok(ffsi.CallerAvailableAllocationUnits.QuadPart == fsi.AvailableAllocationUnits.QuadPart, - "[ffsi] CallerAvailableAllocationUnits error fsi:0x%s, ffsi: 0x%s\n", - wine_dbgstr_longlong(fsi.AvailableAllocationUnits.QuadPart), - wine_dbgstr_longlong(ffsi.CallerAvailableAllocationUnits.QuadPart)); /* Assume file system is NTFS */ - ok(ffsi.BytesPerSector == 512, "[ffsi] BytesPerSector expected 512, got %d\n",ffsi.BytesPerSector); - ok(ffsi.SectorsPerAllocationUnit == 8, "[ffsi] SectorsPerAllocationUnit expected 8, got %d\n",ffsi.SectorsPerAllocationUnit); + ok(ffsi.BytesPerSector == 512, "[ffsi] BytesPerSector expected 512, got %ld\n",ffsi.BytesPerSector); + ok(ffsi.SectorsPerAllocationUnit == 8, "[ffsi] SectorsPerAllocationUnit expected 8, got %ld\n",ffsi.SectorsPerAllocationUnit); CloseHandle( h ); } static void test_file_basic_information(void) { + FILE_BASIC_INFORMATION fbi, fbi2; IO_STATUS_BLOCK io; - FILE_BASIC_INFORMATION fbi; HANDLE h; int res; int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL; @@ -1224,47 +1396,88 @@ static void test_file_basic_information(void) res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res); ok ( (fbi.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE, - "attribute %x not expected\n", fbi.FileAttributes ); + "attribute %lx not expected\n", fbi.FileAttributes ); + + memset(&fbi2, 0, sizeof(fbi2)); + fbi2.LastWriteTime.QuadPart = -1; + io.Status = 0xdeadbeef; + res = pNtSetInformationFile(h, &io, &fbi2, sizeof fbi2, FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't set -1 write time, NtSetInformationFile returned %x\n", res ); + ok ( io.Status == STATUS_SUCCESS, "can't set -1 write time, io.Status is %lx\n", io.Status ); + + memset(&fbi2, 0, sizeof(fbi2)); + fbi2.LastAccessTime.QuadPart = 0x200deadcafebeef; + io.Status = 0xdeadbeef; + res = pNtSetInformationFile(h, &io, &fbi2, sizeof(fbi2), FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't set access time, NtSetInformationFile returned %x\n", res ); + ok ( io.Status == STATUS_SUCCESS, "can't set access time, io.Status is %lx\n", io.Status ); + res = pNtQueryInformationFile(h, &io, &fbi, sizeof(fbi), FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't get access time, NtQueryInformationFile returned %x\n", res ); + ok ( io.Status == STATUS_SUCCESS, "can't get access time, io.Status is %lx\n", io.Status ); + ok ( fbi2.LastAccessTime.QuadPart == fbi.LastAccessTime.QuadPart, + "access time mismatch, set: %s get: %s\n", + wine_dbgstr_longlong(fbi2.LastAccessTime.QuadPart), + wine_dbgstr_longlong(fbi.LastAccessTime.QuadPart) ); + + memset(&fbi2, 0, sizeof(fbi2)); + res = pNtQueryInformationFile(h, &io, &fbi2, sizeof fbi2, FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't get write time 1, res %x\n", res); + ok ( fbi2.LastWriteTime.QuadPart == fbi.LastWriteTime.QuadPart, "write time mismatch, %s != %s\n", + wine_dbgstr_longlong(fbi2.LastWriteTime.QuadPart), + wine_dbgstr_longlong(fbi.LastWriteTime.QuadPart) ); + + memset(&fbi2, 0, sizeof(fbi2)); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile(h, &io, &fbi2, sizeof fbi2, FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't set nothing, NtSetInformationFile returned %x\n", res ); + ok ( io.Status == STATUS_SUCCESS, "can't set nothing, io.Status is %lx\n", io.Status ); + + memset(&fbi2, 0, sizeof(fbi2)); + res = pNtQueryInformationFile(h, &io, &fbi2, sizeof fbi2, FileBasicInformation); + ok ( res == STATUS_SUCCESS, "can't get write time 2, res %x\n", res); + ok ( fbi2.LastWriteTime.QuadPart == fbi.LastWriteTime.QuadPart, "write time changed, %s != %s\n", + wine_dbgstr_longlong(fbi2.LastWriteTime.QuadPart), + wine_dbgstr_longlong(fbi.LastWriteTime.QuadPart) ); /* Then SYSTEM */ /* Clear fbi to avoid setting times */ memset(&fbi, 0, sizeof(fbi)); fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't set system attribute, NtSetInformationFile returned %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set system attribute, io.Status is %x\n", U(io).Status ); + ok ( io.Status == STATUS_SUCCESS, "can't set system attribute, io.Status is %lx\n", io.Status ); memset(&fbi, 0, sizeof(fbi)); res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); - ok ( res == STATUS_SUCCESS, "can't get attributes\n"); - ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM (ok in old linux without xattr)\n", fbi.FileAttributes ); + ok ( res == STATUS_SUCCESS, "can't get system attribute\n"); + ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %lx not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes ); /* Then HIDDEN */ memset(&fbi, 0, sizeof(fbi)); fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); - ok ( res == STATUS_SUCCESS, "can't set system attribute, NtSetInformationFile returned %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set system attribute, io.Status is %x\n", U(io).Status ); + ok ( res == STATUS_SUCCESS, "can't set hidden attribute, NtSetInformationFile returned %x\n", res ); + ok ( io.Status == STATUS_SUCCESS, "can't set hidden attribute, io.Status is %lx\n", io.Status ); memset(&fbi, 0, sizeof(fbi)); res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); - ok ( res == STATUS_SUCCESS, "can't get attributes\n"); - ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN (ok in old linux without xattr)\n", fbi.FileAttributes ); + ok ( res == STATUS_SUCCESS, "can't get hidden attribute\n"); + ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %lx not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes ); /* Check NORMAL last of all (to make sure we can clear attributes) */ memset(&fbi, 0, sizeof(fbi)); fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't set normal attribute, NtSetInformationFile returned %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set normal attribute, io.Status is %x\n", U(io).Status ); + ok ( io.Status == STATUS_SUCCESS, "can't set normal attribute, io.Status is %lx\n", io.Status ); memset(&fbi, 0, sizeof(fbi)); res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); - ok ( res == STATUS_SUCCESS, "can't get attributes\n"); - todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not 0\n", fbi.FileAttributes ); + ok ( res == STATUS_SUCCESS, "can't get normal attribute\n"); + todo_wine ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %lx not 0\n", fbi.FileAttributes ); CloseHandle( h ); } @@ -1290,68 +1503,70 @@ static void test_file_all_information(void) res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation); ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res); ok ( (fai_buf.fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE, - "attribute %x not expected\n", fai_buf.fai.BasicInformation.FileAttributes ); + "attribute %lx not expected\n", fai_buf.fai.BasicInformation.FileAttributes ); /* Then SYSTEM */ /* Clear fbi to avoid setting times */ memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation)); fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_SYSTEM; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation); ok ( res == STATUS_INVALID_INFO_CLASS || broken(res == STATUS_NOT_IMPLEMENTED), "shouldn't be able to set FileAllInformation, res %x\n", res); - todo_wine ok ( U(io).Status == 0xdeadbeef, "shouldn't be able to set FileAllInformation, io.Status is %x\n", U(io).Status); - U(io).Status = 0xdeadbeef; + todo_wine ok ( io.Status == 0xdeadbeef, "shouldn't be able to set FileAllInformation, io.Status is %lx\n", io.Status); + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't set system attribute, res: %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %x\n", U(io).Status ); + ok ( io.Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %lx\n", io.Status ); memset(&fai_buf.fai, 0, sizeof(fai_buf.fai)); res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation); ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res); - ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM (ok in old linux without xattr)\n", fai_buf.fai.BasicInformation.FileAttributes ); + ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %lx not FILE_ATTRIBUTE_SYSTEM\n", fai_buf.fai.BasicInformation.FileAttributes ); /* Then HIDDEN */ memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation)); fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_HIDDEN; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't set system attribute, res: %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %x\n", U(io).Status ); + ok ( io.Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %lx\n", io.Status ); memset(&fai_buf.fai, 0, sizeof(fai_buf.fai)); res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation); ok ( res == STATUS_SUCCESS, "can't get attributes\n"); - ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN (ok in old linux without xattr)\n", fai_buf.fai.BasicInformation.FileAttributes ); + ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %lx not FILE_ATTRIBUTE_HIDDEN\n", fai_buf.fai.BasicInformation.FileAttributes ); /* Check NORMAL last of all (to make sure we can clear attributes) */ memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation)); fai_buf.fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL; - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; res = pNtSetInformationFile(h, &io, &fai_buf.fai.BasicInformation, sizeof fai_buf.fai.BasicInformation, FileBasicInformation); ok ( res == STATUS_SUCCESS, "can't set system attribute, res: %x\n", res ); - ok ( U(io).Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %x\n", U(io).Status ); + ok ( io.Status == STATUS_SUCCESS, "can't set system attribute, io.Status: %lx\n", io.Status ); memset(&fai_buf.fai, 0, sizeof(fai_buf.fai)); res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation); ok ( res == STATUS_SUCCESS, "can't get attributes\n"); - todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fai_buf.fai.BasicInformation.FileAttributes ); + todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %lx not FILE_ATTRIBUTE_NORMAL\n", fai_buf.fai.BasicInformation.FileAttributes ); CloseHandle( h ); } static void delete_object( WCHAR *path ) { - BOOL ret = DeleteFileW( path ); + BOOL ret = SetFileAttributesW( path, FILE_ATTRIBUTE_NORMAL ); + ok( ret || GetLastError() == ERROR_FILE_NOT_FOUND, "SetFileAttribute failed with %lu\n", GetLastError() ); + ret = DeleteFileW( path ); ok( ret || GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_ACCESS_DENIED, - "DeleteFileW failed with %u\n", GetLastError() ); + "DeleteFileW failed with %lu\n", GetLastError() ); if (!ret && GetLastError() == ERROR_ACCESS_DENIED) { ret = RemoveDirectoryW( path ); - ok( ret, "RemoveDirectoryW failed with %u\n", GetLastError() ); + ok( ret, "RemoveDirectoryW failed with %lu\n", GetLastError() ); } } -static void test_file_rename_information(void) +static void test_file_rename_information(FILE_INFORMATION_CLASS class) { static const WCHAR foo_txtW[] = {'\\','f','o','o','.','t','x','t',0}; static const WCHAR fooW[] = {'f','o','o',0}; @@ -1377,16 +1592,26 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + + if (class == FileRenameInformationEx && (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS)) + { + win_skip( "FileRenameInformationEx not supported\n" ); + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + return; + } + + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1394,7 +1619,7 @@ static void test_file_rename_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); @@ -1415,16 +1640,16 @@ static void test_file_rename_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1445,16 +1670,16 @@ static void test_file_rename_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1478,16 +1703,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1512,16 +1737,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1547,16 +1772,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1564,7 +1789,7 @@ static void test_file_rename_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); @@ -1594,16 +1819,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); + todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; todo_wine ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1634,16 +1859,16 @@ static void test_file_rename_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1670,16 +1895,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1704,16 +1929,16 @@ static void test_file_rename_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1740,16 +1965,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1777,16 +2002,16 @@ static void test_file_rename_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1813,16 +2038,16 @@ static void test_file_rename_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists= TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1852,16 +2077,16 @@ static void test_file_rename_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1886,16 +2111,16 @@ static void test_file_rename_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1919,16 +2144,16 @@ static void test_file_rename_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); - fri->ReplaceIfExists = TRUE; + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; fri->RootDirectory = NULL; fri->FileNameLength = name_str.Length; memcpy( fri->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1954,15 +2179,15 @@ static void test_file_rename_information(void) ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + lstrlenW(filename) * sizeof(WCHAR) ); - fri->ReplaceIfExists = FALSE; + fri->Flags = 0; fri->RootDirectory = handle2; fri->FileNameLength = lstrlenW(filename) * sizeof(WCHAR); memcpy( fri->FileName, filename, fri->FileNameLength ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1970,10 +2195,10 @@ static void test_file_rename_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", - wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); + wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); CloseHandle( handle ); @@ -1981,15 +2206,138 @@ static void test_file_rename_information(void) HeapFree( GetProcessHeap(), 0, fri ); delete_object( oldpath ); delete_object( newpath ); + + /* oldpath == newpath */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( oldpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = 0; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "got status %lx\n", res ); + ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); } -static void test_file_link_information(void) +static void test_file_rename_information_ex(void) +{ + static const WCHAR fooW[] = {'f','o','o',0}; + WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; + FILE_RENAME_INFORMATION *fri; + BOOL fileDeleted; + UNICODE_STRING name_str; + HANDLE handle, handle2; + IO_STATUS_BLOCK io; + NTSTATUS res; + + GetTempPathW( MAX_PATH, tmp_path ); + + /* oldpath is a file, newpath is a read-only file, with FILE_RENAME_REPLACE_IF_EXISTS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + + if (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS) + { + win_skip( "FileRenameInformationEx not supported\n" ); + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + return; + } + + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a read-only file, with FILE_RENAME_REPLACE_IF_EXISTS and FILE_RENAME_IGNORE_READONLY_ATTRIBUTE */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + ok( io.Status == STATUS_SUCCESS || io.Status == 0xdeadbeef, + "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_SUCCESS or STATUS_NOT_SUPPORTED, got %lx\n", res ); + + if (res == STATUS_SUCCESS) + { + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "file should not exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + } + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); +} + +static void test_file_link_information(FILE_INFORMATION_CLASS class) { static const WCHAR foo_txtW[] = {'\\','f','o','o','.','t','x','t',0}; static const WCHAR fooW[] = {'f','o','o',0}; WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16], *filename, *p; FILE_LINK_INFORMATION *fli; FILE_NAME_INFORMATION *fni; + WIN32_FIND_DATAW find_data; BOOL success, fileDeleted; UNICODE_STRING name_str; HANDLE handle, handle2; @@ -2009,16 +2357,26 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + + if (class == FileLinkInformationEx && (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS)) + { + win_skip( "FileLinkInformationEx not supported\n" ); + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + return; + } + + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2026,7 +2384,7 @@ static void test_file_link_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); @@ -2047,16 +2405,16 @@ static void test_file_link_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2077,16 +2435,16 @@ static void test_file_link_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2097,6 +2455,46 @@ static void test_file_link_information(void) delete_object( oldpath ); delete_object( newpath ); + /* oldpath is a file, newpath is a file, ReplaceIfExists = TRUE, different casing on link */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + wcsrchr( newpath, '\\' )[1] = 'F'; + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + handle = FindFirstFileW( newpath, &find_data ); + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %ld\n", GetLastError()); + if (handle != INVALID_HANDLE_VALUE) + { + todo_wine ok(!lstrcmpW(wcsrchr(newpath, '\\') + 1, find_data.cFileName), + "Link did not change casing on existing target file: got %s\n", wine_dbgstr_w(find_data.cFileName)); + } + + FindClose( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + /* oldpath is a file, newpath is a file, ReplaceIfExists = FALSE, target file opened */ res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); ok( res != 0, "failed to create temp file\n" ); @@ -2110,16 +2508,16 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2144,16 +2542,16 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2179,16 +2577,17 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef , "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY , + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2196,7 +2595,7 @@ static void test_file_link_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); @@ -2226,16 +2625,17 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); DeleteFileW( newpath ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2260,17 +2660,18 @@ static void test_file_link_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, - "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2297,17 +2698,18 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, - "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2332,16 +2734,17 @@ static void test_file_link_information(void) ok( res != 0, "failed to create temp file\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2368,16 +2771,17 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2405,17 +2809,18 @@ static void test_file_link_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, - "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2442,16 +2847,17 @@ static void test_file_link_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2481,16 +2887,17 @@ static void test_file_link_information(void) pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2515,16 +2922,16 @@ static void test_file_link_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2548,16 +2955,16 @@ static void test_file_link_information(void) ok( success != 0, "failed to create temp directory\n" ); pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); - fli->ReplaceIfExists = TRUE; + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; fli->RootDirectory = NULL; fli->FileNameLength = name_str.Length; memcpy( fli->FileName, name_str.Buffer, name_str.Length ); pRtlFreeUnicodeString( &name_str ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - todo_wine ok( U(io).Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %x\n", U(io).Status ); - ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2583,15 +2990,15 @@ static void test_file_link_information(void) ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + lstrlenW(filename) * sizeof(WCHAR) ); - fli->ReplaceIfExists = FALSE; + fli->Flags = 0; fli->RootDirectory = handle2; fli->FileNameLength = lstrlenW(filename) * sizeof(WCHAR); memcpy( fli->FileName, filename, fli->FileNameLength ); - U(io).Status = 0xdeadbeef; - res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformation ); - ok( U(io).Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %x\n", U(io).Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2599,7 +3006,7 @@ static void test_file_link_information(void) fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; ok( !lstrcmpiW(fni->FileName, oldpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(oldpath + 2), wine_dbgstr_w(fni->FileName) ); @@ -2610,6 +3017,168 @@ static void test_file_link_information(void) HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); delete_object( newpath ); + + /* oldpath == newpath */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( oldpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fli->Flags = 0; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "got io status %#lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "got status %lx\n", res ); + + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "got status %lx\n", res ); + ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + + /* oldpath == newpath, different casing on link */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + wcsrchr( oldpath, '\\' )[1] = 'F'; + pRtlDosPathNameToNtPathName_U( oldpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fli->Flags = 0; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + todo_wine ok( io.Status == 0xdeadbeef, "got io status %#lx\n", io.Status ); + ok( res == STATUS_OBJECT_NAME_COLLISION, "got status %lx\n", res ); + + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); + ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "got status %lx\n", res ); + ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" ); + + CloseHandle( handle ); + handle = FindFirstFileW( oldpath, &find_data ); + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFileW: failed, error %ld\n", GetLastError()); + if (handle != INVALID_HANDLE_VALUE) + { + todo_wine ok(!lstrcmpW(wcsrchr(oldpath, '\\') + 1, find_data.cFileName), + "Link did not change casing on same file: got %s\n", wine_dbgstr_w(find_data.cFileName)); + } + + FindClose( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); +} + +static void test_file_link_information_ex(void) +{ + static const WCHAR fooW[] = {'f','o','o',0}; + WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; + FILE_LINK_INFORMATION *fli; + BOOL fileDeleted; + UNICODE_STRING name_str; + HANDLE handle, handle2; + IO_STATUS_BLOCK io; + NTSTATUS res; + + GetTempPathW( MAX_PATH, tmp_path ); + + /* oldpath is a file, newpath is a read-only file, with FILE_LINK_REPLACE_IF_EXISTS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + + if (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS) + { + win_skip( "FileLinkInformationEx not supported\n" ); + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + return; + } + + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a read-only file, with FILE_LINK_REPLACE_IF_EXISTS and FILE_LINK_IGNORE_READONLY_ATTRIBUTE */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_IGNORE_READONLY_ATTRIBUTE; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); } static void test_file_both_information(void) @@ -2628,17 +3197,50 @@ static void test_file_both_information(void) CloseHandle( h ); } +static NTSTATUS nt_get_file_attrs(const char *name, DWORD *attrs) +{ + WCHAR nameW[MAX_PATH]; + FILE_BASIC_INFORMATION info; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, MAX_PATH ); + + *attrs = INVALID_FILE_ATTRIBUTES; + + if (!pRtlDosPathNameToNtPathName_U( nameW, &nt_name, NULL, NULL )) + return STATUS_UNSUCCESSFUL; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = pNtQueryAttributesFile( &attr, &info ); + pRtlFreeUnicodeString( &nt_name ); + + if (status == STATUS_SUCCESS) + *attrs = info.FileAttributes; + + return status; +} + static void test_file_disposition_information(void) { char tmp_path[MAX_PATH], buffer[MAX_PATH + 16]; DWORD dirpos; - HANDLE handle, handle2, mapping; + HANDLE handle, handle2, handle3, mapping; NTSTATUS res; IO_STATUS_BLOCK io; FILE_DISPOSITION_INFORMATION fdi; + FILE_DISPOSITION_INFORMATION_EX fdie; + FILE_STANDARD_INFORMATION fsi; BOOL fileDeleted; - DWORD fdi2; - void *ptr; + DWORD fdi2, size; + void *view; GetTempPathA( MAX_PATH, tmp_path ); @@ -2648,10 +3250,10 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); res = pNtSetInformationFile( handle, &io, &fdi, 0, FileDispositionInformation ); todo_wine - ok( res == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %x\n", res ); + ok( res == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %lx\n", res ); fdi2 = 0x100; res = pNtSetInformationFile( handle, &io, &fdi2, sizeof(fdi2), FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %x\n", res ); + ok( res == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %lx\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "File shouldn't have been deleted\n" ); @@ -2662,10 +3264,10 @@ static void test_file_disposition_information(void) handle = CreateFileA(buffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); res = pNtQueryInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "Unexpected NtQueryInformationFile result (expected STATUS_INVALID_INFO_CLASS, got %x)\n", res ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "Unexpected NtQueryInformationFile result (expected STATUS_INVALID_INFO_CLASS, got %lx)\n", res ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_ACCESS_DENIED, "unexpected FileDispositionInformation result (expected STATUS_ACCESS_DENIED, got %x)\n", res ); + ok( res == STATUS_ACCESS_DENIED, "unexpected FileDispositionInformation result (expected STATUS_ACCESS_DENIED, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "File shouldn't have been deleted\n" ); @@ -2677,11 +3279,106 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + todo_wine + ok(fsi.DeletePending, "Handle should be marked for deletion\n"); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "File should have been deleted\n" ); - DeleteFileA( buffer ); + + /* file exists until all handles to it get closed */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); + res = nt_get_file_attrs( buffer, &fdi2 ); + todo_wine + ok( res == STATUS_DELETE_PENDING, "got %#lx\n", res ); + /* can't open the deleted file */ + handle3 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + todo_wine + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + if (handle3 != INVALID_HANDLE_VALUE) + CloseHandle( handle3 ); + todo_wine + ok(GetLastError() == ERROR_ACCESS_DENIED, "got %lu\n", GetLastError()); + /* can't open the deleted file (wrong sharing mode) */ + handle3 = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, 0, 0); + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + todo_wine + ok(GetLastError() == ERROR_ACCESS_DENIED, "got %lu\n", GetLastError()); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + + /* file exists until all handles to it get closed */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + /* can open the marked for delete file (proper sharing mode) */ + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + res = nt_get_file_attrs( buffer, &fdi2 ); + ok( res == STATUS_SUCCESS, "got %#lx\n", res ); + /* can't open the marked for delete file (wrong sharing mode) */ + handle3 = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, 0, 0); + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + ok(GetLastError() == ERROR_SHARING_VIOLATION, "got %lu\n", GetLastError()); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + + /* file is deleted after handle with FILE_DISPOSITION_POSIX_SEMANTICS is closed */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + } + CloseHandle( handle2 ); + + /* file is deleted after handle with FILE_DISPOSITION_POSIX_SEMANTICS is closed */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + } /* cannot set disposition on readonly file */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); @@ -2690,7 +3387,7 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); + ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "File shouldn't have been deleted\n" ); @@ -2704,7 +3401,7 @@ static void test_file_disposition_information(void) fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); todo_wine - ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); + ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; todo_wine @@ -2712,47 +3409,125 @@ static void test_file_disposition_information(void) SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); DeleteFileA( buffer ); + /* set disposition on readonly file ignoring readonly attribute */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_SUCCESS + || broken(res == STATUS_INVALID_INFO_CLASS) /* win10 1507 & 32-bit 1607 */ + || broken(res == STATUS_NOT_SUPPORTED), /* win10 1709 & 64-bit 1607 */ + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + } + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); + /* can set disposition on file and then reset it */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "File shouldn't have been deleted\n" ); DeleteFileA( buffer ); - /* Delete-on-close flag doesn't change file disposition until a handle is closed */ + /* can't reset disposition if delete-on-close flag is specified */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "File should have been deleted\n" ); - DeleteFileA( buffer ); - /* Delete-on-close flag sets disposition when a handle is closed and then it could be changed back */ + /* can't reset disposition on duplicated handle if delete-on-close flag is specified */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); ok( DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle2, 0, FALSE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle2, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); CloseHandle( handle2 ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "File should have been deleted\n" ); + + /* can reset delete-on-close flag through FileDispositionInformationEx */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdie.Flags = FILE_DISPOSITION_ON_CLOSE; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + DeleteFileA( buffer ); + } + + /* DeleteFile fails for wrong sharing mode */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fileDeleted = DeleteFileA( buffer ); + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + ok(GetLastError() == ERROR_SHARING_VIOLATION, "got %lu\n", GetLastError()); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); DeleteFileA( buffer ); + /* DeleteFile succeeds for proper sharing mode */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + fileDeleted = DeleteFileA( buffer ); + ok( fileDeleted, "File should have been deleted\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + todo_wine + ok(fsi.DeletePending, "Handle should be marked for deletion\n"); + res = nt_get_file_attrs( buffer, &fdi2 ); + todo_wine + ok( res == STATUS_OBJECT_NAME_NOT_FOUND || broken(res == STATUS_DELETE_PENDING), "got %#lx\n", res ); + /* can't open the deleted file */ + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + todo_wine + ok( handle2 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + todo_wine + ok( GetLastError() == ERROR_FILE_NOT_FOUND || broken(GetLastError() == ERROR_ACCESS_DENIED), "got %lu\n", GetLastError()); + if (handle2 != INVALID_HANDLE_VALUE) + CloseHandle( handle2); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + /* can set disposition on a directory opened with proper access */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); DeleteFileA( buffer ); @@ -2761,27 +3536,131 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "Directory should have been deleted\n" ); - RemoveDirectoryA( buffer ); - /* RemoveDirectory sets directory disposition and it can be undone */ + /* RemoveDirectory fails for wrong sharing mode */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); DeleteFileA( buffer ); ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" ); handle = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); - RemoveDirectoryA( buffer ); - fdi.DoDeleteFile = FALSE; - res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + fileDeleted = RemoveDirectoryA( buffer ); + ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); + ok(GetLastError() == ERROR_SHARING_VIOLATION, "got %lu\n", GetLastError()); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); RemoveDirectoryA( buffer ); + /* RemoveDirectory succeeds for proper sharing mode */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" ); + handle = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + fileDeleted = RemoveDirectoryA( buffer ); + ok( fileDeleted, "Directory should have been deleted\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + todo_wine + ok(fsi.DeletePending, "Handle should be marked for deletion\n"); + res = nt_get_file_attrs( buffer, &fdi2 ); + todo_wine + ok( res == STATUS_OBJECT_NAME_NOT_FOUND || broken(res == STATUS_DELETE_PENDING), "got %#lx\n", res ); + /* can't open the deleted directory */ + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + todo_wine + ok( handle2 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + todo_wine + ok(GetLastError() == ERROR_FILE_NOT_FOUND || broken(GetLastError() == ERROR_ACCESS_DENIED), "got %lu\n", GetLastError()); + if (handle2 != INVALID_HANDLE_VALUE) CloseHandle( handle2 ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "Directory should have been deleted\n" ); + + /* directory exists until all handles to it get closed */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" ); + handle = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle2, &io, &fdi, sizeof fdi, FileDispositionInformation ); + ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %lx)\n", res ); + res = nt_get_file_attrs( buffer, &fdi2 ); + todo_wine + ok( res == STATUS_DELETE_PENDING, "got %#lx\n", res ); + /* can't open the deleted directory */ + handle3 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + todo_wine + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + if (handle3 != INVALID_HANDLE_VALUE) + CloseHandle( handle3 ); + todo_wine + ok(GetLastError() == ERROR_ACCESS_DENIED, "got %lu\n", GetLastError()); + /* can't open the deleted directory (wrong sharing mode) */ + handle3 = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + todo_wine + ok(GetLastError() == ERROR_ACCESS_DENIED, "got %lu\n", GetLastError()); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "Directory should have been deleted\n" ); + + /* directory exists until all handles to it get closed */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" ); + handle = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + /* can open the marked for delete directory (proper sharing mode) */ + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + /* can't open the marked for delete file (wrong sharing mode) */ + handle3 = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + ok( handle3 == INVALID_HANDLE_VALUE, "CreateFile should fail\n" ); + ok(GetLastError() == ERROR_SHARING_VIOLATION, "got %lu\n", GetLastError()); + CloseHandle( handle ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "Directory should have been deleted\n" ); + + /* can open a non-empty directory with FILE_FLAG_DELETE_ON_CLOSE */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + ok( CreateDirectoryA( buffer, NULL ), "CreateDirectory failed\n" ); + dirpos = lstrlenA( buffer ); + lstrcpyA( buffer + dirpos, "\\tst" ); + handle2 = CreateFileA(buffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + CloseHandle( handle2 ); + buffer[dirpos] = '\0'; + handle = CreateFileA(buffer, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); + SetLastError(0xdeadbeef); + CloseHandle( handle ); + ok(GetLastError() == 0xdeadbeef, "got %lu\n", GetLastError()); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); + buffer[dirpos] = '\\'; + fileDeleted = DeleteFileA( buffer ); + ok( fileDeleted, "File should have been deleted\n" ); + buffer[dirpos] = '\0'; + fileDeleted = RemoveDirectoryA( buffer ); + ok( fileDeleted, "Directory should have been deleted\n" ); + /* cannot set disposition on a non-empty directory */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); DeleteFileA( buffer ); @@ -2794,99 +3673,165 @@ static void test_file_disposition_information(void) CloseHandle( handle2 ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine - ok( res == STATUS_DIRECTORY_NOT_EMPTY, "unexpected FileDispositionInformation result (expected STATUS_DIRECTORY_NOT_EMPTY, got %x)\n", res ); - DeleteFileA( buffer ); + ok( res == STATUS_DIRECTORY_NOT_EMPTY, "unexpected FileDispositionInformation result (expected STATUS_DIRECTORY_NOT_EMPTY, got %lx)\n", res ); + fileDeleted = DeleteFileA( buffer ); + ok( fileDeleted, "File should have been deleted\n" ); buffer[dirpos] = '\0'; CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); - RemoveDirectoryA( buffer ); + fileDeleted = RemoveDirectoryA( buffer ); + ok( fileDeleted, "Directory should have been deleted\n" ); + + /* a file with an open mapping handle cannot be deleted */ - /* cannot set disposition on file with file mapping opened */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); - handle = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); - mapping = CreateFileMappingA( handle, NULL, PAGE_READWRITE, 0, 64 * 1024, "DelFileTest" ); - ok( mapping != NULL, "failed to create file mapping\n"); - fdi.DoDeleteFile = TRUE; - res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); - CloseHandle( handle ); - fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - ok( !fileDeleted, "File shouldn't have been deleted\n" ); - CloseHandle( mapping ); - DeleteFileA( buffer ); + handle = CreateFileA( buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "failed to create file, error %lu\n", GetLastError() ); + WriteFile(handle, "data", 4, &size, NULL); + mapping = CreateFileMappingA( handle, NULL, PAGE_READONLY, 0, 4, NULL ); + ok( !!mapping, "failed to create mapping, error %lu\n", GetLastError() ); + + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( !res, "got %#lx\n", res ); + + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( res == STATUS_CANNOT_DELETE, "got %#lx\n", res ); + res = GetFileAttributesA( buffer ); + ok( res != INVALID_FILE_ATTRIBUTES, "expected file to exist\n" ); + + CloseHandle( mapping ); + CloseHandle( handle ); + res = DeleteFileA( buffer ); + ok( res, "got error %lu\n", GetLastError() ); - /* can set disposition on file with file mapping closed */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); - handle = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); - mapping = CreateFileMappingA( handle, NULL, PAGE_READWRITE, 0, 64 * 1024, "DelFileTest" ); - ok( mapping != NULL, "failed to create file mapping\n"); + handle = CreateFileA( buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "failed to create file, error %lu\n", GetLastError() ); + WriteFile(handle, "data", 4, &size, NULL); + mapping = CreateFileMappingA( handle, NULL, PAGE_READONLY, 0, 4, NULL ); + ok( !!mapping, "failed to create mapping, error %lu\n", GetLastError() ); CloseHandle( mapping ); - fdi.DoDeleteFile = TRUE; - res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); - CloseHandle( handle ); - fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - ok( fileDeleted, "File should have been deleted\n" ); - DeleteFileA( buffer ); - /* cannot set disposition on file which is mapped to memory */ + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( !res, "got %#lx\n", res ); + + CloseHandle( handle ); + res = DeleteFileA( buffer ); + ok( !res, "expected failure\n" ); + ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); + + /* a file with an open view cannot be deleted */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); - handle = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); - mapping = CreateFileMappingA( handle, NULL, PAGE_READWRITE, 0, 64 * 1024, "DelFileTest" ); - ok( mapping != NULL, "failed to create file mapping\n"); - ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 ); - ok( ptr != NULL, "MapViewOfFile failed\n"); + handle = CreateFileA( buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "failed to create file, error %lu\n", GetLastError() ); + WriteFile(handle, "data", 4, &size, NULL); + mapping = CreateFileMappingA( handle, NULL, PAGE_READONLY, 0, 4, NULL ); + ok( !!mapping, "failed to create mapping, error %lu\n", GetLastError() ); + view = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4 ); + ok( !!view, "failed to map view, error %lu\n", GetLastError() ); CloseHandle( mapping ); - fdi.DoDeleteFile = TRUE; - res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); - CloseHandle( handle ); - fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - ok( !fileDeleted, "File shouldn't have been deleted\n" ); - UnmapViewOfFile( ptr ); - DeleteFileA( buffer ); - /* can set disposition on file which is mapped to memory and unmapped again */ + fdi.DoDeleteFile = FALSE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( !res, "got %#lx\n", res ); + + fdi.DoDeleteFile = TRUE; + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( res == STATUS_CANNOT_DELETE, "got %#lx\n", res ); + res = GetFileAttributesA( buffer ); + ok( res != INVALID_FILE_ATTRIBUTES, "expected file to exist\n" ); + + UnmapViewOfFile( view ); + CloseHandle( handle ); + res = DeleteFileA( buffer ); + ok( res, "got error %lu\n", GetLastError() ); + GetTempFileNameA( tmp_path, "dis", 0, buffer ); - handle = CreateFileA(buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); - ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); - mapping = CreateFileMappingA( handle, NULL, PAGE_READWRITE, 0, 64 * 1024, "DelFileTest" ); - ok( mapping != NULL, "failed to create file mapping\n"); - ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 ); - ok( ptr != NULL, "MapViewOfFile failed\n"); + handle = CreateFileA( buffer, GENERIC_READ | GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "failed to create file, error %lu\n", GetLastError() ); + WriteFile(handle, "data", 4, &size, NULL); + mapping = CreateFileMappingA( handle, NULL, PAGE_READONLY, 0, 4, NULL ); + ok( !!mapping, "failed to create mapping, error %lu\n", GetLastError() ); + view = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4 ); + ok( !!view, "failed to map view, error %lu\n", GetLastError() ); CloseHandle( mapping ); - UnmapViewOfFile( ptr ); + UnmapViewOfFile( view ); + fdi.DoDeleteFile = TRUE; - res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); + res = pNtSetInformationFile( handle, &io, &fdi, sizeof(fdi), FileDispositionInformation ); + ok( !res, "got %#lx\n", res ); + CloseHandle( handle ); - fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - ok( fileDeleted, "File should have been deleted\n" ); - DeleteFileA( buffer ); -} + res = DeleteFileA( buffer ); + ok( !res, "expected failure\n" ); + ok( GetLastError() == ERROR_FILE_NOT_FOUND, "got error %lu\n", GetLastError() ); -static void test_iocompletion(void) -{ - HANDLE h = INVALID_HANDLE_VALUE; - NTSTATUS res; + /* pending delete flag is shared across handles */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + res = NtQueryInformationFile(handle2, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + fdi.DoDeleteFile = TRUE; + res = NtSetInformationFile(handle, &io, &fdi, sizeof(fdi), FileDispositionInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + res = NtQueryInformationFile(handle2, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + todo_wine + ok(fsi.DeletePending, "Handle should be marked for deletion\n"); + fdi.DoDeleteFile = FALSE; + res = NtSetInformationFile(handle2, &io, &fdi, sizeof(fdi), FileDispositionInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + CloseHandle(handle); + CloseHandle(handle2); + res = GetFileAttributesA( buffer ); + todo_wine + ok( res != INVALID_FILE_ATTRIBUTES, "expected file to exist\n" ); - res = pNtCreateIoCompletion( &h, IO_COMPLETION_ALL_ACCESS, NULL, 0); - - ok( res == 0, "NtCreateIoCompletion anonymous failed: %x\n", res ); - ok( h && h != INVALID_HANDLE_VALUE, "Invalid handle returned\n" ); - - if ( h && h != INVALID_HANDLE_VALUE) - { - test_iocp_setcompletion(h); - test_iocp_fileio(h); - pNtClose(h); - } + /* pending delete flag is shared across handles (even after closing) */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + res = NtQueryInformationFile(handle2, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + fdi.DoDeleteFile = TRUE; + res = NtSetInformationFile(handle, &io, &fdi, sizeof(fdi), FileDispositionInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + res = NtQueryInformationFile(handle2, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + todo_wine + ok(fsi.DeletePending, "Handle should be marked for deletion\n"); + fdi.DoDeleteFile = FALSE; + res = NtSetInformationFile(handle2, &io, &fdi, sizeof(fdi), FileDispositionInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + CloseHandle(handle2); + res = NtQueryInformationFile(handle, &io, &fsi, sizeof(fsi), FileStandardInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile failed %lx\n", res); + ok(!fsi.DeletePending, "Handle shouldn't be marked for deletion\n"); + CloseHandle(handle); + res = GetFileAttributesA( buffer ); + todo_wine + ok( res != INVALID_FILE_ATTRIBUTES, "expected file to exist\n" ); } static void test_file_name_information(void) @@ -2937,33 +3882,33 @@ static void test_file_name_information(void) ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n"); hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileNameInformation ); - ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x.\n", hr); + ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#lx.\n", hr); memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileNameInformation ); - ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n", + ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_BUFFER_OVERFLOW); - ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n", - U(io).Status, STATUS_BUFFER_OVERFLOW); - ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength); + ok(io.Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#lx, expected %#lx.\n", + io.Status, STATUS_BUFFER_OVERFLOW); + ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %lu\n", info->FileNameLength); ok(info->FileName[2] == 0xcccc, "info->FileName[2] is %#x, expected 0xcccc.\n", info->FileName[2]); ok(CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]), "info->FileName[1] is %p, expected %p.\n", CharLowerW((LPWSTR)(UINT_PTR)info->FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1])); - ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information); + ok(io.Information == sizeof(*info), "io.Information is %Iu\n", io.Information); memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation ); - ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS); - ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS); - ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %u\n", info->FileNameLength); + ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_SUCCESS); + ok(io.Status == STATUS_SUCCESS, "io.Status is %#lx, expected %#lx.\n", io.Status, STATUS_SUCCESS); + ok(info->FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), "info->FileNameLength is %lu\n", info->FileNameLength); ok(info->FileName[info->FileNameLength / sizeof(WCHAR)] == 0xcccc, "info->FileName[len] is %#x, expected 0xcccc.\n", info->FileName[info->FileNameLength / sizeof(WCHAR)]); info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0'; ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n", wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected )); ok(io.Information == FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength, - "io.Information is %lu, expected %u.\n", + "io.Information is %Iu, expected %lu.\n", io.Information, FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + info->FileNameLength); CloseHandle( h ); @@ -3006,7 +3951,7 @@ static void test_file_name_information(void) memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, info_size, FileNameInformation ); - ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS); + ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_SUCCESS); info->FileName[info->FileNameLength / sizeof(WCHAR)] = '\0'; ok(!lstrcmpiW( info->FileName, expected ), "info->FileName is %s, expected %s.\n", wine_dbgstr_w( info->FileName ), wine_dbgstr_w( expected )); @@ -3066,30 +4011,30 @@ static void test_file_all_name_information(void) ok(h != INVALID_HANDLE_VALUE, "Failed to open file.\n"); hr = pNtQueryInformationFile( h, &io, info, sizeof(*info) - 1, FileAllInformation ); - ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#x, expected %#x.\n", + ok(hr == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_INFO_LENGTH_MISMATCH); memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, sizeof(*info), FileAllInformation ); - ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#x, expected %#x.\n", + ok(hr == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_BUFFER_OVERFLOW); - ok(U(io).Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#x, expected %#x.\n", - U(io).Status, STATUS_BUFFER_OVERFLOW); + ok(io.Status == STATUS_BUFFER_OVERFLOW, "io.Status is %#lx, expected %#lx.\n", + io.Status, STATUS_BUFFER_OVERFLOW); ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), - "info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength ); + "info->NameInformation.FileNameLength is %lu\n", info->NameInformation.FileNameLength ); ok(info->NameInformation.FileName[2] == 0xcccc, "info->NameInformation.FileName[2] is %#x, expected 0xcccc.\n", info->NameInformation.FileName[2]); ok(CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]) == CharLowerW((LPWSTR)(UINT_PTR)expected[1]), "info->NameInformation.FileName[1] is %p, expected %p.\n", CharLowerW((LPWSTR)(UINT_PTR)info->NameInformation.FileName[1]), CharLowerW((LPWSTR)(UINT_PTR)expected[1])); - ok(io.Information == sizeof(*info), "io.Information is %lu\n", io.Information); + ok(io.Information == sizeof(*info), "io.Information is %Iu\n", io.Information); memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation ); - ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS); - ok(U(io).Status == STATUS_SUCCESS, "io.Status is %#x, expected %#x.\n", U(io).Status, STATUS_SUCCESS); + ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_SUCCESS); + ok(io.Status == STATUS_SUCCESS, "io.Status is %#lx, expected %#lx.\n", io.Status, STATUS_SUCCESS); ok(info->NameInformation.FileNameLength == lstrlenW( expected ) * sizeof(WCHAR), - "info->NameInformation.FileNameLength is %u\n", info->NameInformation.FileNameLength ); + "info->NameInformation.FileNameLength is %lu\n", info->NameInformation.FileNameLength ); ok(info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] == 0xcccc, "info->NameInformation.FileName[len] is %#x, expected 0xcccc.\n", info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)]); @@ -3099,7 +4044,7 @@ static void test_file_all_name_information(void) wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected )); ok(io.Information == FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + info->NameInformation.FileNameLength, - "io.Information is %lu\n", io.Information ); + "io.Information is %Iu\n", io.Information ); CloseHandle( h ); HeapFree( GetProcessHeap(), 0, info ); @@ -3141,7 +4086,7 @@ static void test_file_all_name_information(void) memset( info, 0xcc, info_size ); hr = pNtQueryInformationFile( h, &io, info, info_size, FileAllInformation ); - ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#x, expected %#x.\n", hr, STATUS_SUCCESS); + ok(hr == STATUS_SUCCESS, "NtQueryInformationFile returned %#lx, expected %#lx.\n", hr, STATUS_SUCCESS); info->NameInformation.FileName[info->NameInformation.FileNameLength / sizeof(WCHAR)] = '\0'; ok(!lstrcmpiW( info->NameInformation.FileName, expected ), "info->NameInformation.FileName is %s, expected %s.\n", wine_dbgstr_w( info->NameInformation.FileName ), wine_dbgstr_w( expected )); @@ -3153,15 +4098,35 @@ static void test_file_all_name_information(void) HeapFree( GetProcessHeap(), 0, file_name ); } +#define test_completion_flags(a,b) _test_completion_flags(__LINE__,a,b) +static void _test_completion_flags(unsigned line, HANDLE handle, DWORD expected_flags) +{ + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + + info.Flags = 0xdeadbeef; + status = pNtQueryInformationFile(handle, &io, &info, sizeof(info), + FileIoCompletionNotificationInformation); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok_(__FILE__,line)(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok_(__FILE__,line)(io.Information == sizeof(info), "Information = %Iu\n", io.Information); + ok_(__FILE__,line)((info.Flags & expected_flags) == expected_flags, "got %08lx\n", info.Flags); +} + static void test_file_completion_information(void) { + DECLSPEC_ALIGN(TEST_OVERLAPPED_READ_SIZE) static unsigned char aligned_buf[TEST_OVERLAPPED_READ_SIZE]; + static const char pipe_name[] = "\\\\.\\pipe\\test_file_completion_information"; static const char buf[] = "testdata"; FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + HANDLE port, h, completion, server, client; + FILE_COMPLETION_INFORMATION fci; + BYTE recv_buf[TEST_BUF_LEN]; + DWORD num_bytes, flag; OVERLAPPED ov, *pov; IO_STATUS_BLOCK io; NTSTATUS status; - DWORD num_bytes; - HANDLE port, h; ULONG_PTR key; BOOL ret; int i; @@ -3169,9 +4134,9 @@ static void test_file_completion_information(void) if (!(h = create_temp_file(0))) return; status = pNtSetInformationFile(h, &io, &info, sizeof(info) - 1, FileIoCompletionNotificationInformation); - ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_INFO_CLASS /* XP */, - "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) + ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS /* XP */), + "expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + if (status != STATUS_INFO_LENGTH_MISMATCH) { win_skip("FileIoCompletionNotificationInformation class not supported\n"); CloseHandle(h); @@ -3180,122 +4145,206 @@ static void test_file_completion_information(void) info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08lx\n", status); CloseHandle(h); if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_SET_EVENT_ON_HANDLE); info.Flags = FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_SET_EVENT_ON_HANDLE); + + info.Flags = 0; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_SET_EVENT_ON_HANDLE); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); + + info.Flags = 0xdeadbeef; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); CloseHandle(h); if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; - - info.Flags = ~0U; - status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok(!(info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS), "got %08x\n", info.Flags); + test_completion_flags(h, 0); memset(&ov, 0, sizeof(ov)); ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0); - ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); + ok(port != NULL, "CreateIoCompletionPort failed, error %lu\n", GetLastError()); for (i = 0; i < 10; i++) { SetLastError(0xdeadbeef); ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* Before Vista */, + "Unexpected result %#x, GetLastError() %lu.\n", ret, GetLastError()); if (ret || GetLastError() != ERROR_IO_PENDING) break; ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); - ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ok(ret, "GetOverlappedResult failed, error %lu\n", GetLastError()); ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); - ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ok(ret, "GetQueuedCompletionStatus failed, error %lu\n", GetLastError()); ret = FALSE; } if (ret) { - ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); key = 0; pov = NULL; ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); - ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); - ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); + ok(ret, "GetQueuedCompletionStatus failed, error %lu\n", GetLastError()); + ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %Ix\n", key); ok(pov == &ov, "expected %p, got %p\n", &ov, pov); } - else - win_skip("WriteFile never returned TRUE\n"); info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - - info.Flags = 0; - status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); for (i = 0; i < 10; i++) { SetLastError(0xdeadbeef); ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* Before Vista */, + "Unexpected result %#x, GetLastError() %lu.\n", ret, GetLastError()); if (ret || GetLastError() != ERROR_IO_PENDING) break; ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); - ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ok(ret, "GetOverlappedResult failed, error %lu\n", GetLastError()); ret = FALSE; } if (ret) { - ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); pov = (void *)0xdeadbeef; ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 500); ok(!ret, "GetQueuedCompletionStatus succeeded\n"); ok(pov == NULL, "expected NULL, got %p\n", pov); } - else - win_skip("WriteFile never returned TRUE\n"); info.Flags = 0; status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - - info.Flags = 0; - status = pNtQueryInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + test_completion_flags(h, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); for (i = 0; i < 10; i++) { SetLastError(0xdeadbeef); ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* Before Vista */, + "Unexpected result %#x, GetLastError() %lu.\n", ret, GetLastError()); if (ret || GetLastError() != ERROR_IO_PENDING) break; ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); - ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ok(ret, "GetOverlappedResult failed, error %lu\n", GetLastError()); ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); - ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ok(ret, "GetQueuedCompletionStatus failed, error %lu\n", GetLastError()); ret = FALSE; } if (ret) { - ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); pov = (void *)0xdeadbeef; ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); ok(!ret, "GetQueuedCompletionStatus succeeded\n"); ok(pov == NULL, "expected NULL, got %p\n", pov); } - else - win_skip("WriteFile never returned TRUE\n"); + + CloseHandle(port); + CloseHandle(h); + + if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING))) + return; + + port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %lu.\n", GetLastError()); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %#lx.\n", status); + test_completion_flags(h, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS); + + ret = WriteFile(h, aligned_buf, sizeof(aligned_buf), &num_bytes, &ov); + if (!ret && GetLastError() == ERROR_IO_PENDING) + { + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %lu.\n", GetLastError()); + ok(num_bytes == sizeof(aligned_buf), "expected sizeof(aligned_buf), got %lu.\n", num_bytes); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %lu.\n", GetLastError()); + } + ok(num_bytes == sizeof(aligned_buf), "expected sizeof(buf), got %lu.\n", num_bytes); + + SetLastError(0xdeadbeef); + ret = ReadFile(h, aligned_buf, sizeof(aligned_buf), &num_bytes, &ov); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "Unexpected result, ret %#x, error %lu.\n", + ret, GetLastError()); + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %lu.\n", GetLastError()); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %lu.\n", GetLastError()); CloseHandle(ov.hEvent); CloseHandle(port); CloseHandle(h); + + /* Test that setting FileCompletionInformation makes an overlapped file signaled unless FILE_SKIP_SET_EVENT_ON_HANDLE is set */ + for (flag = 0; flag <= FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; flag = flag ? flag << 1 : 1) + { + winetest_push_context("%#lx", flag); + + status = pNtCreateIoCompletion(&completion, IO_COMPLETION_ALL_ACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + + memset(&ov, 0, sizeof(ov)); + ReadFile(server, recv_buf, TEST_BUF_LEN, &num_bytes, &ov); + ok(!is_signaled(server), "Expected not signaled.\n"); + + info.Flags = flag; + status = pNtSetInformationFile(server, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + test_completion_flags(server, flag); + + fci.CompletionPort = completion; + fci.CompletionKey = CKEY_FIRST; + io.Status = 0xdeadbeef; + status = pNtSetInformationFile(server, &io, &fci, sizeof(fci), FileCompletionInformation); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(io.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx.\n", io.Status); + if (flag == FILE_SKIP_SET_EVENT_ON_HANDLE) + ok(!is_signaled(server), "Expected not signaled.\n"); + else + ok(is_signaled(server), "Expected signaled.\n"); + + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(completion); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + winetest_pop_context(); + } } static void test_file_id_information(void) @@ -3324,15 +4373,15 @@ static void test_file_id_information(void) ok( ret, "GetFileInformationByHandle failed\n" ); dwords = (DWORD *)&fid.VolumeSerialNumber; - ok( dwords[0] == info.dwVolumeSerialNumber, "expected %08x, got %08x\n", + ok( dwords[0] == info.dwVolumeSerialNumber, "expected %08lx, got %08lx\n", info.dwVolumeSerialNumber, dwords[0] ); ok( dwords[1] != 0x11111111, "expected != 0x11111111\n" ); dwords = (DWORD *)&fid.FileId; - ok( dwords[0] == info.nFileIndexLow, "expected %08x, got %08x\n", info.nFileIndexLow, dwords[0] ); - ok( dwords[1] == info.nFileIndexHigh, "expected %08x, got %08x\n", info.nFileIndexHigh, dwords[1] ); - ok( dwords[2] == 0, "expected 0, got %08x\n", dwords[2] ); - ok( dwords[3] == 0, "expected 0, got %08x\n", dwords[3] ); + ok( dwords[0] == info.nFileIndexLow, "expected %08lx, got %08lx\n", info.nFileIndexLow, dwords[0] ); + ok( dwords[1] == info.nFileIndexHigh, "expected %08lx, got %08lx\n", info.nFileIndexHigh, dwords[1] ); + ok( dwords[2] == 0, "expected 0, got %08lx\n", dwords[2] ); + ok( dwords[3] == 0, "expected 0, got %08lx\n", dwords[3] ); CloseHandle( h ); } @@ -3347,19 +4396,299 @@ static void test_file_access_information(void) if (!(h = create_temp_file(0))) return; status = pNtQueryInformationFile( h, &io, &info, sizeof(info) - 1, FileAccessInformation ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); status = pNtQueryInformationFile( (HANDLE)0xdeadbeef, &io, &info, sizeof(info), FileAccessInformation ); - ok( status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %08x\n", status ); + ok( status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %08lx\n", status ); memset(&info, 0x11, sizeof(info)); status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAccessInformation ); - ok( status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status ); - ok( info.AccessFlags == 0x13019f, "got %08x\n", info.AccessFlags ); + ok( status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status ); + ok( info.AccessFlags == 0x13019f, "got %08lx\n", info.AccessFlags ); CloseHandle( h ); } +static void test_file_attribute_tag_information(void) +{ + FILE_ATTRIBUTE_TAG_INFORMATION info; + FILE_BASIC_INFORMATION fbi = { 0 }; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE h; + + if (!(h = create_temp_file(0))) return; + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info) - 1, FileAttributeTagInformation ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status ); + + status = pNtQueryInformationFile( (HANDLE)0xdeadbeef, &io, &info, sizeof(info), FileAttributeTagInformation ); + ok( status == STATUS_INVALID_HANDLE, "got %#lx\n", status ); + + memset(&info, 0x11, sizeof(info)); + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAttributeTagInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + info.FileAttributes &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + ok( info.FileAttributes == FILE_ATTRIBUTE_ARCHIVE, "got attributes %#lx\n", info.FileAttributes ); + ok( !info.ReparseTag, "got reparse tag %#lx\n", info.ReparseTag ); + + fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM; + status = pNtSetInformationFile(h, &io, &fbi, sizeof(fbi), FileBasicInformation); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + + memset(&info, 0x11, sizeof(info)); + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAttributeTagInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + todo_wine ok( info.FileAttributes == FILE_ATTRIBUTE_SYSTEM, "got attributes %#lx\n", info.FileAttributes ); + ok( !info.ReparseTag, "got reparse tag %#lx\n", info.ReparseTag ); + + fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN; + status = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + + memset(&info, 0x11, sizeof(info)); + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileAttributeTagInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + todo_wine ok( info.FileAttributes == FILE_ATTRIBUTE_HIDDEN, "got attributes %#lx\n", info.FileAttributes ); + ok( !info.ReparseTag, "got reparse tag %#lx\n", info.ReparseTag ); + + CloseHandle( h ); +} + +static void test_file_stat_information(void) +{ + BY_HANDLE_FILE_INFORMATION info; + FILE_STAT_INFORMATION fsd; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE h; + BOOL ret; + + if (!(h = create_temp_file(0))) return; + + memset( &fsd, 0x11, sizeof(fsd) ); + status = pNtQueryInformationFile( h, &io, &fsd, sizeof(fsd), FileStatInformation ); + if (status == STATUS_NOT_IMPLEMENTED || status == STATUS_INVALID_INFO_CLASS) + { + win_skip( "FileStatInformation not supported\n" ); + CloseHandle( h ); + return; + } + ok( status == STATUS_SUCCESS, "query FileStatInformation returned %#lx\n", status ); + + memset( &info, 0x22, sizeof(info) ); + ret = GetFileInformationByHandle( h, &info ); + ok( ret, "GetFileInformationByHandle failed\n" ); + + ok( fsd.FileId.u.LowPart == info.nFileIndexLow, "expected %08lx, got %08lx\n", info.nFileIndexLow, fsd.FileId.u.LowPart ); + ok( fsd.FileId.u.HighPart == info.nFileIndexHigh, "expected %08lx, got %08lx\n", info.nFileIndexHigh, fsd.FileId.u.HighPart ); + ok( fsd.CreationTime.u.LowPart == info.ftCreationTime.dwLowDateTime, "expected %08lx, got %08lx\n", + info.ftCreationTime.dwLowDateTime, fsd.CreationTime.u.LowPart ); + ok( fsd.CreationTime.u.HighPart == info.ftCreationTime.dwHighDateTime, "expected %08lx, got %08lx\n", + info.ftCreationTime.dwHighDateTime, fsd.CreationTime.u.HighPart ); + ok( fsd.LastAccessTime.u.LowPart == info.ftLastAccessTime.dwLowDateTime, "expected %08lx, got %08lx\n", + info.ftLastAccessTime.dwLowDateTime, fsd.LastAccessTime.u.LowPart ); + ok( fsd.LastAccessTime.u.HighPart == info.ftLastAccessTime.dwHighDateTime, "expected %08lx, got %08lx\n", + info.ftLastAccessTime.dwHighDateTime, fsd.LastAccessTime.u.HighPart ); + ok( fsd.LastWriteTime.u.LowPart == info.ftLastWriteTime.dwLowDateTime, "expected %08lx, got %08lx\n", + info.ftLastWriteTime.dwLowDateTime, fsd.LastWriteTime.u.LowPart ); + ok( fsd.LastWriteTime.u.HighPart == info.ftLastWriteTime.dwHighDateTime, "expected %08lx, got %08lx\n", + info.ftLastWriteTime.dwHighDateTime, fsd.LastWriteTime.u.HighPart ); + /* TODO: ChangeTime */ + /* TODO: AllocationSize */ + ok( fsd.EndOfFile.u.LowPart == info.nFileSizeLow, "expected %08lx, got %08lx\n", info.nFileSizeLow, fsd.EndOfFile.u.LowPart ); + ok( fsd.EndOfFile.u.HighPart == info.nFileSizeHigh, "expected %08lx, got %08lx\n", info.nFileSizeHigh, fsd.EndOfFile.u.HighPart ); + ok( fsd.FileAttributes == info.dwFileAttributes, "expected %08lx, got %08lx\n", info.dwFileAttributes, fsd.FileAttributes ); + ok( !fsd.ReparseTag, "got reparse tag %#lx\n", fsd.ReparseTag ); + ok( fsd.NumberOfLinks == info.nNumberOfLinks, "expected %08lx, got %08lx\n", info.nNumberOfLinks, fsd.NumberOfLinks ); + ok( fsd.EffectiveAccess == FILE_ALL_ACCESS, "got %08lx\n", fsd.EffectiveAccess ); + + CloseHandle( h ); +} + +static void rename_file( HANDLE h, const WCHAR *filename ) +{ + FILE_RENAME_INFORMATION *fri; + UNICODE_STRING ntpath; + IO_STATUS_BLOCK io; + NTSTATUS status; + BOOLEAN ret; + ULONG size; + + ret = pRtlDosPathNameToNtPathName_U( filename, &ntpath, NULL, NULL ); + ok( ret, "RtlDosPathNameToNtPathName_U failed\n" ); + + size = offsetof( FILE_RENAME_INFORMATION, FileName ) + ntpath.Length; + fri = HeapAlloc( GetProcessHeap(), 0, size ); + ok( fri != NULL, "HeapAlloc failed\n" ); + fri->ReplaceIfExists = TRUE; + fri->RootDirectory = NULL; + fri->FileNameLength = ntpath.Length; + memcpy( fri->FileName, ntpath.Buffer, ntpath.Length ); + pRtlFreeUnicodeString( &ntpath ); + + status = pNtSetInformationFile( h, &io, fri, size, FileRenameInformation ); + HeapFree( GetProcessHeap(), 0, fri ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); +} + +static void test_dotfile_file_attributes(void) +{ + char temppath[MAX_PATH], filename[MAX_PATH]; + WCHAR temppathW[MAX_PATH], filenameW[MAX_PATH]; + FILE_BASIC_INFORMATION info = { 0 }; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD attrs; + HANDLE h; + + GetTempPathA( MAX_PATH, temppath ); + GetTempFileNameA( temppath, ".foo", 0, filename ); + h = CreateFileA( filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0 ); + ok( h != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + + status = nt_get_file_attrs(filename, &attrs); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(attrs & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", attrs ); + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + info.FileAttributes = FILE_ATTRIBUTE_SYSTEM; + status = pNtSetInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + + status = nt_get_file_attrs(filename, &attrs); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( attrs & FILE_ATTRIBUTE_SYSTEM, "got attributes %#lx\n", attrs ); + ok( !(attrs & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", attrs ); + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( info.FileAttributes & FILE_ATTRIBUTE_SYSTEM, "got attributes %#lx\n", info.FileAttributes ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + CloseHandle( h ); + + GetTempPathW( MAX_PATH, temppathW ); + GetTempFileNameW( temppathW, L"foo", 0, filenameW ); + h = CreateFileW( filenameW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0 ); + ok( h != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + + GetTempFileNameW( temppathW, L".foo", 0, filenameW ); + winetest_push_context("foo -> .foo"); + rename_file( h, filenameW ); + winetest_pop_context(); + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + GetTempFileNameW( temppathW, L"foo", 0, filenameW ); + winetest_push_context(".foo -> foo"); + rename_file( h, filenameW ); + winetest_pop_context(); + + status = pNtQueryInformationFile( h, &io, &info, sizeof(info), FileBasicInformation ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( !(info.FileAttributes & FILE_ATTRIBUTE_HIDDEN), "got attributes %#lx\n", info.FileAttributes ); + + CloseHandle( h ); +} + +static void test_file_mode(void) +{ + UNICODE_STRING file_name, pipe_dev_name, mountmgr_dev_name, mailslot_dev_name; + WCHAR tmp_path[MAX_PATH], dos_file_name[MAX_PATH]; + FILE_MODE_INFORMATION mode; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + HANDLE file; + unsigned i; + DWORD res, access; + NTSTATUS status; + + const struct { + UNICODE_STRING *file_name; + ULONG options; + ULONG mode; + } option_tests[] = { + { &file_name, 0, 0 }, + { &file_name, FILE_NON_DIRECTORY_FILE, 0 }, + { &file_name, FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY, FILE_SEQUENTIAL_ONLY }, + { &file_name, FILE_WRITE_THROUGH, FILE_WRITE_THROUGH }, + { &file_name, FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_ALERT }, + { &file_name, FILE_NO_INTERMEDIATE_BUFFERING, FILE_NO_INTERMEDIATE_BUFFERING }, + { &file_name, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, FILE_SYNCHRONOUS_IO_NONALERT }, + { &file_name, FILE_DELETE_ON_CLOSE, 0 }, + { &file_name, FILE_RANDOM_ACCESS | FILE_NO_COMPRESSION, 0 }, + { &pipe_dev_name, 0, 0 }, + { &pipe_dev_name, FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_ALERT }, + { &mailslot_dev_name, 0, 0 }, + { &mailslot_dev_name, FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_ALERT }, + { &mountmgr_dev_name, 0, 0 }, + { &mountmgr_dev_name, FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO_ALERT } + }; + + static WCHAR pipe_devW[] = {'\\','?','?','\\','P','I','P','E','\\'}; + static WCHAR mailslot_devW[] = {'\\','?','?','\\','M','A','I','L','S','L','O','T','\\'}; + static WCHAR mountmgr_devW[] = + {'\\','?','?','\\','M','o','u','n','t','P','o','i','n','t','M','a','n','a','g','e','r'}; + + GetTempPathW(MAX_PATH, tmp_path); + res = GetTempFileNameW(tmp_path, fooW, 0, dos_file_name); + ok(res, "GetTempFileNameW failed: %lu\n", GetLastError()); + pRtlDosPathNameToNtPathName_U( dos_file_name, &file_name, NULL, NULL ); + + pipe_dev_name.Buffer = pipe_devW; + pipe_dev_name.Length = sizeof(pipe_devW); + pipe_dev_name.MaximumLength = sizeof(pipe_devW); + + mailslot_dev_name.Buffer = mailslot_devW; + mailslot_dev_name.Length = sizeof(mailslot_devW); + mailslot_dev_name.MaximumLength = sizeof(mailslot_devW); + + mountmgr_dev_name.Buffer = mountmgr_devW; + mountmgr_dev_name.Length = sizeof(mountmgr_devW); + mountmgr_dev_name.MaximumLength = sizeof(mountmgr_devW); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + for (i = 0; i < ARRAY_SIZE(option_tests); i++) + { + attr.ObjectName = option_tests[i].file_name; + access = SYNCHRONIZE; + + if (option_tests[i].file_name == &file_name) + { + file = CreateFileW(dos_file_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError()); + CloseHandle(file); + access |= GENERIC_WRITE | DELETE; + } + + status = pNtOpenFile(&file, access, &attr, &io, 0, option_tests[i].options); + ok(status == STATUS_SUCCESS, "[%u] NtOpenFile failed: %lx\n", i, status); + + memset(&mode, 0xcc, sizeof(mode)); + status = pNtQueryInformationFile(file, &io, &mode, sizeof(mode), FileModeInformation); + ok(status == STATUS_SUCCESS, "[%u] can't get FileModeInformation: %lx\n", i, status); + ok(mode.Mode == option_tests[i].mode, "[%u] Mode = %lx, expected %lx\n", + i, mode.Mode, option_tests[i].mode); + + pNtClose(file); + if (option_tests[i].file_name == &file_name) + DeleteFileW(dos_file_name); + } + + pRtlFreeUnicodeString(&file_name); +} + static void test_query_volume_information_file(void) { NTSTATUS status; @@ -3382,33 +4711,30 @@ static void test_query_volume_information_file(void) status = pNtOpenFile( &dir, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); ZeroMemory( buf, sizeof(buf) ); - U(io).Status = 0xdadadada; + io.Status = 0xdadadada; io.Information = 0xcacacaca; status = pNtQueryVolumeInformationFile( dir, &io, buf, sizeof(buf), FileFsVolumeInformation ); ffvi = (FILE_FS_VOLUME_INFORMATION *)buf; - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", status); - ok(U(io).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", U(io).Status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %ld\n", status); + ok(io.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %ld\n", io.Status); -todo_wine -{ ok(io.Information == (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + ffvi->VolumeLabelLength), - "expected %d, got %lu\n", (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + ffvi->VolumeLabelLength), + "expected %ld, got %Iu\n", (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + ffvi->VolumeLabelLength), io.Information); - ok(ffvi->VolumeCreationTime.QuadPart != 0, "Missing VolumeCreationTime\n"); + todo_wine ok(ffvi->VolumeCreationTime.QuadPart != 0, "Missing VolumeCreationTime\n"); ok(ffvi->VolumeSerialNumber != 0, "Missing VolumeSerialNumber\n"); ok(ffvi->SupportsObjects == 1,"expected 1, got %d\n", ffvi->SupportsObjects); -} - ok(ffvi->VolumeLabelLength == lstrlenW(ffvi->VolumeLabel) * sizeof(WCHAR), "got %d\n", ffvi->VolumeLabelLength); + ok(ffvi->VolumeLabelLength == lstrlenW(ffvi->VolumeLabel) * sizeof(WCHAR), "got %ld\n", ffvi->VolumeLabelLength); - trace("VolumeSerialNumber: %x VolumeLabelName: %s\n", ffvi->VolumeSerialNumber, wine_dbgstr_w(ffvi->VolumeLabel)); + trace("VolumeSerialNumber: %lx VolumeLabelName: %s\n", ffvi->VolumeSerialNumber, wine_dbgstr_w(ffvi->VolumeLabel)); CloseHandle( dir ); } @@ -3435,24 +4761,24 @@ static void test_query_attribute_information_file(void) status = pNtOpenFile( &dir, SYNCHRONIZE|FILE_LIST_DIRECTORY, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT ); - ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status ); + ok( !status, "open %s failed %lx\n", wine_dbgstr_w(nameW.Buffer), status ); pRtlFreeUnicodeString( &nameW ); ZeroMemory( buf, sizeof(buf) ); - U(io).Status = 0xdadadada; + io.Status = 0xdadadada; io.Information = 0xcacacaca; status = pNtQueryVolumeInformationFile( dir, &io, buf, sizeof(buf), FileFsAttributeInformation ); ffai = (FILE_FS_ATTRIBUTE_INFORMATION *)buf; - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", status); - ok(U(io).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", U(io).Status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %ld\n", status); + ok(io.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %ld\n", io.Status); ok(ffai->FileSystemAttributes != 0, "Missing FileSystemAttributes\n"); ok(ffai->MaximumComponentNameLength != 0, "Missing MaximumComponentNameLength\n"); ok(ffai->FileSystemNameLength != 0, "Missing FileSystemNameLength\n"); - trace("FileSystemAttributes: %x MaximumComponentNameLength: %x FileSystemName: %s\n", + trace("FileSystemAttributes: %lx MaximumComponentNameLength: %lx FileSystemName: %s\n", ffai->FileSystemAttributes, ffai->MaximumComponentNameLength, wine_dbgstr_wn(ffai->FileSystemName, ffai->FileSystemNameLength / sizeof(WCHAR))); @@ -3507,29 +4833,29 @@ static void test_NtCreateFile(void) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; - for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + for (i = 0; i < ARRAY_SIZE(td); i++) { status = pNtCreateFile(&handle, GENERIC_READ, &attr, &io, NULL, td[i].attrib_in, FILE_SHARE_READ|FILE_SHARE_WRITE, td[i].disposition, 0, NULL, 0); - ok(status == td[i].status, "%d: expected %#x got %#x\n", i, td[i].status, status); + ok(status == td[i].status, "%ld: expected %#lx got %#lx\n", i, td[i].status, status); if (!status) { - ok(io.Information == td[i].result,"%d: expected %#x got %#lx\n", i, td[i].result, io.Information); + ok(io.Information == td[i].result,"%ld: expected %#lx got %#Ix\n", i, td[i].result, io.Information); ret = GetFileAttributesW(path); ret &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; /* FIXME: leave only 'else' case below once Wine is fixed */ if (ret != td[i].attrib_out) { - todo_wine - ok(ret == td[i].attrib_out, "%d: expected %#x got %#x\n", i, td[i].attrib_out, ret); + todo_wine + ok(ret == td[i].attrib_out, "%ld: expected %#lx got %#lx\n", i, td[i].attrib_out, ret); SetFileAttributesW(path, td[i].attrib_out); } else - ok(ret == td[i].attrib_out, "%d: expected %#x got %#x\n", i, td[i].attrib_out, ret); + ok(ret == td[i].attrib_out, "%ld: expected %#lx got %#lx\n", i, td[i].attrib_out, ret); CloseHandle(handle); } @@ -3544,83 +4870,18 @@ static void test_NtCreateFile(void) pRtlFreeUnicodeString( &nameW ); SetFileAttributesW(path, FILE_ATTRIBUTE_ARCHIVE); DeleteFileW( path ); -} -static void test_readonly(void) -{ - static const WCHAR fooW[] = {'f','o','o',0}; - NTSTATUS status; - HANDLE handle; - WCHAR path[MAX_PATH]; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - UNICODE_STRING nameW; - - GetTempPathW(MAX_PATH, path); - GetTempFileNameW(path, fooW, 0, path); - DeleteFileW(path); + wcscat( path, L"\\" ); pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL); - attr.Length = sizeof(attr); - attr.RootDirectory = NULL; - attr.ObjectName = &nameW; - attr.Attributes = OBJ_CASE_INSENSITIVE; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - - status = pNtCreateFile(&handle, GENERIC_READ, &attr, &io, NULL, FILE_ATTRIBUTE_READONLY, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_CREATE, 0, NULL, 0); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, GENERIC_WRITE, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_ACCESS_DENIED, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, GENERIC_READ, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, FILE_READ_ATTRIBUTES, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, FILE_WRITE_ATTRIBUTES, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, DELETE, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, READ_CONTROL, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, WRITE_DAC, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, WRITE_OWNER, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle(handle); - - status = pNtOpenFile(&handle, SYNCHRONIZE, &attr, &io, - FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN_FOR_BACKUP_INTENT); - ok(status == STATUS_SUCCESS, "got %#x\n", status); - CloseHandle( handle ); - - pRtlFreeUnicodeString(&nameW); - SetFileAttributesW(path, FILE_ATTRIBUTE_ARCHIVE); - DeleteFileW(path); + status = pNtCreateFile( &handle, GENERIC_READ, &attr, &io, NULL, + 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, 0, NULL, 0); + ok( status == STATUS_OBJECT_NAME_INVALID, "failed %s %lx\n", debugstr_w(nameW.Buffer), status ); + status = pNtCreateFile( &handle, GENERIC_READ, &attr, &io, NULL, + 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, + FILE_DIRECTORY_FILE, NULL, 0); + ok( !status, "failed %s %lx\n", debugstr_w(nameW.Buffer), status ); + RemoveDirectoryW( path ); } static void test_read_write(void) @@ -3636,213 +4897,213 @@ static void test_read_write(void) event = CreateEventA( NULL, TRUE, FALSE, NULL ); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtReadFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtReadFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, NULL, sizeof(buf), &offset, NULL); - ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtWriteFile(INVALID_HANDLE_VALUE, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); hfile = create_temp_file(0); if (!hfile) return; - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); - ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; SetEvent(event); status = pNtWriteFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); - ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_INVALID_USER_BUFFER, "expected STATUS_INVALID_USER_BUFFER, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); ok(!is_signaled(event), "event is not signaled\n"); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; SetEvent(event); status = pNtReadFile(hfile, event, NULL, NULL, &iob, NULL, sizeof(contents), NULL, NULL); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); ok(is_signaled(event), "event is not signaled\n"); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; SetEvent(event); status = pNtReadFile(hfile, event, NULL, NULL, &iob, (void*)0xdeadbeef, sizeof(contents), NULL, NULL); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); ok(is_signaled(event), "event is not signaled\n"); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, 7, NULL, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 7, "expected 7, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 7, "expected 7, got %Iu\n", iob.Information); SetFilePointer(hfile, 0, NULL, FILE_BEGIN); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)-1 /* FILE_WRITE_TO_END_OF_FILE */; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents + 7, sizeof(contents) - 7, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == sizeof(contents) - 7, "expected sizeof(contents)-7, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == sizeof(contents) - 7, "expected sizeof(contents)-7, got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(INVALID_HANDLE_VALUE, buf, 0, &bytes, NULL); ok(!ret, "ReadFile should fail\n"); - ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, 0, &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); SetFilePointer(hfile, 0, NULL, FILE_BEGIN); bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n"); for (i = -20; i < -1; i++) { if (i == -2) continue; - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)i; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL); - ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "%ld: expected STATUS_INVALID_PARAMETER, got %#lx\n", i, status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); } SetFilePointer(hfile, sizeof(contents) - 4, NULL, FILE_BEGIN); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 4, "expected 4, got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL); - ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status); - ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", status); + ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); SetFilePointer(hfile, 0, NULL, FILE_BEGIN); bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n"); ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); SetFilePointer(hfile, 0, NULL, FILE_BEGIN); bytes = 0; SetLastError(0xdeadbeef); ret = WriteFile(hfile, contents, sizeof(contents), &bytes, NULL); - ok(ret, "WriteFile error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); + ok(ret, "WriteFile error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); /* test reading beyond EOF */ bytes = -1; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); bytes = -1; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, 0, &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); bytes = -1; SetLastError(0xdeadbeef); ret = ReadFile(hfile, NULL, 0, &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); - S(U(ovl)).Offset = sizeof(contents); - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = sizeof(contents); + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; @@ -3850,82 +5111,82 @@ static void test_read_write(void) SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl); ok(!ret, "ReadFile should fail\n"); - ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); - S(U(ovl)).Offset = sizeof(contents); - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = sizeof(contents); + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = -1; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, 0, &bytes, &ovl); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL); - ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status); - ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", status); + ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, NULL, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = sizeof(contents); status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status); - ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", status); + ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = sizeof(contents); status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status); - ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", status); + ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)-2 /* FILE_USE_FILE_POINTER_POSITION */; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); for (i = -20; i < 0; i++) { if (i == -2) continue; - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)i; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "%ld: expected STATUS_INVALID_PARAMETER, got %#lx\n", i, status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); } SetFilePointer(hfile, 0, NULL, FILE_BEGIN); @@ -3933,78 +5194,78 @@ static void test_read_write(void) bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %Iu\n", iob.Information); ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = sizeof(contents) - 4; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL); - ok(status == STATUS_SUCCESS, "NtWriteFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtWriteFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 4, "expected 4, got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_SUCCESS, "NtReadFile error %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information); + ok(status == STATUS_SUCCESS, "NtReadFile error %#lx\n", status); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %Iu\n", iob.Information); ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n"); ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - S(U(ovl)).Offset = sizeof(contents) - 4; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = sizeof(contents) - 4; + ovl.OffsetHigh = 0; ovl.hEvent = 0; bytes = 0; SetLastError(0xdeadbeef); ret = WriteFile(hfile, "ABCD", 4, &bytes, &ovl); - ok(ret, "WriteFile error %d\n", GetLastError()); - ok(bytes == 4, "bytes %u\n", bytes); + ok(ret, "WriteFile error %ld\n", GetLastError()); + ok(bytes == 4, "bytes %lu\n", bytes); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); - S(U(ovl)).Offset = 0; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = 0; + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl); - ok(ret, "ReadFile error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh); + ok(ret, "ReadFile error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %Iu\n", ovl.InternalHigh); ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n"); ok(!memcmp(buf + sizeof(contents) - 4, "ABCD", 4), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == sizeof(contents), "expected sizeof(contents), got %u\n", off); + ok(off == sizeof(contents), "expected sizeof(contents), got %lu\n", off); CloseHandle(hfile); @@ -4015,11 +5276,11 @@ static void test_read_write(void) SetLastError(0xdeadbeef); ret = ReadFile(INVALID_HANDLE_VALUE, buf, 0, &bytes, NULL); ok(!ret, "ReadFile should fail\n"); - ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); - S(U(ovl)).Offset = 0; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = 0; + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; @@ -4027,83 +5288,84 @@ static void test_read_write(void) SetLastError(0xdeadbeef); /* ReadFile return value depends on Windows version and testing it is not practical */ ReadFile(hfile, buf, 0, &bytes, &ovl); - ok(bytes == 0, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(bytes == 0, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = WriteFile(hfile, contents, sizeof(contents), &bytes, NULL); ok(!ret, "WriteFile should fail\n"); - ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), NULL, NULL); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); for (i = -20; i < -1; i++) { - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)i; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL); - ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "%ld: expected STATUS_INVALID_PARAMETER, got %#lx\n", i, status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); } - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, contents, sizeof(contents), &offset, NULL); - ok(status == STATUS_PENDING || status == STATUS_SUCCESS /* before Vista */, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status); + ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, + "expected STATUS_PENDING, got %#lx.\n", status); if (status == STATUS_PENDING) { ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); } - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, NULL); ok(!ret, "ReadFile should fail\n"); - ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); + ok(bytes == 0, "bytes %lu\n", bytes); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), NULL, NULL); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); for (i = -20; i < 0; i++) { - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)i; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_INVALID_PARAMETER, "%d: expected STATUS_INVALID_PARAMETER, got %#x\n", i, status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %ld\n", iob.Information); + ok(status == STATUS_INVALID_PARAMETER, "%ld: expected STATUS_INVALID_PARAMETER, got %#lx\n", i, status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Id\n", iob.Information); } off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); /* test reading beyond EOF */ offset.QuadPart = sizeof(contents); - S(U(ovl)).Offset = offset.u.LowPart; - S(U(ovl)).OffsetHigh = offset.u.HighPart; + ovl.Offset = offset.u.LowPart; + ovl.OffsetHigh = offset.u.HighPart; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; @@ -4112,11 +5374,12 @@ static void test_read_write(void) ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl); ok(!ret, "ReadFile should fail\n"); ret = GetLastError(); - ok(ret == ERROR_IO_PENDING || ret == ERROR_HANDLE_EOF /* before Vista */, "expected ERROR_IO_PENDING or ERROR_HANDLE_EOF, got %d\n", ret); - ok(bytes == 0, "bytes %u\n", bytes); + ok(ret == ERROR_IO_PENDING || broken(ret == ERROR_HANDLE_EOF) /* before Vista */, + "expected ERROR_IO_PENDING, got %ld\n", ret); + ok(bytes == 0, "bytes %lu\n", bytes); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); if (ret == ERROR_IO_PENDING) { @@ -4124,258 +5387,256 @@ static void test_read_write(void) SetLastError(0xdeadbeef); ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); ok(!ret, "GetOverlappedResult should report FALSE\n"); - ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %d\n", GetLastError()); - ok(bytes == 0, "expected 0, read %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(GetLastError() == ERROR_HANDLE_EOF, "expected ERROR_HANDLE_EOF, got %ld\n", GetLastError()); + ok(bytes == 0, "expected 0, read %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); } off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); offset.QuadPart = sizeof(contents); - S(U(ovl)).Offset = offset.u.LowPart; - S(U(ovl)).OffsetHigh = offset.u.HighPart; + ovl.Offset = offset.u.LowPart; + ovl.OffsetHigh = offset.u.HighPart; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, 0, &bytes, &ovl); - /* ReadFile return value depends on Windows version and testing it is not practical */ - if (!ret) - ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* before Vista */, + "Unexpected result, ret %#lx, GetLastError() %lu.\n", ret, GetLastError()); ret = GetLastError(); - ok(bytes == 0, "bytes %u\n", bytes); + ok(bytes == 0, "bytes %lu\n", bytes); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); if (ret == ERROR_IO_PENDING) { bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); - ok(ret, "GetOverlappedResult should report TRUE\n"); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "expected 0, read %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(ret, "GetOverlappedResult returned FALSE with %lu (expected TRUE)\n", GetLastError()); + ok(bytes == 0, "expected 0, read %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); } offset.QuadPart = sizeof(contents); - S(U(ovl)).Offset = offset.u.LowPart; - S(U(ovl)).OffsetHigh = offset.u.HighPart; + ovl.Offset = offset.u.LowPart; + ovl.OffsetHigh = offset.u.HighPart; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(hfile, NULL, 0, &bytes, &ovl); - /* ReadFile return value depends on Windows version and testing it is not practical */ - if (!ret) - ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* before Vista */, + "Unexpected result, ret %#lx, GetLastError() %lu.\n", ret, GetLastError()); ret = GetLastError(); - ok(bytes == 0, "bytes %u\n", bytes); + ok(bytes == 0, "bytes %lu\n", bytes); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); if (ret == ERROR_IO_PENDING) { bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); - ok(ret, "GetOverlappedResult should report TRUE\n"); - ok(GetLastError() == 0xdeadbeef, "expected 0xdeadbeef, got %d\n", GetLastError()); - ok(bytes == 0, "expected 0, read %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 0, "expected 0, got %lu\n", ovl.InternalHigh); + ok(ret, "GetOverlappedResult returned FALSE with %lu (expected TRUE)\n", GetLastError()); + ok(bytes == 0, "expected 0, read %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 0, "expected 0, got %Iu\n", ovl.InternalHigh); } - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = sizeof(contents); status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); if (status == STATUS_PENDING) { ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); - ok(U(iob).Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); + ok(iob.Status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); } else { - ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#x\n", status); - ok(U(iob).Status == -1, "expected -1, got %#x\n", U(iob).Status); - ok(iob.Information == -1, "expected -1, got %lu\n", iob.Information); + ok(status == STATUS_END_OF_FILE, "expected STATUS_END_OF_FILE, got %#lx\n", status); + ok(iob.Status == -1, "expected -1, got %#lx\n", iob.Status); + ok(iob.Information == -1, "expected -1, got %Iu\n", iob.Information); } off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = sizeof(contents); status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, 0, &offset, NULL); + ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, + "expected STATUS_PENDING, got %#lx.\n", status); if (status == STATUS_PENDING) { ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); } else { - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", status); - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 0, "expected 0, got %lu\n", iob.Information); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 0, "expected 0, got %Iu\n", iob.Information); } off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); - S(U(ovl)).Offset = 0; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = 0; + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl); - /* ReadFile return value depends on Windows version and testing it is not practical */ + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* before Vista */, + "Unexpected result, ret %#lx, GetLastError() %lu.\n", ret, GetLastError()); if (!ret) - { - ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); - } - else ok(bytes == 14, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh); + ok(bytes == 0, "bytes %lu\n", bytes); + else + ok(bytes == 14, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %Iu\n", ovl.InternalHigh); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); bytes = 0xdeadbeef; ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); - ok(ret, "GetOverlappedResult error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh); + ok(ret, "GetOverlappedResult error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %Iu\n", ovl.InternalHigh); ok(!memcmp(contents, buf, sizeof(contents)), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); SetFilePointer(hfile, sizeof(contents) - 4, NULL, FILE_BEGIN); SetEndOfFile(hfile); SetFilePointer(hfile, 0, NULL, FILE_BEGIN); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = (LONGLONG)-1 /* FILE_WRITE_TO_END_OF_FILE */; status = pNtWriteFile(hfile, 0, NULL, NULL, &iob, "DCBA", 4, &offset, NULL); - ok(status == STATUS_PENDING || status == STATUS_SUCCESS /* before Vista */, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status); + ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, + "expected STATUS_PENDING, got %#lx.\n", status); if (status == STATUS_PENDING) { ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); } - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == 4, "expected 4, got %lu\n", iob.Information); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == 4, "expected 4, got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); - U(iob).Status = -1; + iob.Status = -1; iob.Information = -1; offset.QuadPart = 0; status = pNtReadFile(hfile, 0, NULL, NULL, &iob, buf, sizeof(buf), &offset, NULL); - ok(status == STATUS_PENDING || status == STATUS_SUCCESS, "expected STATUS_PENDING or STATUS_SUCCESS, got %#x\n", status); + ok(status == STATUS_PENDING || broken(status == STATUS_SUCCESS) /* before Vista */, + "expected STATUS_PENDING, got %#lx.\n", status); if (status == STATUS_PENDING) { ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); } - ok(U(iob).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x\n", U(iob).Status); - ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %lu\n", iob.Information); + ok(iob.Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", iob.Status); + ok(iob.Information == sizeof(contents), "expected sizeof(contents), got %Iu\n", iob.Information); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n"); ok(!memcmp(buf + sizeof(contents) - 4, "DCBA", 4), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); - S(U(ovl)).Offset = sizeof(contents) - 4; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = sizeof(contents) - 4; + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0; SetLastError(0xdeadbeef); ret = WriteFile(hfile, "ABCD", 4, &bytes, &ovl); - /* WriteFile return value depends on Windows version and testing it is not practical */ + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* before Vista */, + "Unexpected result %#lx, GetLastError() %lu.\n", ret, GetLastError()); if (!ret) { - ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(bytes == 0, "bytes %lu\n", bytes); ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); } - else ok(bytes == 4, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 4, "expected 4, got %lu\n", ovl.InternalHigh); + else ok(bytes == 4, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 4, "expected 4, got %Iu\n", ovl.InternalHigh); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); bytes = 0xdeadbeef; ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); - ok(ret, "GetOverlappedResult error %d\n", GetLastError()); - ok(bytes == 4, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == 4, "expected 4, got %lu\n", ovl.InternalHigh); + ok(ret, "GetOverlappedResult error %ld\n", GetLastError()); + ok(bytes == 4, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == 4, "expected 4, got %Iu\n", ovl.InternalHigh); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); - S(U(ovl)).Offset = 0; - S(U(ovl)).OffsetHigh = 0; + ovl.Offset = 0; + ovl.OffsetHigh = 0; ovl.Internal = -1; ovl.InternalHigh = -1; ovl.hEvent = 0; bytes = 0; SetLastError(0xdeadbeef); ret = ReadFile(hfile, buf, sizeof(buf), &bytes, &ovl); - /* ReadFile return value depends on Windows version and testing it is not practical */ + ok((!ret && GetLastError() == ERROR_IO_PENDING) || broken(ret) /* before Vista */, + "Unexpected result %#lx, GetLastError() %lu.\n", ret, GetLastError()); if (!ret) { - ok(GetLastError() == ERROR_IO_PENDING, "expected ERROR_IO_PENDING, got %d\n", GetLastError()); - ok(bytes == 0, "bytes %u\n", bytes); + ok(bytes == 0, "bytes %lu\n", bytes); ret = WaitForSingleObject(hfile, 3000); - ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %d\n", ret); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject error %ld\n", ret); } - else ok(bytes == 14, "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh); + else ok(bytes == 14, "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %Iu\n", ovl.InternalHigh); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); bytes = 0xdeadbeef; ret = GetOverlappedResult(hfile, &ovl, &bytes, TRUE); - ok(ret, "GetOverlappedResult error %d\n", GetLastError()); - ok(bytes == sizeof(contents), "bytes %u\n", bytes); - ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx\n", ovl.Internal); - ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %lu\n", ovl.InternalHigh); + ok(ret, "GetOverlappedResult error %ld\n", GetLastError()); + ok(bytes == sizeof(contents), "bytes %lu\n", bytes); + ok((NTSTATUS)ovl.Internal == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#Ix\n", ovl.Internal); + ok(ovl.InternalHigh == sizeof(contents), "expected sizeof(contents), got %Iu\n", ovl.InternalHigh); ok(!memcmp(contents, buf, sizeof(contents) - 4), "file contents mismatch\n"); ok(!memcmp(buf + sizeof(contents) - 4, "ABCD", 4), "file contents mismatch\n"); off = SetFilePointer(hfile, 0, NULL, FILE_CURRENT); - ok(off == 0, "expected 0, got %u\n", off); + ok(off == 0, "expected 0, got %lu\n", off); CloseHandle(event); CloseHandle(hfile); @@ -4395,18 +5656,18 @@ static void test_ioctl(void) SetEvent(event); status = pNtFsControlFile(file, event, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0); todo_wine - ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile returned %x\n", status); + ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile returned %lx\n", status); ok(!is_signaled(event), "event is signaled\n"); status = pNtFsControlFile(file, (HANDLE)0xdeadbeef, NULL, NULL, &iosb, 0xdeadbeef, 0, 0, 0, 0); - ok(status == STATUS_INVALID_HANDLE, "NtFsControlFile returned %x\n", status); + ok(status == STATUS_INVALID_HANDLE, "NtFsControlFile returned %lx\n", status); memset(&iosb, 0x55, sizeof(iosb)); - status = NtFsControlFile(file, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, + status = pNtFsControlFile(file, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &peek_buf, sizeof(peek_buf)); todo_wine - ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile failed: %x\n", status); - ok(iosb.Status == 0x55555555, "iosb.Status = %x\n", iosb.Status); + ok(status == STATUS_INVALID_DEVICE_REQUEST, "NtFsControlFile failed: %lx\n", status); + ok(iosb.Status == 0x55555555, "iosb.Status = %lx\n", iosb.Status); CloseHandle(event); CloseHandle(file); @@ -4427,33 +5688,52 @@ static void test_flush_buffers_file(void) hfileread = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - ok(hfileread != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError()); + ok(hfileread != INVALID_HANDLE_VALUE, "could not open temp file, error %ld.\n", GetLastError()); status = pNtFlushBuffersFile(hfile, NULL); - todo_wine - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status); status = pNtFlushBuffersFile(hfile, (IO_STATUS_BLOCK *)0xdeadbeaf); - todo_wine - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %#lx.\n", status); + io_status_block.Information = 0xdeadbeef; + io_status_block.Status = 0xdeadbeef; status = pNtFlushBuffersFile(hfile, &io_status_block); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx.\n", status); + ok(io_status_block.Status == STATUS_SUCCESS, "Got unexpected io_status_block.Status %#lx.\n", + io_status_block.Status); + ok(!io_status_block.Information, "Got unexpected io_status_block.Information %#Ix.\n", + io_status_block.Information); status = pNtFlushBuffersFile(hfileread, &io_status_block); - ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#x.\n", status); + ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#lx.\n", status); + io_status_block.Information = 0xdeadbeef; + io_status_block.Status = 0xdeadbeef; status = pNtFlushBuffersFile(NULL, &io_status_block); - ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#x.\n", status); + ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#lx.\n", status); + ok(io_status_block.Status == 0xdeadbeef, "Got unexpected io_status_block.Status %#lx.\n", + io_status_block.Status); + ok(io_status_block.Information == 0xdeadbeef, "Got unexpected io_status_block.Information %#Ix.\n", + io_status_block.Information); CloseHandle(hfileread); CloseHandle(hfile); hfile = CreateFileA(buffer, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - ok(hfile != INVALID_HANDLE_VALUE, "could not open temp file, error %d.\n", GetLastError()); + ok(hfile != INVALID_HANDLE_VALUE, "could not open temp file, error %ld.\n", GetLastError()); status = pNtFlushBuffersFile(hfile, &io_status_block); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#x.\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx.\n", status); + + io_status_block.Information = 0xdeadbeef; + io_status_block.Status = 0xdeadbeef; + status = pNtFlushBuffersFile((HANDLE)0xdeadbeef, &io_status_block); + ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#lx.\n", status); + ok(io_status_block.Status == 0xdeadbeef, "Got unexpected io_status_block.Status %#lx.\n", + io_status_block.Status); + ok(io_status_block.Information == 0xdeadbeef, "Got unexpected io_status_block.Information %#Ix.\n", + io_status_block.Information); CloseHandle(hfile); DeleteFileA(buffer); @@ -4461,7 +5741,7 @@ static void test_flush_buffers_file(void) static void test_query_ea(void) { - #define EA_BUFFER_SIZE 4097 +#define EA_BUFFER_SIZE 4097 unsigned char data[EA_BUFFER_SIZE + 8]; unsigned char *buffer = (void *)(((DWORD_PTR)data + 7) & ~7); DWORD buffer_len, i; @@ -4472,242 +5752,288 @@ static void test_query_ea(void) if (!(handle = create_temp_file(0))) return; /* test with INVALID_HANDLE_VALUE */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; memset(buffer, 0xcc, EA_BUFFER_SIZE); buffer_len = EA_BUFFER_SIZE - 1; status = pNtQueryEaFile(INVALID_HANDLE_VALUE, &io, buffer, buffer_len, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_OBJECT_TYPE_MISMATCH, "expected STATUS_OBJECT_TYPE_MISMATCH, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "expected STATUS_OBJECT_TYPE_MISMATCH, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); ok(buffer[0] == 0xcc, "data at position 0 overwritten\n"); /* test with 0xdeadbeef */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; memset(buffer, 0xcc, EA_BUFFER_SIZE); buffer_len = EA_BUFFER_SIZE - 1; status = pNtQueryEaFile((void *)0xdeadbeef, &io, buffer, buffer_len, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_INVALID_HANDLE, "expected STATUS_INVALID_HANDLE, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); ok(buffer[0] == 0xcc, "data at position 0 overwritten\n"); /* test without buffer */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; status = pNtQueryEaFile(handle, &io, NULL, 0, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); /* test with zero buffer */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; status = pNtQueryEaFile(handle, &io, buffer, 0, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); /* test with very small buffer */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; memset(buffer, 0xcc, EA_BUFFER_SIZE); buffer_len = 4; status = pNtQueryEaFile(handle, &io, buffer, buffer_len, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); for (i = 0; i < buffer_len && !buffer[i]; i++); - ok(i == buffer_len, "expected %u bytes filled with 0x00, got %u bytes\n", buffer_len, i); + ok(i == buffer_len, "expected %lu bytes filled with 0x00, got %lu bytes\n", buffer_len, i); ok(buffer[i] == 0xcc, "data at position %u overwritten\n", buffer[i]); /* test with very big buffer */ - U(io).Status = 0xdeadbeef; + io.Status = 0xdeadbeef; io.Information = 0xdeadbeef; memset(buffer, 0xcc, EA_BUFFER_SIZE); buffer_len = EA_BUFFER_SIZE - 1; status = pNtQueryEaFile(handle, &io, buffer, buffer_len, TRUE, NULL, 0, NULL, FALSE); - ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %x\n", status); - ok(U(io).Status == 0xdeadbeef, "expected 0xdeadbeef, got %x\n", U(io).Status); - ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %lu\n", io.Information); + ok(status == STATUS_NO_EAS_ON_FILE, "expected STATUS_NO_EAS_ON_FILE, got %#lx\n", status); + ok(io.Status == 0xdeadbeef, "expected 0xdeadbeef, got %#lx\n", io.Status); + ok(io.Information == 0xdeadbeef, "expected 0xdeadbeef, got %#Ix\n", io.Information); for (i = 0; i < buffer_len && !buffer[i]; i++); - ok(i == buffer_len, "expected %u bytes filled with 0x00, got %u bytes\n", buffer_len, i); + ok(i == buffer_len, "expected %lu bytes filled with 0x00, got %lu bytes\n", buffer_len, i); ok(buffer[i] == 0xcc, "data at position %u overwritten\n", buffer[i]); CloseHandle(handle); - #undef EA_BUFFER_SIZE +#undef EA_BUFFER_SIZE } -static INT build_reparse_buffer(WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer) +static void test_file_readonly_access(void) { - REPARSE_DATA_BUFFER *buffer; - INT buffer_len, string_len; - WCHAR *dest; - - string_len = (lstrlenW(filename)+1)*sizeof(WCHAR); - buffer_len = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer[1]) + string_len; - buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len); - buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; - buffer->ReparseDataLength = sizeof(buffer->MountPointReparseBuffer) + string_len; - buffer->MountPointReparseBuffer.SubstituteNameLength = string_len - sizeof(WCHAR); - buffer->MountPointReparseBuffer.PrintNameOffset = string_len; - dest = &buffer->MountPointReparseBuffer.PathBuffer[0]; - memcpy(dest, filename, string_len); - *pbuffer = buffer; - return buffer_len; -} - -static void test_junction_points(void) -{ - static const WCHAR junctionW[] = {'\\','j','u','n','c','t','i','o','n',0}; - WCHAR path[MAX_PATH], junction_path[MAX_PATH], target_path[MAX_PATH]; - static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0}; - FILE_BASIC_INFORMATION old_attrib, new_attrib; - static const WCHAR fooW[] = {'f','o','o',0}; - static WCHAR volW[] = {'c',':','\\',0}; - REPARSE_GUID_DATA_BUFFER guid_buffer; - static const WCHAR dotW[] = {'.',0}; - REPARSE_DATA_BUFFER *buffer = NULL; - DWORD dwret, dwLen, dwFlags, err; - INT buffer_len, string_len; - IO_STATUS_BLOCK iosb; + static const DWORD default_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + static const WCHAR fooW[] = {'f', 'o', 'o', 0}; + WCHAR path[MAX_PATH]; + OBJECT_ATTRIBUTES attr; UNICODE_STRING nameW; - HANDLE hJunction; - WCHAR *dest; - BOOL bret; + IO_STATUS_BLOCK io; + HANDLE handle; + NTSTATUS status; + DWORD ret; - /* Create a temporary folder for the junction point tests */ - GetTempFileNameW(dotW, fooW, 0, path); + /* Set up */ + GetTempPathW(MAX_PATH, path); + GetTempFileNameW(path, fooW, 0, path); DeleteFileW(path); - if (!CreateDirectoryW(path, NULL)) - { - win_skip("Unable to create a temporary junction point directory.\n"); - return; - } - - /* Check that the volume this folder is located on supports junction points */ pRtlDosPathNameToNtPathName_U(path, &nameW, NULL, NULL); - volW[0] = nameW.Buffer[4]; - pRtlFreeUnicodeString( &nameW ); - GetVolumeInformationW(volW, 0, 0, 0, &dwLen, &dwFlags, 0, 0); - if (!(dwFlags & FILE_SUPPORTS_REPARSE_POINTS)) + + attr.Length = sizeof(attr); + attr.RootDirectory = NULL; + attr.ObjectName = &nameW; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = pNtCreateFile(&handle, FILE_GENERIC_WRITE, &attr, &io, NULL, FILE_ATTRIBUTE_READONLY, default_sharing, + FILE_CREATE, 0, NULL, 0); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx.\n", status); + CloseHandle(handle); + + /* NtCreateFile FILE_GENERIC_WRITE */ + status = pNtCreateFile(&handle, FILE_GENERIC_WRITE, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, default_sharing, + FILE_OPEN, FILE_NON_DIRECTORY_FILE, NULL, 0); + ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#lx.\n", status); + + /* NtCreateFile DELETE without FILE_DELETE_ON_CLOSE */ + status = pNtCreateFile(&handle, DELETE, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, default_sharing, FILE_OPEN, + FILE_NON_DIRECTORY_FILE, NULL, 0); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx.\n", status); + CloseHandle(handle); + + /* NtCreateFile DELETE with FILE_DELETE_ON_CLOSE */ + status = pNtCreateFile(&handle, SYNCHRONIZE | DELETE, &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, default_sharing, + FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE, NULL, 0); + ok(status == STATUS_CANNOT_DELETE, "expected STATUS_CANNOT_DELETE, got %#lx.\n", status); + + /* NtOpenFile GENERIC_WRITE */ + status = pNtOpenFile(&handle, GENERIC_WRITE, &attr, &io, default_sharing, FILE_NON_DIRECTORY_FILE); + ok(status == STATUS_ACCESS_DENIED, "expected STATUS_ACCESS_DENIED, got %#lx.\n", status); + + /* NtOpenFile DELETE without FILE_DELETE_ON_CLOSE */ + status = pNtOpenFile(&handle, DELETE, &attr, &io, default_sharing, FILE_NON_DIRECTORY_FILE); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %#lx.\n", status); + CloseHandle(handle); + + /* NtOpenFile DELETE with FILE_DELETE_ON_CLOSE */ + status = pNtOpenFile(&handle, DELETE, &attr, &io, default_sharing, FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE); + ok(status == STATUS_CANNOT_DELETE, "expected STATUS_CANNOT_DELETE, got %#lx.\n", status); + + ret = GetFileAttributesW(path); + ok(ret & FILE_ATTRIBUTE_READONLY, "got wrong attribute: %#lx.\n", ret); + + /* Clean up */ + pRtlFreeUnicodeString(&nameW); + SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL); + DeleteFileW(path); +} + +static void test_mailslot_name(void) +{ + char buffer[1024] = {0}; + const FILE_NAME_INFORMATION *name = (const FILE_NAME_INFORMATION *)buffer; + HANDLE server, client, device; + IO_STATUS_BLOCK io; + NTSTATUS ret; + + server = CreateMailslotA( "\\\\.\\mailslot\\winetest", 100, 1000, NULL ); + ok(server != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + + ret = NtQueryInformationFile( server, &io, buffer, 0, FileNameInformation ); + ok(ret == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", ret); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtQueryInformationFile( server, &io, buffer, + offsetof(FILE_NAME_INFORMATION, FileName[5]), FileNameInformation ); + todo_wine ok(ret == STATUS_BUFFER_OVERFLOW, "got %#lx\n", ret); + if (ret == STATUS_BUFFER_OVERFLOW) { - skip("File system does not support junction points.\n"); - RemoveDirectoryW(path); + ok(name->FileNameLength == 18, "got length %lu\n", name->FileNameLength); + ok(!memcmp(name->FileName, L"\\wine", 10), "got %s\n", + debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR))); + } + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtQueryInformationFile( server, &io, buffer, sizeof(buffer), FileNameInformation ); + todo_wine ok(!ret, "got %#lx\n", ret); + if (!ret) + { + ok(name->FileNameLength == 18, "got length %lu\n", name->FileNameLength); + ok(!memcmp(name->FileName, L"\\winetest", 18), "got %s\n", + debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR))); + } + + client = CreateFileA( "\\\\.\\mailslot\\winetest", 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + ok(client != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + + ret = NtQueryInformationFile( client, &io, buffer, 0, FileNameInformation ); + ok(ret == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", ret); + + ret = NtQueryInformationFile( client, &io, buffer, sizeof(buffer), FileNameInformation ); + todo_wine ok(ret == STATUS_INVALID_PARAMETER || !ret /* win8+ */, "got %#lx\n", ret); + if (!ret) + { + ok(name->FileNameLength == 18, "got length %lu\n", name->FileNameLength); + ok(!memcmp(name->FileName, L"\\winetest", 18), "got %s\n", + debugstr_wn(name->FileName, name->FileNameLength / sizeof(WCHAR))); + } + + CloseHandle( server ); + CloseHandle( client ); + + device = CreateFileA("\\\\.\\mailslot", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(device != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + + ret = NtQueryInformationFile( device, &io, buffer, 0, FileNameInformation ); + ok(ret == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", ret); + + ret = NtQueryInformationFile( device, &io, buffer, sizeof(buffer), FileNameInformation ); + todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + + CloseHandle( device ); +} + +static void test_reparse_points(void) +{ + OBJECT_ATTRIBUTES attr; + HANDLE handle; + IO_STATUS_BLOCK io; + NTSTATUS status; + UNICODE_STRING nameW; + unsigned char reparse_data[1]; + + pRtlInitUnicodeString( &nameW, L"\\??\\C:\\" ); + InitializeObjectAttributes( &attr, &nameW, 0, NULL, NULL ); + + status = pNtOpenFile( &handle, READ_CONTROL, &attr, &io, 0, 0 ); + ok( !status, "open %s failed %#lx\n", wine_dbgstr_w(nameW.Buffer), status ); + + status = pNtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT, NULL, 0, NULL, 0 ); + ok( status == STATUS_INVALID_USER_BUFFER, "expected %#lx, got %#lx\n", STATUS_INVALID_USER_BUFFER, status ); + + status = pNtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse_data, 0 ); + ok( status == STATUS_INVALID_USER_BUFFER, "expected %#lx, got %#lx\n", STATUS_INVALID_USER_BUFFER, status ); + + /* a volume cannot be a reparse point by definition */ + status = pNtFsControlFile( handle, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse_data, 1 ); + ok( status == STATUS_NOT_A_REPARSE_POINT, "expected %#lx, got %#lx\n", STATUS_NOT_A_REPARSE_POINT, status ); + + CloseHandle( handle ); +} + +static void test_set_io_completion_ex(void) +{ + HANDLE completion, completion_reserve, apc_reserve; + LARGE_INTEGER timeout = {{0}}; + IO_STATUS_BLOCK iosb; + ULONG_PTR key, value; + NTSTATUS status; + SIZE_T size = 3; + + if (!pNtSetIoCompletionEx || !pNtAllocateReserveObject) + { + win_skip("NtSetIoCompletionEx() or NtAllocateReserveObject() is unavailable.\n"); return; } - /* Create the folder to be replaced by a junction point */ - lstrcpyW(junction_path, path); - lstrcatW(junction_path, junctionW); - bret = CreateDirectoryW(junction_path, NULL); - ok(bret, "Failed to create junction point directory.\n"); + if (sizeof(size) > 4) size |= (ULONGLONG)0x12345678 << 32; - /* Create a destination folder for the junction point to target */ - lstrcpyW(target_path, path); - lstrcatW(target_path, targetW); - bret = CreateDirectoryW(target_path, NULL); - ok(bret, "Failed to create junction point target directory.\n"); - pRtlDosPathNameToNtPathName_U(target_path, &nameW, NULL, NULL); + status = pNtCreateIoCompletion(&completion, IO_COMPLETION_ALL_ACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtAllocateReserveObject(&completion_reserve, NULL, MemoryReserveObjectTypeIoCompletion); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtAllocateReserveObject(&apc_reserve, NULL, MemoryReserveObjectTypeUserApc); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); - /* Create the junction point */ - hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); - if (hJunction == INVALID_HANDLE_VALUE) - { - win_skip("Failed to open junction point directory handle (0x%x).\n", GetLastError()); - goto cleanup; - } - dwret = NtQueryInformationFile(hJunction, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation); - ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%x).\n", dwret); - buffer_len = build_reparse_buffer(nameW.Buffer, &buffer); - bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0); - ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError()); + /* Parameter checks */ + status = pNtSetIoCompletionEx(NULL, completion_reserve, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); - /* Check the file attributes of the junction point */ - dwret = GetFileAttributesW(junction_path); - ok(dwret != (DWORD)~0, "Junction point doesn't exist (attributes: 0x%x)!\n", dwret); - ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: %d)\n", dwret); + status = pNtSetIoCompletionEx(INVALID_HANDLE_VALUE, completion_reserve, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); - /* Read back the junction point */ - HeapFree(GetProcessHeap(), 0, buffer); - buffer_len = sizeof(*buffer) + MAX_PATH*sizeof(WCHAR); - buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_len); - bret = DeviceIoControl(hJunction, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buffer, buffer_len, &dwret, 0); - string_len = buffer->MountPointReparseBuffer.SubstituteNameLength; - dest = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)]; - ok(bret, "Failed to read junction point!\n"); - ok((memcmp(dest, nameW.Buffer, string_len) == 0), "Junction point destination does not match ('%s' != '%s')!\n", - wine_dbgstr_w(dest), wine_dbgstr_w(nameW.Buffer)); + status = pNtSetIoCompletionEx(completion, NULL, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); - /* Delete the junction point */ - memset(&old_attrib, 0x00, sizeof(old_attrib)); - old_attrib.LastAccessTime.QuadPart = 0x200deadcafebeef; - dwret = NtSetInformationFile(hJunction, &iosb, &old_attrib, sizeof(old_attrib), FileBasicInformation); - ok(dwret == STATUS_SUCCESS, "Failed to set junction point folder's attributes (0x%x).\n", dwret); - memset(&guid_buffer, 0x00, sizeof(guid_buffer)); - guid_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; - bret = DeviceIoControl(hJunction, FSCTL_DELETE_REPARSE_POINT, (LPVOID)&guid_buffer, - REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &dwret, 0); - ok(bret, "Failed to delete junction point! (0x%x)\n", GetLastError()); - memset(&new_attrib, 0x00, sizeof(new_attrib)); - dwret = NtQueryInformationFile(hJunction, &iosb, &new_attrib, sizeof(new_attrib), FileBasicInformation); - ok(dwret == STATUS_SUCCESS, "Failed to get junction point folder's attributes (0x%x).\n", dwret); - ok(old_attrib.LastAccessTime.QuadPart == new_attrib.LastAccessTime.QuadPart, - "Junction point folder's access time does not match.\n"); - CloseHandle(hJunction); + status = pNtSetIoCompletionEx(completion, INVALID_HANDLE_VALUE, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); - /* Check deleting a junction point as if it were a directory */ - HeapFree(GetProcessHeap(), 0, buffer); - hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); - buffer_len = build_reparse_buffer(nameW.Buffer, &buffer); - bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0); - ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError()); - CloseHandle(hJunction); - bret = RemoveDirectoryW(junction_path); - ok(bret, "Failed to delete junction point as directory!\n"); - dwret = GetFileAttributesW(junction_path); - ok(dwret == (DWORD)~0, "Junction point still exists (attributes: 0x%x)!\n", dwret); + status = pNtSetIoCompletionEx(completion, apc_reserve, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); - /* Check deleting a junction point as if it were a file */ - HeapFree(GetProcessHeap(), 0, buffer); - bret = CreateDirectoryW(junction_path, NULL); - ok(bret, "Failed to create junction point target directory.\n"); - hJunction = CreateFileW(junction_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); - buffer_len = build_reparse_buffer(nameW.Buffer, &buffer); - bret = DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, (LPVOID)buffer, buffer_len, NULL, 0, &dwret, 0); - ok(bret, "Failed to create junction point! (0x%x)\n", GetLastError()); - CloseHandle(hJunction); - bret = DeleteFileW(junction_path); - ok(!bret, "Succeeded in deleting junction point as file!\n"); - err = GetLastError(); - ok(err == ERROR_ACCESS_DENIED, "Expected last error 0x%x for DeleteFile on junction point (actually 0x%x)!\n", - ERROR_ACCESS_DENIED, err); - dwret = GetFileAttributesW(junction_path); - ok(dwret != (DWORD)~0, "Junction point doesn't exist (attributes: 0x%x)!\n", dwret); - ok(dwret & FILE_ATTRIBUTE_REPARSE_POINT, "File is not a junction point! (attributes: 0x%x)\n", dwret); + /* Normal call */ + status = pNtSetIoCompletionEx(completion, completion_reserve, CKEY_FIRST, CVALUE_FIRST, STATUS_INVALID_DEVICE_REQUEST, size); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); - /* Test deleting a junction point's target */ - dwret = GetFileAttributesW(junction_path); - ok(dwret == 0x410 || broken(dwret == 0x430) /* win2k */, - "Unexpected junction point attributes (0x%x != 0x410)!\n", dwret); - bret = RemoveDirectoryW(target_path); - ok(bret, "Failed to delete junction point target!\n"); - bret = CreateDirectoryW(target_path, NULL); - ok(bret, "Failed to create junction point target directory.\n"); + status = pNtRemoveIoCompletion(completion, &key, &value, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key == CKEY_FIRST, "Invalid completion key: %#Ix\n", key); + ok(iosb.Information == size, "Invalid iosb.Information: %Iu\n", iosb.Information); + ok(iosb.Status == STATUS_INVALID_DEVICE_REQUEST, "Invalid iosb.Status: %#lx\n", iosb.Status); + ok(value == CVALUE_FIRST, "Invalid completion value: %#Ix\n", value); -cleanup: - /* Cleanup */ - pRtlFreeUnicodeString( &nameW ); - HeapFree(GetProcessHeap(), 0, buffer); - bret = RemoveDirectoryW(junction_path); - ok(bret, "Failed to remove temporary junction point directory!\n"); - bret = RemoveDirectoryW(target_path); - ok(bret, "Failed to remove temporary target directory!\n"); - RemoveDirectoryW(path); + CloseHandle(apc_reserve); + CloseHandle(completion_reserve); + CloseHandle(completion); } START_TEST(file) @@ -4727,6 +6053,7 @@ START_TEST(file) pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U"); pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll, "RtlWow64EnableFsRedirectionEx"); + pNtAllocateReserveObject= (void *)GetProcAddress(hntdll, "NtAllocateReserveObject"); pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); @@ -4741,8 +6068,11 @@ START_TEST(file) pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); pNtQueryIoCompletion = (void *)GetProcAddress(hntdll, "NtQueryIoCompletion"); pNtRemoveIoCompletion = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion"); + pNtRemoveIoCompletionEx = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletionEx"); pNtSetIoCompletion = (void *)GetProcAddress(hntdll, "NtSetIoCompletion"); + pNtSetIoCompletionEx = (void *)GetProcAddress(hntdll, "NtSetIoCompletionEx"); pNtSetInformationFile = (void *)GetProcAddress(hntdll, "NtSetInformationFile"); + pNtQueryAttributesFile = (void *)GetProcAddress(hntdll, "NtQueryAttributesFile"); pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile"); pNtQueryDirectoryFile = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile"); pNtQueryVolumeInformationFile = (void *)GetProcAddress(hntdll, "NtQueryVolumeInformationFile"); @@ -4752,30 +6082,41 @@ START_TEST(file) test_read_write(); test_NtCreateFile(); - test_readonly(); create_file_test(); open_file_test(); delete_file_test(); read_file_test(); append_file_test(); nt_mailslot_test(); - test_iocompletion(); + test_set_io_completion(); + test_set_io_completion_ex(); + test_file_io_completion(); test_file_basic_information(); test_file_all_information(); test_file_both_information(); test_file_name_information(); test_file_full_size_information(); test_file_all_name_information(); - test_file_rename_information(); - test_file_link_information(); + test_file_rename_information(FileRenameInformation); + test_file_rename_information(FileRenameInformationEx); + test_file_rename_information_ex(); + test_file_link_information(FileLinkInformation); + test_file_link_information(FileLinkInformationEx); + test_file_link_information_ex(); test_file_disposition_information(); test_file_completion_information(); test_file_id_information(); test_file_access_information(); + test_file_attribute_tag_information(); + test_file_stat_information(); + test_dotfile_file_attributes(); + test_file_mode(); + test_file_readonly_access(); test_query_volume_information_file(); test_query_attribute_information_file(); test_ioctl(); - test_flush_buffers_file(); test_query_ea(); - test_junction_points(); + test_flush_buffers_file(); + test_mailslot_name(); + test_reparse_points(); } diff --git a/modules/rostests/winetests/ntdll/generated.c b/modules/rostests/winetests/ntdll/generated.c index d277e02073b..2c3b462011e 100644 --- a/modules/rostests/winetests/ntdll/generated.c +++ b/modules/rostests/winetests/ntdll/generated.c @@ -7,8 +7,13 @@ #define WINE_NOWINSOCK -#include "ntdll_test.h" +#include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" #include "wine/test.h" /*********************************************************************** @@ -25,7 +30,7 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus) # define _TYPE_ALIGNMENT(type) __alignof(type) -#elif defined(__GNUC__) +#elif defined(__GNUC__) || defined(__clang__) # define _TYPE_ALIGNMENT(type) __alignof__(type) #else /* @@ -47,23 +52,23 @@ * Test helper macros */ -#define TEST_TYPE_SIZE(type, size) C_ASSERT(sizeof(type) == size); +#define TEST_TYPE_SIZE(type, size) C_ASSERT(sizeof(type) == size); #ifdef TYPE_ALIGNMENT -# define TEST_TYPE_ALIGN(type, align) C_ASSERT(TYPE_ALIGNMENT(type) == align); +# define TEST_TYPE_ALIGN(type, align) C_ASSERT(TYPE_ALIGNMENT(type) == align); #else # define TEST_TYPE_ALIGN(type, align) #endif #ifdef _TYPE_ALIGNMENT -# define TEST_TARGET_ALIGN(type, align) C_ASSERT(_TYPE_ALIGNMENT(*(type)0) == align); -# define TEST_FIELD_ALIGN(type, field, align) C_ASSERT(_TYPE_ALIGNMENT(((type*)0)->field) == align); +# define TEST_TARGET_ALIGN(type, align) C_ASSERT(_TYPE_ALIGNMENT(*(type)0) == align); +# define TEST_FIELD_ALIGN(type, field, align) C_ASSERT(_TYPE_ALIGNMENT(((type*)0)->field) == align); #else # define TEST_TARGET_ALIGN(type, align) # define TEST_FIELD_ALIGN(type, field, align) #endif -#define TEST_FIELD_OFFSET(type, field, offset) C_ASSERT(FIELD_OFFSET(type, field) == offset); +#define TEST_FIELD_OFFSET(type, field, offset) C_ASSERT(FIELD_OFFSET(type, field) == offset); #define TEST_TARGET_SIZE(type, size) TEST_TYPE_SIZE(*(type)0, size) #define TEST_FIELD_SIZE(type, field, size) TEST_TYPE_SIZE((((type*)0)->field), size) @@ -94,6 +99,7 @@ static void test_pack_DWORD_PTR(void) /* DWORD_PTR */ TEST_TYPE_SIZE (DWORD_PTR, 8) TEST_TYPE_ALIGN (DWORD_PTR, 8) + TEST_TYPE_UNSIGNED(DWORD_PTR) } static void test_pack_HALF_PTR(void) @@ -101,7 +107,7 @@ static void test_pack_HALF_PTR(void) /* HALF_PTR */ TEST_TYPE_SIZE (HALF_PTR, 4) TEST_TYPE_ALIGN (HALF_PTR, 4) - TEST_TYPE_SIGNED(HALF_PTR) + TEST_TYPE_SIGNED (HALF_PTR) } static void test_pack_INT16(void) @@ -109,7 +115,7 @@ static void test_pack_INT16(void) /* INT16 */ TEST_TYPE_SIZE (INT16, 2) TEST_TYPE_ALIGN (INT16, 2) - TEST_TYPE_SIGNED(INT16) + TEST_TYPE_SIGNED (INT16) } static void test_pack_INT32(void) @@ -117,7 +123,7 @@ static void test_pack_INT32(void) /* INT32 */ TEST_TYPE_SIZE (INT32, 4) TEST_TYPE_ALIGN (INT32, 4) - TEST_TYPE_SIGNED(INT32) + TEST_TYPE_SIGNED (INT32) } static void test_pack_INT64(void) @@ -125,7 +131,7 @@ static void test_pack_INT64(void) /* INT64 */ TEST_TYPE_SIZE (INT64, 8) TEST_TYPE_ALIGN (INT64, 8) - TEST_TYPE_SIGNED(INT64) + TEST_TYPE_SIGNED (INT64) } static void test_pack_INT8(void) @@ -133,7 +139,7 @@ static void test_pack_INT8(void) /* INT8 */ TEST_TYPE_SIZE (INT8, 1) TEST_TYPE_ALIGN (INT8, 1) - TEST_TYPE_SIGNED(INT8) + TEST_TYPE_SIGNED (INT8) } static void test_pack_INT_PTR(void) @@ -141,7 +147,7 @@ static void test_pack_INT_PTR(void) /* INT_PTR */ TEST_TYPE_SIZE (INT_PTR, 8) TEST_TYPE_ALIGN (INT_PTR, 8) - TEST_TYPE_SIGNED(INT_PTR) + TEST_TYPE_SIGNED (INT_PTR) } static void test_pack_LONG32(void) @@ -149,7 +155,7 @@ static void test_pack_LONG32(void) /* LONG32 */ TEST_TYPE_SIZE (LONG32, 4) TEST_TYPE_ALIGN (LONG32, 4) - TEST_TYPE_SIGNED(LONG32) + TEST_TYPE_SIGNED (LONG32) } static void test_pack_LONG64(void) @@ -157,7 +163,7 @@ static void test_pack_LONG64(void) /* LONG64 */ TEST_TYPE_SIZE (LONG64, 8) TEST_TYPE_ALIGN (LONG64, 8) - TEST_TYPE_SIGNED(LONG64) + TEST_TYPE_SIGNED (LONG64) } static void test_pack_LONG_PTR(void) @@ -165,7 +171,7 @@ static void test_pack_LONG_PTR(void) /* LONG_PTR */ TEST_TYPE_SIZE (LONG_PTR, 8) TEST_TYPE_ALIGN (LONG_PTR, 8) - TEST_TYPE_SIGNED(LONG_PTR) + TEST_TYPE_SIGNED (LONG_PTR) } static void test_pack_SIZE_T(void) @@ -173,6 +179,7 @@ static void test_pack_SIZE_T(void) /* SIZE_T */ TEST_TYPE_SIZE (SIZE_T, 8) TEST_TYPE_ALIGN (SIZE_T, 8) + TEST_TYPE_UNSIGNED(SIZE_T) } static void test_pack_SSIZE_T(void) @@ -180,6 +187,7 @@ static void test_pack_SSIZE_T(void) /* SSIZE_T */ TEST_TYPE_SIZE (SSIZE_T, 8) TEST_TYPE_ALIGN (SSIZE_T, 8) + TEST_TYPE_SIGNED (SSIZE_T) } static void test_pack_UHALF_PTR(void) @@ -484,6 +492,7 @@ static void test_pack_HRESULT(void) /* HRESULT */ TEST_TYPE_SIZE (HRESULT, 4) TEST_TYPE_ALIGN (HRESULT, 4) + TEST_TYPE_SIGNED (HRESULT) } static void test_pack_IMAGE_ARCHIVE_MEMBER_HEADER(void) @@ -835,15 +844,17 @@ static void test_pack_IMAGE_LINENUMBER(void) static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 112) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 112) */ TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, 8) } static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY32(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY32 */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, 88) - TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, 8) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, 72) */ + TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, 4) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, Size, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, Size, 4) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, Size, 0) @@ -871,45 +882,46 @@ static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY32(void) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitTotalFreeThreshold, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitTotalFreeThreshold, 4) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitTotalFreeThreshold, 28) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, LockPrefixTable, 8) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, LockPrefixTable, 8) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, LockPrefixTable, 4) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, LockPrefixTable, 4) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, LockPrefixTable, 32) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, MaximumAllocationSize, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, MaximumAllocationSize, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, MaximumAllocationSize, 40) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, MaximumAllocationSize, 36) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, VirtualMemoryThreshold, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, VirtualMemoryThreshold, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, VirtualMemoryThreshold, 44) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, VirtualMemoryThreshold, 40) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessHeapFlags, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessHeapFlags, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessHeapFlags, 48) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessHeapFlags, 44) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessAffinityMask, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessAffinityMask, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessAffinityMask, 52) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, ProcessAffinityMask, 48) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 2) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 2) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 56) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 2) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 2) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 58) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 8) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 8) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 64) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 52) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 2) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 2) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 54) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 4) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 4) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 56) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, SecurityCookie, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, SecurityCookie, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SecurityCookie, 72) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SecurityCookie, 60) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable, 76) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerTable, 64) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerCount, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerCount, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerCount, 80) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, SEHandlerCount, 68) } static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY64(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY64 */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, 112) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, 112) */ TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, 8) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, Size, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, Size, 4) @@ -956,9 +968,9 @@ static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY64(void) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 2) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 2) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 76) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 2) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 2) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 78) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 2) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 2) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 78) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 8) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 8) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 80) @@ -1738,7 +1750,7 @@ static void test_pack_LONG(void) /* LONG */ TEST_TYPE_SIZE (LONG, 4) TEST_TYPE_ALIGN (LONG, 4) - TEST_TYPE_SIGNED(LONG) + TEST_TYPE_SIGNED (LONG) } static void test_pack_LONGLONG(void) @@ -1746,7 +1758,7 @@ static void test_pack_LONGLONG(void) /* LONGLONG */ TEST_TYPE_SIZE (LONGLONG, 8) TEST_TYPE_ALIGN (LONGLONG, 8) - TEST_TYPE_SIGNED(LONGLONG) + TEST_TYPE_SIGNED (LONGLONG) } static void test_pack_LUID(void) @@ -2809,6 +2821,7 @@ static void test_pack_SECURITY_CONTEXT_TRACKING_MODE(void) /* SECURITY_CONTEXT_TRACKING_MODE */ TEST_TYPE_SIZE (SECURITY_CONTEXT_TRACKING_MODE, 1) TEST_TYPE_ALIGN (SECURITY_CONTEXT_TRACKING_MODE, 1) + TEST_TYPE_UNSIGNED(SECURITY_CONTEXT_TRACKING_MODE) } static void test_pack_SECURITY_DESCRIPTOR(void) @@ -2896,7 +2909,7 @@ static void test_pack_SHORT(void) /* SHORT */ TEST_TYPE_SIZE (SHORT, 2) TEST_TYPE_ALIGN (SHORT, 2) - TEST_TYPE_SIGNED(SHORT) + TEST_TYPE_SIGNED (SHORT) } static void test_pack_SID(void) @@ -3117,7 +3130,7 @@ static void test_pack_BOOL(void) /* BOOL */ TEST_TYPE_SIZE (BOOL, 4) TEST_TYPE_ALIGN (BOOL, 4) - TEST_TYPE_SIGNED(BOOL) + TEST_TYPE_SIGNED (BOOL) } static void test_pack_BYTE(void) @@ -3177,7 +3190,7 @@ static void test_pack_HFILE(void) /* HFILE */ TEST_TYPE_SIZE (HFILE, 4) TEST_TYPE_ALIGN (HFILE, 4) - TEST_TYPE_SIGNED(HFILE) + TEST_TYPE_SIGNED (HFILE) } static void test_pack_HGDIOBJ(void) @@ -3213,7 +3226,7 @@ static void test_pack_INT(void) /* INT */ TEST_TYPE_SIZE (INT, 4) TEST_TYPE_ALIGN (INT, 4) - TEST_TYPE_SIGNED(INT) + TEST_TYPE_SIGNED (INT) } static void test_pack_LOCALHANDLE(void) @@ -3228,6 +3241,7 @@ static void test_pack_LPARAM(void) /* LPARAM */ TEST_TYPE_SIZE (LPARAM, 8) TEST_TYPE_ALIGN (LPARAM, 8) + TEST_TYPE_SIGNED (LPARAM) } static void test_pack_LPCRECT(void) @@ -3296,6 +3310,7 @@ static void test_pack_LRESULT(void) /* LRESULT */ TEST_TYPE_SIZE (LRESULT, 8) TEST_TYPE_ALIGN (LRESULT, 8) + TEST_TYPE_SIGNED (LRESULT) } static void test_pack_POINT(void) @@ -3486,6 +3501,7 @@ static void test_pack_WPARAM(void) /* WPARAM */ TEST_TYPE_SIZE (WPARAM, 8) TEST_TYPE_ALIGN (WPARAM, 8) + TEST_TYPE_UNSIGNED(WPARAM) } #else /* _WIN64 */ @@ -3511,6 +3527,7 @@ static void test_pack_DWORD_PTR(void) /* DWORD_PTR */ TEST_TYPE_SIZE (DWORD_PTR, 4) TEST_TYPE_ALIGN (DWORD_PTR, 4) + TEST_TYPE_UNSIGNED(DWORD_PTR) } static void test_pack_HALF_PTR(void) @@ -3518,7 +3535,7 @@ static void test_pack_HALF_PTR(void) /* HALF_PTR */ TEST_TYPE_SIZE (HALF_PTR, 2) TEST_TYPE_ALIGN (HALF_PTR, 2) - TEST_TYPE_SIGNED(HALF_PTR) + TEST_TYPE_SIGNED (HALF_PTR) } static void test_pack_INT16(void) @@ -3526,7 +3543,7 @@ static void test_pack_INT16(void) /* INT16 */ TEST_TYPE_SIZE (INT16, 2) TEST_TYPE_ALIGN (INT16, 2) - TEST_TYPE_SIGNED(INT16) + TEST_TYPE_SIGNED (INT16) } static void test_pack_INT32(void) @@ -3534,7 +3551,7 @@ static void test_pack_INT32(void) /* INT32 */ TEST_TYPE_SIZE (INT32, 4) TEST_TYPE_ALIGN (INT32, 4) - TEST_TYPE_SIGNED(INT32) + TEST_TYPE_SIGNED (INT32) } static void test_pack_INT64(void) @@ -3542,7 +3559,7 @@ static void test_pack_INT64(void) /* INT64 */ TEST_TYPE_SIZE (INT64, 8) TEST_TYPE_ALIGN (INT64, 8) - TEST_TYPE_SIGNED(INT64) + TEST_TYPE_SIGNED (INT64) } static void test_pack_INT8(void) @@ -3550,7 +3567,7 @@ static void test_pack_INT8(void) /* INT8 */ TEST_TYPE_SIZE (INT8, 1) TEST_TYPE_ALIGN (INT8, 1) - TEST_TYPE_SIGNED(INT8) + TEST_TYPE_SIGNED (INT8) } static void test_pack_INT_PTR(void) @@ -3558,7 +3575,7 @@ static void test_pack_INT_PTR(void) /* INT_PTR */ TEST_TYPE_SIZE (INT_PTR, 4) TEST_TYPE_ALIGN (INT_PTR, 4) - TEST_TYPE_SIGNED(INT_PTR) + TEST_TYPE_SIGNED (INT_PTR) } static void test_pack_LONG32(void) @@ -3566,7 +3583,7 @@ static void test_pack_LONG32(void) /* LONG32 */ TEST_TYPE_SIZE (LONG32, 4) TEST_TYPE_ALIGN (LONG32, 4) - TEST_TYPE_SIGNED(LONG32) + TEST_TYPE_SIGNED (LONG32) } static void test_pack_LONG64(void) @@ -3574,7 +3591,7 @@ static void test_pack_LONG64(void) /* LONG64 */ TEST_TYPE_SIZE (LONG64, 8) TEST_TYPE_ALIGN (LONG64, 8) - TEST_TYPE_SIGNED(LONG64) + TEST_TYPE_SIGNED (LONG64) } static void test_pack_LONG_PTR(void) @@ -3582,7 +3599,7 @@ static void test_pack_LONG_PTR(void) /* LONG_PTR */ TEST_TYPE_SIZE (LONG_PTR, 4) TEST_TYPE_ALIGN (LONG_PTR, 4) - TEST_TYPE_SIGNED(LONG_PTR) + TEST_TYPE_SIGNED (LONG_PTR) } static void test_pack_SIZE_T(void) @@ -3590,6 +3607,7 @@ static void test_pack_SIZE_T(void) /* SIZE_T */ TEST_TYPE_SIZE (SIZE_T, 4) TEST_TYPE_ALIGN (SIZE_T, 4) + TEST_TYPE_UNSIGNED(SIZE_T) } static void test_pack_SSIZE_T(void) @@ -3597,6 +3615,7 @@ static void test_pack_SSIZE_T(void) /* SSIZE_T */ TEST_TYPE_SIZE (SSIZE_T, 4) TEST_TYPE_ALIGN (SSIZE_T, 4) + TEST_TYPE_SIGNED (SSIZE_T) } static void test_pack_UHALF_PTR(void) @@ -3901,6 +3920,7 @@ static void test_pack_HRESULT(void) /* HRESULT */ TEST_TYPE_SIZE (HRESULT, 4) TEST_TYPE_ALIGN (HRESULT, 4) + TEST_TYPE_SIGNED (HRESULT) } static void test_pack_IMAGE_ARCHIVE_MEMBER_HEADER(void) @@ -4252,14 +4272,16 @@ static void test_pack_IMAGE_LINENUMBER(void) static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 72) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 72) */ TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, 4) } static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY32(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY32 */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, 72) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, 72) */ TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, 4) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, Size, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, Size, 4) @@ -4306,9 +4328,9 @@ static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY32(void) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 2) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 2) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, CSDVersion, 52) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 2) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 2) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, Reserved1, 54) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 2) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 2) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, DependentLoadFlags, 54) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 4) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY32, EditList, 56) @@ -4326,7 +4348,8 @@ static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY32(void) static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY64(void) { /* IMAGE_LOAD_CONFIG_DIRECTORY64 */ - TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, 112) + /* size varies depending on Windows version */ + /* TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, 112) */ TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, 8) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, Size, 4) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, Size, 4) @@ -4373,9 +4396,9 @@ static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY64(void) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 2) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 2) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, CSDVersion, 76) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 2) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 2) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, Reserved1, 78) + TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 2) + TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 2) + TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, DependentLoadFlags, 78) TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 8) TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 8) TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY64, EditList, 80) @@ -5155,7 +5178,7 @@ static void test_pack_LONG(void) /* LONG */ TEST_TYPE_SIZE (LONG, 4) TEST_TYPE_ALIGN (LONG, 4) - TEST_TYPE_SIGNED(LONG) + TEST_TYPE_SIGNED (LONG) } static void test_pack_LONGLONG(void) @@ -5163,7 +5186,7 @@ static void test_pack_LONGLONG(void) /* LONGLONG */ TEST_TYPE_SIZE (LONGLONG, 8) TEST_TYPE_ALIGN (LONGLONG, 8) - TEST_TYPE_SIGNED(LONGLONG) + TEST_TYPE_SIGNED (LONGLONG) } static void test_pack_LUID(void) @@ -6226,6 +6249,7 @@ static void test_pack_SECURITY_CONTEXT_TRACKING_MODE(void) /* SECURITY_CONTEXT_TRACKING_MODE */ TEST_TYPE_SIZE (SECURITY_CONTEXT_TRACKING_MODE, 1) TEST_TYPE_ALIGN (SECURITY_CONTEXT_TRACKING_MODE, 1) + TEST_TYPE_UNSIGNED(SECURITY_CONTEXT_TRACKING_MODE) } static void test_pack_SECURITY_DESCRIPTOR(void) @@ -6313,7 +6337,7 @@ static void test_pack_SHORT(void) /* SHORT */ TEST_TYPE_SIZE (SHORT, 2) TEST_TYPE_ALIGN (SHORT, 2) - TEST_TYPE_SIGNED(SHORT) + TEST_TYPE_SIGNED (SHORT) } static void test_pack_SID(void) @@ -6534,7 +6558,7 @@ static void test_pack_BOOL(void) /* BOOL */ TEST_TYPE_SIZE (BOOL, 4) TEST_TYPE_ALIGN (BOOL, 4) - TEST_TYPE_SIGNED(BOOL) + TEST_TYPE_SIGNED (BOOL) } static void test_pack_BYTE(void) @@ -6594,7 +6618,7 @@ static void test_pack_HFILE(void) /* HFILE */ TEST_TYPE_SIZE (HFILE, 4) TEST_TYPE_ALIGN (HFILE, 4) - TEST_TYPE_SIGNED(HFILE) + TEST_TYPE_SIGNED (HFILE) } static void test_pack_HGDIOBJ(void) @@ -6630,7 +6654,7 @@ static void test_pack_INT(void) /* INT */ TEST_TYPE_SIZE (INT, 4) TEST_TYPE_ALIGN (INT, 4) - TEST_TYPE_SIGNED(INT) + TEST_TYPE_SIGNED (INT) } static void test_pack_LOCALHANDLE(void) @@ -6645,6 +6669,7 @@ static void test_pack_LPARAM(void) /* LPARAM */ TEST_TYPE_SIZE (LPARAM, 4) TEST_TYPE_ALIGN (LPARAM, 4) + TEST_TYPE_SIGNED (LPARAM) } static void test_pack_LPCRECT(void) @@ -6713,6 +6738,7 @@ static void test_pack_LRESULT(void) /* LRESULT */ TEST_TYPE_SIZE (LRESULT, 4) TEST_TYPE_ALIGN (LRESULT, 4) + TEST_TYPE_SIGNED (LRESULT) } static void test_pack_POINT(void) @@ -6903,6 +6929,7 @@ static void test_pack_WPARAM(void) /* WPARAM */ TEST_TYPE_SIZE (WPARAM, 4) TEST_TYPE_ALIGN (WPARAM, 4) + TEST_TYPE_UNSIGNED(WPARAM) } #endif /* _WIN64 */ diff --git a/modules/rostests/winetests/ntdll/info.c b/modules/rostests/winetests/ntdll/info.c index e4332fe68d2..92dc64b5eff 100644 --- a/modules/rostests/winetests/ntdll/info.c +++ b/modules/rostests/winetests/ntdll/info.c @@ -18,59 +18,87 @@ * */ -#include "ntdll_test.h" -#include +#include #include -static NTSTATUS (WINAPI * pRtlDowncaseUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN); +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winnls.h" +#include "ddk/ntddk.h" +#include "psapi.h" +#include "wine/test.h" + +#ifdef __REACTOS__ +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define AlwaysOn DEPPolicyAlwaysOn +#endif + static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); +static NTSTATUS (WINAPI * pNtSetSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG); +static NTSTATUS (WINAPI * pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtQuerySystemInformationEx)(SYSTEM_INFORMATION_CLASS, void*, ULONG, void*, ULONG, ULONG*); static NTSTATUS (WINAPI * pNtPowerInformation)(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG); -static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI * pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG); static NTSTATUS (WINAPI * pNtSetInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG); static NTSTATUS (WINAPI * pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*); static NTSTATUS (WINAPI * pNtQueryVirtualMemory)(HANDLE, LPCVOID, MEMORY_INFORMATION_CLASS , PVOID , SIZE_T , SIZE_T *); static NTSTATUS (WINAPI * pNtCreateSection)(HANDLE*,ACCESS_MASK,const OBJECT_ATTRIBUTES*,const LARGE_INTEGER*,ULONG,ULONG,HANDLE); -static NTSTATUS (WINAPI * pNtMapViewOfSection)(HANDLE,HANDLE,PVOID*,ULONG,SIZE_T,const LARGE_INTEGER*,SIZE_T*,SECTION_INHERIT,ULONG,ULONG); +static NTSTATUS (WINAPI * pNtMapViewOfSection)(HANDLE,HANDLE,PVOID*,ULONG_PTR,SIZE_T,const LARGE_INTEGER*,SIZE_T*,SECTION_INHERIT,ULONG,ULONG); static NTSTATUS (WINAPI * pNtUnmapViewOfSection)(HANDLE,PVOID); static NTSTATUS (WINAPI * pNtClose)(HANDLE); static ULONG (WINAPI * pNtGetCurrentProcessorNumber)(void); -static BOOL (WINAPI * pIsWow64Process)(HANDLE, PBOOL); static BOOL (WINAPI * pGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_RELATIONSHIP,SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*,DWORD*); +static DEP_SYSTEM_POLICY_TYPE (WINAPI * pGetSystemDEPPolicy)(void); +static NTSTATUS (WINAPI * pNtOpenThread)(HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID *); +static NTSTATUS (WINAPI * pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, void *, ULONG, ULONG *); +static NTSTATUS (WINAPI * pNtCreateDebugObject)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, ULONG ); +static NTSTATUS (WINAPI * pNtSetInformationDebugObject)(HANDLE,DEBUGOBJECTINFOCLASS,PVOID,ULONG,ULONG*); +static NTSTATUS (WINAPI * pDbgUiConvertStateChangeStructure)(DBGUI_WAIT_STATE_CHANGE*,DEBUG_EVENT*); +static HANDLE (WINAPI * pDbgUiGetThreadDebugObject)(void); +static void (WINAPI * pDbgUiSetThreadDebugObject)(HANDLE); +static NTSTATUS (WINAPI * pNtSystemDebugControl)(SYSDBG_COMMAND,PVOID,ULONG,PVOID,ULONG,PULONG); static BOOL is_wow64; +static BOOL old_wow64; /* one_before_last_pid is used to be able to compare values of a still running process with the output of the test_query_process_times and test_query_process_handlecount tests. */ static DWORD one_before_last_pid = 0; +static inline DWORD_PTR get_affinity_mask(DWORD num_cpus) +{ + if (num_cpus >= sizeof(DWORD_PTR) * 8) return ~(DWORD_PTR)0; + return ((DWORD_PTR)1 << num_cpus) - 1; +} + #define NTDLL_GET_PROC(func) do { \ p ## func = (void*)GetProcAddress(hntdll, #func); \ if(!p ## func) { \ trace("GetProcAddress(%s) failed\n", #func); \ - return FALSE; \ } \ } while(0) -static BOOL InitFunctionPtrs(void) +/* Firmware table providers */ +#define ACPI 0x41435049 +#define FIRM 0x4649524D +#define RSMB 0x52534D42 + +static void InitFunctionPtrs(void) { /* All needed functions are NT based, so using GetModuleHandle is a good check */ HMODULE hntdll = GetModuleHandleA("ntdll"); HMODULE hkernel32 = GetModuleHandleA("kernel32"); - if (!hntdll) - { - win_skip("Not running on NT\n"); - return FALSE; - } - - NTDLL_GET_PROC(RtlDowncaseUnicodeString); NTDLL_GET_PROC(NtQuerySystemInformation); + NTDLL_GET_PROC(NtQuerySystemInformationEx); + NTDLL_GET_PROC(NtSetSystemInformation); + NTDLL_GET_PROC(RtlGetNativeSystemInformation); NTDLL_GET_PROC(NtPowerInformation); - NTDLL_GET_PROC(NtQueryInformationProcess); NTDLL_GET_PROC(NtQueryInformationThread); NTDLL_GET_PROC(NtSetInformationProcess); NTDLL_GET_PROC(NtSetInformationThread); @@ -80,84 +108,332 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(NtCreateSection); NTDLL_GET_PROC(NtMapViewOfSection); NTDLL_GET_PROC(NtUnmapViewOfSection); + NTDLL_GET_PROC(NtOpenThread); + NTDLL_GET_PROC(NtQueryObject); + NTDLL_GET_PROC(NtCreateDebugObject); + NTDLL_GET_PROC(NtSetInformationDebugObject); + NTDLL_GET_PROC(NtGetCurrentProcessorNumber); + NTDLL_GET_PROC(DbgUiConvertStateChangeStructure); + NTDLL_GET_PROC(DbgUiGetThreadDebugObject); + NTDLL_GET_PROC(DbgUiSetThreadDebugObject); + NTDLL_GET_PROC(NtSystemDebugControl); - /* not present before XP */ - pNtGetCurrentProcessorNumber = (void *) GetProcAddress(hntdll, "NtGetCurrentProcessorNumber"); + if (!IsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; - pIsWow64Process = (void *)GetProcAddress(hkernel32, "IsWow64Process"); - if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + if (is_wow64) + { + TEB64 *teb64 = ULongToPtr( NtCurrentTeb()->GdiBatchCount ); - /* starting with Win7 */ - pNtQuerySystemInformationEx = (void *) GetProcAddress(hntdll, "NtQuerySystemInformationEx"); - if (!pNtQuerySystemInformationEx) - win_skip("NtQuerySystemInformationEx() is not supported, some tests will be skipped.\n"); + if (teb64) + { + PEB64 *peb64 = ULongToPtr(teb64->Peb); + old_wow64 = !peb64->LdrData; + } + } + pGetSystemDEPPolicy = (void *)GetProcAddress(hkernel32, "GetSystemDEPPolicy"); pGetLogicalProcessorInformationEx = (void *) GetProcAddress(hkernel32, "GetLogicalProcessorInformationEx"); - - return TRUE; } static void test_query_basic(void) { NTSTATUS status; - ULONG ReturnLength; - SYSTEM_BASIC_INFORMATION sbi; + ULONG i, ReturnLength; + SYSTEM_BASIC_INFORMATION sbi, sbi2, sbi3; /* This test also covers some basic parameter testing that should be the same for * every information class */ /* Use a nonexistent info class */ - trace("Check nonexistent info class\n"); status = pNtQuerySystemInformation(-1, NULL, 0, NULL); ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED /* vista */, - "Expected STATUS_INVALID_INFO_CLASS or STATUS_NOT_IMPLEMENTED, got %08x\n", status); + "Expected STATUS_INVALID_INFO_CLASS or STATUS_NOT_IMPLEMENTED, got %08lx\n", status); /* Use an existing class but with a zero-length buffer */ - trace("Check zero-length buffer\n"); status = pNtQuerySystemInformation(SystemBasicInformation, NULL, 0, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); /* Use an existing class, correct length but no SystemInformation buffer */ - trace("Check no SystemInformation buffer\n"); status = pNtQuerySystemInformation(SystemBasicInformation, NULL, sizeof(sbi), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER /* vista */, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got %08lx\n", status); /* Use an existing class, correct length, a pointer to a buffer but no ReturnLength pointer */ - trace("Check no ReturnLength pointer\n"); status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); /* Check a too large buffer size */ - trace("Check a too large buffer size\n"); status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi) * 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); /* Finally some correct calls */ - trace("Check with correct parameters\n"); + memset(&sbi, 0xcc, sizeof(sbi)); status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(sbi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(sbi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); /* Check if we have some return values */ - trace("Number of Processors : %d\n", sbi.NumberOfProcessors); + if (winetest_debug > 1) trace("Number of Processors : %d\n", sbi.NumberOfProcessors); ok( sbi.NumberOfProcessors > 0, "Expected more than 0 processors, got %d\n", sbi.NumberOfProcessors); + + memset(&sbi2, 0xcc, sizeof(sbi2)); + status = pRtlGetNativeSystemInformation(SystemBasicInformation, &sbi2, sizeof(sbi2), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx.\n", status); + ok( sizeof(sbi2) == ReturnLength, "Unexpected length %lu.\n", ReturnLength); + + ok( sbi.unknown == sbi2.unknown, "Expected unknown %#lx, got %#lx.\n", sbi.unknown, sbi2.unknown); + ok( sbi.KeMaximumIncrement == sbi2.KeMaximumIncrement, "Expected KeMaximumIncrement %lu, got %lu.\n", + sbi.KeMaximumIncrement, sbi2.KeMaximumIncrement); + ok( sbi.PageSize == sbi2.PageSize, "Expected PageSize field %lu, %lu.\n", sbi.PageSize, sbi2.PageSize); + ok( sbi.MmNumberOfPhysicalPages == sbi2.MmNumberOfPhysicalPages, + "Expected MmNumberOfPhysicalPages %lu, got %lu.\n", + sbi.MmNumberOfPhysicalPages, sbi2.MmNumberOfPhysicalPages); + ok( sbi.MmLowestPhysicalPage == sbi2.MmLowestPhysicalPage, "Expected MmLowestPhysicalPage %lu, got %lu.\n", + sbi.MmLowestPhysicalPage, sbi2.MmLowestPhysicalPage); + ok( sbi.MmHighestPhysicalPage == sbi2.MmHighestPhysicalPage, "Expected MmHighestPhysicalPage %lu, got %lu.\n", + sbi.MmHighestPhysicalPage, sbi2.MmHighestPhysicalPage); + /* Higher 32 bits of AllocationGranularity is sometimes garbage on Windows. */ + ok( (ULONG)sbi.AllocationGranularity == (ULONG)sbi2.AllocationGranularity, + "Expected AllocationGranularity %#Ix, got %#Ix.\n", + sbi.AllocationGranularity, sbi2.AllocationGranularity); + ok( sbi.LowestUserAddress == sbi2.LowestUserAddress, "Expected LowestUserAddress %p, got %p.\n", + sbi.LowestUserAddress, sbi2.LowestUserAddress); + ok( sbi.ActiveProcessorsAffinityMask == sbi2.ActiveProcessorsAffinityMask, + "Expected ActiveProcessorsAffinityMask %#Ix, got %#Ix.\n", + sbi.ActiveProcessorsAffinityMask, sbi2.ActiveProcessorsAffinityMask); + ok( sbi.NumberOfProcessors == sbi2.NumberOfProcessors, "Expected NumberOfProcessors %u, got %u.\n", + sbi.NumberOfProcessors, sbi2.NumberOfProcessors); +#ifdef _WIN64 + ok( sbi.HighestUserAddress == sbi2.HighestUserAddress, "Expected HighestUserAddress %p, got %p.\n", + (void *)sbi.HighestUserAddress, (void *)sbi2.HighestUserAddress); +#else + ok( sbi.HighestUserAddress == (void *)0x7ffeffff, "wrong limit %p\n", sbi.HighestUserAddress); + todo_wine_if( old_wow64 ) + ok( sbi2.HighestUserAddress == (is_wow64 ? (void *)0xfffeffff : (void *)0x7ffeffff), + "wrong limit %p\n", sbi.HighestUserAddress); +#endif + + memset(&sbi3, 0xcc, sizeof(sbi3)); + status = pNtQuerySystemInformation(SystemNativeBasicInformation, &sbi3, sizeof(sbi3), &ReturnLength); +#ifdef _WIN64 + ok( status == STATUS_SUCCESS || broken(status == STATUS_INVALID_INFO_CLASS), "got %08lx\n", status); + if (!status) + { + ok( sizeof(sbi3) == ReturnLength, "Unexpected length %lu.\n", ReturnLength); + ok( !memcmp( &sbi2, &sbi3, offsetof(SYSTEM_BASIC_INFORMATION,NumberOfProcessors)+1 ), + "info is different\n" ); + } +#else + ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_SUCCESS), /* some Win8 */ + "got %08lx\n", status); + status = pRtlGetNativeSystemInformation( SystemNativeBasicInformation, &sbi3, sizeof(sbi3), &ReturnLength ); + ok( !status || status == STATUS_INFO_LENGTH_MISMATCH || + broken(status == STATUS_INVALID_INFO_CLASS) || broken(status == STATUS_NOT_IMPLEMENTED), + "failed %lx\n", status ); + if (!status || status == STATUS_INFO_LENGTH_MISMATCH) + todo_wine_if( old_wow64 ) + ok( !status == !is_wow64, "got wrong status %lx wow64 %u\n", status, is_wow64 ); + if (!status) + { + ok( sizeof(sbi3) == ReturnLength, "Unexpected length %lu.\n", ReturnLength); + ok( !memcmp( &sbi2, &sbi3, offsetof(SYSTEM_BASIC_INFORMATION,NumberOfProcessors)+1 ), + "info is different\n" ); + } + else if (status == STATUS_INFO_LENGTH_MISMATCH) + { + /* SystemNativeBasicInformation uses the 64-bit structure on Wow64 */ + struct + { + DWORD unknown; + ULONG KeMaximumIncrement; + ULONG PageSize; + ULONG MmNumberOfPhysicalPages; + ULONG MmLowestPhysicalPage; + ULONG MmHighestPhysicalPage; + ULONG64 AllocationGranularity; + ULONG64 LowestUserAddress; + ULONG64 HighestUserAddress; + ULONG64 ActiveProcessorsAffinityMask; + BYTE NumberOfProcessors; + } sbi64; + + ok( ReturnLength == sizeof(sbi64), "len %lx\n", ReturnLength ); + memset( &sbi64, 0xcc, sizeof(sbi64) ); + ReturnLength = 0; + status = pRtlGetNativeSystemInformation( SystemNativeBasicInformation, &sbi64, sizeof(sbi64), &ReturnLength ); + ok( !status, "failed %lx\n", status ); + ok( ReturnLength == sizeof(sbi64), "len %lx\n", ReturnLength ); + + ok( sbi.unknown == sbi64.unknown, "unknown %#lx / %#lx\n", sbi.unknown, sbi64.unknown); + ok( sbi.KeMaximumIncrement == sbi64.KeMaximumIncrement, "KeMaximumIncrement %lu / %lu\n", + sbi.KeMaximumIncrement, sbi64.KeMaximumIncrement); + ok( sbi.PageSize == sbi64.PageSize, "PageSize %lu / %lu\n", sbi.PageSize, sbi64.PageSize); + ok( sbi.MmNumberOfPhysicalPages == sbi64.MmNumberOfPhysicalPages, + "MmNumberOfPhysicalPages %lu / %lu\n", + sbi.MmNumberOfPhysicalPages, sbi64.MmNumberOfPhysicalPages); + ok( sbi.MmLowestPhysicalPage == sbi64.MmLowestPhysicalPage, "MmLowestPhysicalPage %lu / %lu\n", + sbi.MmLowestPhysicalPage, sbi64.MmLowestPhysicalPage); + ok( sbi.MmHighestPhysicalPage == sbi64.MmHighestPhysicalPage, "MmHighestPhysicalPage %lu / %lu\n", + sbi.MmHighestPhysicalPage, sbi64.MmHighestPhysicalPage); + ok( sbi.AllocationGranularity == (ULONG_PTR)sbi64.AllocationGranularity, + "AllocationGranularity %#Ix / %#Ix\n", sbi.AllocationGranularity, + (ULONG_PTR)sbi64.AllocationGranularity); + ok( (ULONG_PTR)sbi.LowestUserAddress == sbi64.LowestUserAddress, "LowestUserAddress %p / %s\n", + sbi.LowestUserAddress, wine_dbgstr_longlong(sbi64.LowestUserAddress)); + ok( sbi.ActiveProcessorsAffinityMask == sbi64.ActiveProcessorsAffinityMask, + "ActiveProcessorsAffinityMask %#Ix / %s\n", + sbi.ActiveProcessorsAffinityMask, wine_dbgstr_longlong(sbi64.ActiveProcessorsAffinityMask)); + ok( sbi.NumberOfProcessors == sbi64.NumberOfProcessors, "NumberOfProcessors %u / %u\n", + sbi.NumberOfProcessors, sbi64.NumberOfProcessors); + ok( sbi64.HighestUserAddress == 0x7ffffffeffff, "wrong limit %s\n", + wine_dbgstr_longlong(sbi64.HighestUserAddress)); + } +#endif + + memset(&sbi3, 0xcc, sizeof(sbi3)); + status = pNtQuerySystemInformation(SystemEmulationBasicInformation, &sbi3, sizeof(sbi3), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx.\n", status); + ok( sizeof(sbi3) == ReturnLength, "Unexpected length %lu.\n", ReturnLength); + ok( !memcmp( &sbi, &sbi3, offsetof(SYSTEM_BASIC_INFORMATION,NumberOfProcessors)+1 ), + "info is different\n" ); + + for (i = 0; i < 256; i++) + { + NTSTATUS expect = pNtQuerySystemInformation( i, NULL, 0, &ReturnLength ); + status = pRtlGetNativeSystemInformation( i, NULL, 0, &ReturnLength ); + switch (i) + { + case SystemNativeBasicInformation: + ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_INFO_LENGTH_MISMATCH || + broken(status == STATUS_NOT_IMPLEMENTED) /* vista */, "%lu: %lx / %lx\n", i, status, expect ); + break; + case SystemBasicInformation: + case SystemCpuInformation: + case SystemEmulationBasicInformation: + case SystemEmulationProcessorInformation: + ok( status == expect, "%lu: %lx / %lx\n", i, status, expect ); + break; + default: + if (is_wow64) /* only a few info classes are supported on Wow64 */ + todo_wine_if (is_wow64 && status != STATUS_INVALID_INFO_CLASS) + ok( status == STATUS_INVALID_INFO_CLASS || + broken(status == STATUS_NOT_IMPLEMENTED), /* vista */ + "%lu: %lx\n", i, status ); + else + ok( status == expect, "%lu: %lx / %lx\n", i, status, expect ); + break; + } + } } static void test_query_cpu(void) { - DWORD status; - ULONG ReturnLength; - SYSTEM_CPU_INFORMATION sci; + NTSTATUS status; + ULONG len, buffer[16]; + SYSTEM_PROCESSOR_FEATURES_INFORMATION features; + SYSTEM_CPU_INFORMATION sci, sci2, sci3; - status = pNtQuerySystemInformation(SystemCpuInformation, &sci, sizeof(sci), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(sci) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + memset(&sci, 0xcc, sizeof(sci)); + status = pNtQuerySystemInformation(SystemCpuInformation, &sci, sizeof(sci), &len); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(sci) == len, "Inconsistent length %ld\n", len); - /* Check if we have some return values */ - trace("Processor FeatureSet : %08x\n", sci.ProcessorFeatureBits); - ok( sci.ProcessorFeatureBits != 0, "Expected some features for this processor, got %08x\n", sci.ProcessorFeatureBits); + memset(&sci2, 0xcc, sizeof(sci2)); + status = pRtlGetNativeSystemInformation(SystemCpuInformation, &sci2, sizeof(sci2), &len); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx.\n", status); + ok( sizeof(sci2) == len, "Unexpected length %lu.\n", len); + + if (is_wow64) + { + ok( sci.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL, "ProcessorArchitecture wrong %x\n", + sci.ProcessorArchitecture ); + ok( sci2.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || + sci2.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64, + "ProcessorArchitecture wrong %x\n", sci2.ProcessorArchitecture ); + } + else + ok( sci.ProcessorArchitecture == sci2.ProcessorArchitecture, + "ProcessorArchitecture differs %x / %x\n", + sci.ProcessorArchitecture, sci2.ProcessorArchitecture ); + + if (sci2.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM64) + { + /* Check if we have some return values */ + if (winetest_debug > 1) trace("Processor FeatureSet : %08lx\n", sci.ProcessorFeatureBits); + ok( sci.ProcessorFeatureBits != 0, "Expected some features for this processor, got %08lx\n", + sci.ProcessorFeatureBits); + } + ok( sci.ProcessorLevel == sci2.ProcessorLevel, "ProcessorLevel differs %x / %x\n", + sci.ProcessorLevel, sci2.ProcessorLevel ); + ok( sci.ProcessorRevision == sci2.ProcessorRevision, "ProcessorRevision differs %x / %x\n", + sci.ProcessorRevision, sci2.ProcessorRevision ); + ok( sci.MaximumProcessors == sci2.MaximumProcessors, "MaximumProcessors differs %x / %x\n", + sci.MaximumProcessors, sci2.MaximumProcessors ); + ok( sci.ProcessorFeatureBits == sci2.ProcessorFeatureBits, "ProcessorFeatureBits differs %lx / %lx\n", + sci.ProcessorFeatureBits, sci2.ProcessorFeatureBits ); + + memset(&sci3, 0xcc, sizeof(sci3)); + status = pNtQuerySystemInformation(SystemEmulationProcessorInformation, &sci3, sizeof(sci3), &len); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx.\n", status); + ok( sizeof(sci3) == len, "Unexpected length %lu.\n", len); + +#ifdef _WIN64 + if (sci2.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64) + ok( sci3.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM, "ProcessorArchitecture wrong %x\n", + sci3.ProcessorArchitecture ); + else + ok( sci3.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL, "ProcessorArchitecture wrong %x\n", + sci3.ProcessorArchitecture ); +#else + ok( sci.ProcessorArchitecture == sci3.ProcessorArchitecture, + "ProcessorArchitecture differs %x / %x\n", + sci.ProcessorArchitecture, sci3.ProcessorArchitecture ); +#endif + ok( sci.ProcessorLevel == sci3.ProcessorLevel, "ProcessorLevel differs %x / %x\n", + sci.ProcessorLevel, sci3.ProcessorLevel ); + ok( sci.ProcessorRevision == sci3.ProcessorRevision, "ProcessorRevision differs %x / %x\n", + sci.ProcessorRevision, sci3.ProcessorRevision ); + ok( sci.MaximumProcessors == sci3.MaximumProcessors, "MaximumProcessors differs %x / %x\n", + sci.MaximumProcessors, sci3.MaximumProcessors ); + ok( sci.ProcessorFeatureBits == sci3.ProcessorFeatureBits, "ProcessorFeatureBits differs %lx / %lx\n", + sci.ProcessorFeatureBits, sci3.ProcessorFeatureBits ); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorFeaturesInformation, &features, sizeof(features), &len ); + if (status != STATUS_NOT_SUPPORTED && status != STATUS_INVALID_INFO_CLASS) + { + ok( !status, "SystemProcessorFeaturesInformation failed %lx\n", status ); + ok( len == sizeof(features), "wrong len %lu\n", len ); + ok( (ULONG)features.ProcessorFeatureBits == sci.ProcessorFeatureBits, "wrong bits %I64x / %lx\n", + features.ProcessorFeatureBits, sci.ProcessorFeatureBits ); + } + else skip( "SystemProcessorFeaturesInformation is not supported\n" ); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorBrandString, buffer, sizeof(buffer), &len ); + if (status != STATUS_NOT_SUPPORTED) + { + ok( !status, "SystemProcessorBrandString failed %lx\n", status ); + ok( len == 49, "wrong len %lu\n", len ); + trace( "got %s len %u\n", debugstr_a( (char *)buffer ), lstrlenA( (char *)buffer )); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorBrandString, buffer, 49, &len ); + ok( !status, "SystemProcessorBrandString failed %lx\n", status ); + ok( len == 49, "wrong len %lu\n", len ); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorBrandString, buffer, 48, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "SystemProcessorBrandString failed %lx\n", status ); + ok( len == 49, "wrong len %lu\n", len ); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorBrandString, (char *)buffer + 1, 49, &len ); + ok( status == STATUS_DATATYPE_MISALIGNMENT, "SystemProcessorBrandString failed %lx\n", status ); + ok( len == 0xdeadbeef, "wrong len %lu\n", len ); + } + else skip( "SystemProcessorBrandString is not supported\n" ); } static void test_query_performance(void) @@ -168,7 +444,7 @@ static void test_query_performance(void) DWORD size = sizeof(SYSTEM_PERFORMANCE_INFORMATION); status = pNtQuerySystemInformation(SystemPerformanceInformation, buffer, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); status = pNtQuerySystemInformation(SystemPerformanceInformation, buffer, size, &ReturnLength); if (status == STATUS_INFO_LENGTH_MISMATCH && is_wow64) @@ -177,13 +453,13 @@ static void test_query_performance(void) size += 16; status = pNtQuerySystemInformation(SystemPerformanceInformation, buffer, size, &ReturnLength); } - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( ReturnLength == size, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( ReturnLength == size, "Inconsistent length %ld\n", ReturnLength); status = pNtQuerySystemInformation(SystemPerformanceInformation, buffer, size + 2, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( ReturnLength == size || ReturnLength == size + 2, - "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( ReturnLength == size || ReturnLength == size + 2 /* win8+ */, + "Inconsistent length %ld\n", ReturnLength); /* Not return values yet, as struct members are unknown */ } @@ -204,83 +480,55 @@ static void test_query_timeofday(void) SYSTEM_TIMEOFDAY_INFORMATION_PRIVATE sti; - /* The struct size for NT (32 bytes) and Win2K/XP (48 bytes) differ. - * - * Windows 2000 and XP return STATUS_INFO_LENGTH_MISMATCH if the given buffer size is greater - * then 48 and 0 otherwise - * Windows NT returns STATUS_INFO_LENGTH_MISMATCH when the given buffer size is not correct - * and 0 otherwise - * - * Windows 2000 and XP copy the given buffer size into the provided buffer, if the return code is STATUS_SUCCESS - * NT only fills the buffer if the return code is STATUS_SUCCESS - * - */ + status = pNtQuerySystemInformation( SystemTimeOfDayInformation, &sti, 0, &ReturnLength ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%ld)\n", ReturnLength); - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, sizeof(sti), &ReturnLength); + sti.uCurrentTimeZoneId = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemTimeOfDayInformation, &sti, 24, &ReturnLength ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( 24 == ReturnLength, "ReturnLength should be 24, it is (%ld)\n", ReturnLength); + ok( 0xdeadbeef == sti.uCurrentTimeZoneId, "This part of the buffer should not have been filled\n"); - if (status == STATUS_INFO_LENGTH_MISMATCH) - { - trace("Windows version is NT, we have to cater for differences with W2K/WinXP\n"); - - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%d)\n", ReturnLength); + sti.uCurrentTimeZoneId = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemTimeOfDayInformation, &sti, 32, &ReturnLength ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( 32 == ReturnLength, "ReturnLength should be 32, it is (%ld)\n", ReturnLength); + ok( 0xdeadbeef != sti.uCurrentTimeZoneId, "Buffer should have been partially filled\n"); - sti.uCurrentTimeZoneId = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 28, &ReturnLength); - ok(status == STATUS_SUCCESS || broken(status == STATUS_INFO_LENGTH_MISMATCH /* NT4 */), "Expected STATUS_SUCCESS, got %08x\n", status); - ok( 0xdeadbeef == sti.uCurrentTimeZoneId, "This part of the buffer should not have been filled\n"); + status = pNtQuerySystemInformation( SystemTimeOfDayInformation, &sti, 49, &ReturnLength ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( ReturnLength == 0 || ReturnLength == sizeof(sti) /* vista */, + "ReturnLength should be 0, it is (%ld)\n", ReturnLength); - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 32, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( 32 == ReturnLength, "ReturnLength should be 0, it is (%d)\n", ReturnLength); - } - else - { - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 0, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%d)\n", ReturnLength); - - sti.uCurrentTimeZoneId = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 24, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( 24 == ReturnLength, "ReturnLength should be 24, it is (%d)\n", ReturnLength); - ok( 0xdeadbeef == sti.uCurrentTimeZoneId, "This part of the buffer should not have been filled\n"); - - sti.uCurrentTimeZoneId = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 32, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( 32 == ReturnLength, "ReturnLength should be 32, it is (%d)\n", ReturnLength); - ok( 0xdeadbeef != sti.uCurrentTimeZoneId, "Buffer should have been partially filled\n"); - - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 49, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( ReturnLength == 0 || ReturnLength == sizeof(sti) /* vista */, - "ReturnLength should be 0, it is (%d)\n", ReturnLength); - - status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, sizeof(sti), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(sti) == ReturnLength, "Inconsistent length %d\n", ReturnLength); - } + status = pNtQuerySystemInformation( SystemTimeOfDayInformation, &sti, sizeof(sti), &ReturnLength ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(sti) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); /* Check if we have some return values */ - trace("uCurrentTimeZoneId : (%d)\n", sti.uCurrentTimeZoneId); + if (winetest_debug > 1) trace("uCurrentTimeZoneId : (%ld)\n", sti.uCurrentTimeZoneId); } -static void test_query_process(void) +static void test_query_process( BOOL extended ) { NTSTATUS status; DWORD last_pid; ULONG ReturnLength; int i = 0, k = 0; - BOOL is_nt = FALSE; - SYSTEM_BASIC_INFORMATION sbi; + PROCESS_BASIC_INFORMATION pbi; + THREAD_BASIC_INFORMATION tbi; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; + HANDLE handle; /* Copy of our winternl.h structure turned into a private one */ typedef struct _SYSTEM_PROCESS_INFORMATION_PRIVATE { ULONG NextEntryOffset; DWORD dwThreadCount; - DWORD dwUnknown1[6]; + LARGE_INTEGER WorkingSetPrivateSize; + ULONG HardFaultCount; + ULONG NumberOfThreadsHighWatermark; + ULONGLONG CycleTime; FILETIME ftCreationTime; FILETIME ftUserTime; FILETIME ftKernelTime; @@ -289,100 +537,189 @@ static void test_query_process(void) HANDLE UniqueProcessId; HANDLE ParentProcessId; ULONG HandleCount; - DWORD dwUnknown3; - DWORD dwUnknown4; - VM_COUNTERS vmCounters; + ULONG SessionId; + ULONG_PTR UniqueProcessKey; + VM_COUNTERS_EX vmCounters; IO_COUNTERS ioCounters; SYSTEM_THREAD_INFORMATION ti[1]; } SYSTEM_PROCESS_INFORMATION_PRIVATE; - ULONG SystemInformationLength = sizeof(SYSTEM_PROCESS_INFORMATION_PRIVATE); - SYSTEM_PROCESS_INFORMATION_PRIVATE *spi, *spi_buf = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); + BOOL is_process_wow64 = FALSE, current_process_found = FALSE; + SYSTEM_PROCESS_INFORMATION_PRIVATE *spi, *spi_buf; + SYSTEM_EXTENDED_THREAD_INFORMATION *ti; + SYSTEM_INFORMATION_CLASS info_class; + void *expected_address; + ULONG thread_info_size; + + if (extended) + { + info_class = SystemExtendedProcessInformation; + thread_info_size = sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION); + } + else + { + info_class = SystemProcessInformation; + thread_info_size = sizeof(SYSTEM_THREAD_INFORMATION); + } /* test ReturnLength */ ReturnLength = 0; - status = pNtQuerySystemInformation(SystemProcessInformation, NULL, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH got %08x\n", status); - ok( ReturnLength > 0 || broken(ReturnLength == 0) /* NT4, Win2K */, - "Expected a ReturnLength to show the needed length\n"); + status = pNtQuerySystemInformation( info_class, NULL, 0, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH got %08lx\n", status); + ok( ReturnLength > 0, "got 0 length\n" ); - /* W2K3 and later returns the needed length, the rest returns 0, so we have to loop */ - for (;;) + /* W2K3 and later returns the needed length, the rest returns 0. */ + if (!ReturnLength) { - status = pNtQuerySystemInformation(SystemProcessInformation, spi_buf, SystemInformationLength, &ReturnLength); - - if (status != STATUS_INFO_LENGTH_MISMATCH) break; - - spi_buf = HeapReAlloc(GetProcessHeap(), 0, spi_buf , SystemInformationLength *= 2); + win_skip( "Zero return length, skipping tests." ); + return; } - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + + winetest_push_context( "extended %d", extended ); + + spi_buf = HeapAlloc(GetProcessHeap(), 0, ReturnLength); + status = pNtQuerySystemInformation(info_class, spi_buf, ReturnLength, &ReturnLength); + + /* Sometimes new process or threads appear between the call and increase the size, + * otherwise the previously returned buffer size should be sufficient. */ + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH, + "Expected STATUS_SUCCESS, got %08lx\n", status ); + spi = spi_buf; - /* Get the first NextEntryOffset, from this we can deduce the OS version we're running - * - * W2K/WinXP/W2K3: - * NextEntryOffset for a process is 184 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION) - * NT: - * NextEntryOffset for a process is 136 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION) - * Wine (with every windows version): - * NextEntryOffset for a process is 0 if just this test is running - * NextEntryOffset for a process is 184 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION) + - * ProcessName.MaximumLength - * if more wine processes are running - * - * Note : On windows the first process is in fact the Idle 'process' with a thread for every processor - */ - - pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength); - - is_nt = ( spi->NextEntryOffset - (sbi.NumberOfProcessors * sizeof(SYSTEM_THREAD_INFORMATION)) == 136); - - if (is_nt) win_skip("Windows version is NT, we will skip thread tests\n"); - - /* Check if we have some return values - * - * On windows there will be several processes running (Including the always present Idle and System) - * On wine we only have one (if this test is the only wine process running) - */ - - /* Loop through the processes */ - for (;;) { + DWORD_PTR tid; + DWORD j; + + winetest_push_context( "i %u (%s)", i, debugstr_w(spi->ProcessName.Buffer) ); + i++; last_pid = (DWORD_PTR)spi->UniqueProcessId; + ok( !(last_pid & 3), "Unexpected PID low bits: %p\n", spi->UniqueProcessId ); - disable_success_count - ok( spi->dwThreadCount > 0, "Expected some threads for this process, got 0\n"); + if (last_pid == GetCurrentProcessId()) + current_process_found = TRUE; - /* Loop through the threads, skip NT4 for now */ - - if (!is_nt) + if (extended && is_wow64 && spi->UniqueProcessId) { - DWORD j; - for ( j = 0; j < spi->dwThreadCount; j++) + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + cid.UniqueProcess = spi->UniqueProcessId; + cid.UniqueThread = 0; + status = NtOpenProcess( &handle, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, + "Got unexpected status %#lx, pid %p.\n", status, spi->UniqueProcessId ); + + if (!status) { - k++; - disable_success_count - ok ( spi->ti[j].ClientId.UniqueProcess == spi->UniqueProcessId, - "The owning pid of the thread (%p) doesn't equal the pid (%p) of the process\n", - spi->ti[j].ClientId.UniqueProcess, spi->UniqueProcessId); + ULONG_PTR info; + + status = NtQueryInformationProcess( handle, ProcessWow64Information, &info, sizeof(info), NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + is_process_wow64 = !!info; + NtClose( handle ); } } - if (!spi->NextEntryOffset) break; + for (j = 0; j < spi->dwThreadCount; j++) + { + ti = (SYSTEM_EXTENDED_THREAD_INFORMATION *)((BYTE *)spi->ti + j * thread_info_size); + k++; + ok ( ti->ThreadInfo.ClientId.UniqueProcess == spi->UniqueProcessId, + "The owning pid of the thread (%p) doesn't equal the pid (%p) of the process\n", + ti->ThreadInfo.ClientId.UniqueProcess, spi->UniqueProcessId ); + + tid = (DWORD_PTR)ti->ThreadInfo.ClientId.UniqueThread; + ok( !(tid & 3), "Unexpected TID low bits: %p\n", ti->ThreadInfo.ClientId.UniqueThread ); + + if (extended) + { + todo_wine ok( !!ti->StackBase, "Got NULL StackBase.\n" ); + todo_wine ok( !!ti->StackLimit, "Got NULL StackLimit.\n" ); + ok( !!ti->Win32StartAddress, "Got NULL Win32StartAddress.\n" ); + + cid.UniqueProcess = 0; + cid.UniqueThread = ti->ThreadInfo.ClientId.UniqueThread; + + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + status = NtOpenThread( &handle, THREAD_QUERY_INFORMATION, &attr, &cid ); + if (!status) + { + THREAD_BASIC_INFORMATION tbi; + + status = pNtQueryInformationThread( handle, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + expected_address = tbi.TebBaseAddress; + if (is_wow64 && is_process_wow64) + expected_address = (BYTE *)expected_address - 0x2000; + if (!is_wow64 && !is_process_wow64 && !tbi.TebBaseAddress) + win_skip( "Could not get TebBaseAddress, thread %lu.\n", j ); + else + ok( ti->TebBase == expected_address || (is_wow64 && !expected_address && !!ti->TebBase), + "Got unexpected TebBase %p, expected %p.\n", ti->TebBase, expected_address ); + + NtClose( handle ); + } + } + } + + if (!spi->NextEntryOffset) + { + winetest_pop_context(); + break; + } one_before_last_pid = last_pid; spi = (SYSTEM_PROCESS_INFORMATION_PRIVATE*)((char*)spi + spi->NextEntryOffset); + winetest_pop_context(); } - trace("Total number of running processes : %d\n", i); - if (!is_nt) trace("Total number of running threads : %d\n", k); + ok( current_process_found, "Test process not found.\n" ); + if (winetest_debug > 1) trace("%u processes, %u threads\n", i, k); if (one_before_last_pid == 0) one_before_last_pid = last_pid; HeapFree( GetProcessHeap(), 0, spi_buf); + + for (i = 1; i < 4; ++i) + { + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + cid.UniqueProcess = ULongToHandle(GetCurrentProcessId() + i); + cid.UniqueThread = 0; + + status = NtOpenProcess( &handle, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_SUCCESS || broken( status == STATUS_ACCESS_DENIED ) /* wxppro */, + "NtOpenProcess returned:%lx\n", status ); + if (status != STATUS_SUCCESS) continue; + + status = NtQueryInformationProcess( handle, ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); + ok( status == STATUS_SUCCESS, "NtQueryInformationProcess returned:%lx\n", status ); + ok( pbi.UniqueProcessId == GetCurrentProcessId(), + "Expected pid %p, got %p\n", ULongToHandle(GetCurrentProcessId()), ULongToHandle(pbi.UniqueProcessId) ); + + NtClose( handle ); + } + + for (i = 1; i < 4; ++i) + { + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + cid.UniqueProcess = 0; + cid.UniqueThread = ULongToHandle(GetCurrentThreadId() + i); + + status = NtOpenThread( &handle, THREAD_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_SUCCESS || broken( status == STATUS_ACCESS_DENIED ) /* wxppro */, + "NtOpenThread returned:%lx\n", status ); + if (status != STATUS_SUCCESS) continue; + + status = pNtQueryInformationThread( handle, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); + ok( status == STATUS_SUCCESS, "NtQueryInformationThread returned:%lx\n", status ); + ok( tbi.ClientId.UniqueThread == ULongToHandle(GetCurrentThreadId()), + "Expected tid %p, got %p\n", ULongToHandle(GetCurrentThreadId()), tbi.ClientId.UniqueThread ); + + NtClose( handle ); + } + winetest_pop_context(); } static void test_query_procperf(void) @@ -395,13 +732,13 @@ static void test_query_procperf(void) /* Find out the number of processors */ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); NeededLength = sbi.NumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); sppi = HeapAlloc(GetProcessHeap(), 0, NeededLength); status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); /* Try it for 1 processor */ sppi->KernelTime.QuadPart = 0xdeaddead; @@ -409,9 +746,9 @@ static void test_query_procperf(void) sppi->IdleTime.QuadPart = 0xdeaddead; status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); ok( sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) == ReturnLength, - "Inconsistent length %d\n", ReturnLength); + "Inconsistent length %ld\n", ReturnLength); ok (sppi->KernelTime.QuadPart != 0xdeaddead, "KernelTime unchanged\n"); ok (sppi->UserTime.QuadPart != 0xdeaddead, "UserTime unchanged\n"); ok (sppi->IdleTime.QuadPart != 0xdeaddead, "IdleTime unchanged\n"); @@ -421,8 +758,8 @@ static void test_query_procperf(void) sppi->UserTime.QuadPart = 0xdeaddead; sppi->IdleTime.QuadPart = 0xdeaddead; status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, NeededLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( NeededLength == ReturnLength, "Inconsistent length (%d) <-> (%d)\n", NeededLength, ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( NeededLength == ReturnLength, "Inconsistent length (%ld) <-> (%ld)\n", NeededLength, ReturnLength); ok (sppi->KernelTime.QuadPart != 0xdeaddead, "KernelTime unchanged\n"); ok (sppi->UserTime.QuadPart != 0xdeaddead, "UserTime unchanged\n"); ok (sppi->IdleTime.QuadPart != 0xdeaddead, "IdleTime unchanged\n"); @@ -434,8 +771,8 @@ static void test_query_procperf(void) sppi->IdleTime.QuadPart = 0xdeaddead; status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, NeededLength + 2, &ReturnLength); ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH /* vista */, - "Expected STATUS_SUCCESS or STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( NeededLength == ReturnLength, "Inconsistent length (%d) <-> (%d)\n", NeededLength, ReturnLength); + "Expected STATUS_SUCCESS or STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( NeededLength == ReturnLength, "Inconsistent length (%ld) <-> (%ld)\n", NeededLength, ReturnLength); if (status == STATUS_SUCCESS) { ok (sppi->KernelTime.QuadPart != 0xdeaddead, "KernelTime unchanged\n"); @@ -454,37 +791,64 @@ static void test_query_procperf(void) static void test_query_module(void) { + const RTL_PROCESS_MODULE_INFORMATION_EX *infoex; + RTL_PROCESS_MODULES *info; NTSTATUS status; - ULONG ReturnLength; - ULONG ModuleCount, i; + ULONG size, i; + char *buffer; - ULONG SystemInformationLength = sizeof(RTL_PROCESS_MODULES); - RTL_PROCESS_MODULES* smi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); - RTL_PROCESS_MODULE_INFORMATION* sm; + status = pNtQuerySystemInformation(SystemModuleInformation, NULL, 0, &size); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status); + ok(size > 0, "expected nonzero size\n"); - /* Request the needed length */ - status = pNtQuerySystemInformation(SystemModuleInformation, smi, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( ReturnLength > 0, "Expected a ReturnLength to show the needed length\n"); + info = malloc(size); + status = pNtQuerySystemInformation(SystemModuleInformation, info, size, &size); + ok(!status, "got %#lx\n", status); - SystemInformationLength = ReturnLength; - smi = HeapReAlloc(GetProcessHeap(), 0, smi , SystemInformationLength); - status = pNtQuerySystemInformation(SystemModuleInformation, smi, SystemInformationLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(info->ModulesCount > 0, "Expected some modules to be loaded\n"); - ModuleCount = smi->ModulesCount; - sm = &smi->Modules[0]; - /* our implementation is a stub for now */ - ok( ModuleCount > 0, "Expected some modules to be loaded\n"); - - /* Loop through all the modules/drivers, Wine doesn't get here (yet) */ - for (i = 0; i < ModuleCount ; i++) + for (i = 0; i < info->ModulesCount; i++) { - ok( i == sm->LoadOrderIndex, "LoadOrderIndex (%d) should have matched %u\n", sm->LoadOrderIndex, i); - sm++; + RTL_PROCESS_MODULE_INFORMATION *module = &info->Modules[i]; + + ok(module->LoadOrderIndex == i, "%lu: got index %u\n", i, module->LoadOrderIndex); + ok(module->ImageBaseAddress || is_wow64, "%lu: got NULL address for %s\n", i, module->Name); + ok(module->ImageSize, "%lu: got 0 size\n", i); + ok(module->LoadCount, "%lu: got 0 load count\n", i); } - HeapFree( GetProcessHeap(), 0, smi); + free(info); + + status = pNtQuerySystemInformation(SystemModuleInformationEx, NULL, 0, &size); + if (status == STATUS_INVALID_INFO_CLASS) + { + win_skip("SystemModuleInformationEx is not supported.\n"); + return; + } + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status); + ok(size > 0, "expected nonzero size\n"); + + buffer = malloc(size); + status = pNtQuerySystemInformation(SystemModuleInformationEx, buffer, size, &size); + ok(!status, "got %#lx\n", status); + + infoex = (const void *)buffer; + for (i = 0; infoex->NextOffset; i++) + { + const RTL_PROCESS_MODULE_INFORMATION *module = &infoex->BaseInfo; + + ok(module->LoadOrderIndex == i, "%lu: got index %u\n", i, module->LoadOrderIndex); + ok(module->ImageBaseAddress || is_wow64, "%lu: got NULL address for %s\n", i, module->Name); + ok(module->ImageSize, "%lu: got 0 size\n", i); + ok(module->LoadCount, "%lu: got 0 load count\n", i); + + infoex = (const void *)((const char *)infoex + infoex->NextOffset); + } + ok(((char *)infoex - buffer) + sizeof(infoex->NextOffset) == size, + "got size %lu, null terminator %Iu\n", size, (char *)infoex - buffer); + + free(buffer); + } static void test_query_handle(void) @@ -494,16 +858,19 @@ static void test_query_handle(void) ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION); SYSTEM_HANDLE_INFORMATION* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); HANDLE EventHandle; - BOOL found; + BOOL found, ret; INT i; EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL); - ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() ); + ok( EventHandle != NULL, "CreateEventA failed %lu\n", GetLastError() ); + ret = SetHandleInformation(EventHandle, HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE, + HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE); + ok(ret, "got error %lu\n", GetLastError()); /* Request the needed length : a SystemInformationLength greater than one struct sets ReturnLength */ ReturnLength = 0xdeadbeef; status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); ok( ReturnLength != 0xdeadbeef, "Expected valid ReturnLength\n" ); SystemInformationLength = ReturnLength; @@ -519,11 +886,11 @@ static void test_query_handle(void) memset(shi, 0x55, SystemInformationLength); status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); } - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION, Handle[shi->Count]); ok( ReturnLength == ExpectedLength || broken(ReturnLength == ExpectedLength - sizeof(DWORD)), /* Vista / 2008 */ - "Expected length %u, got %u\n", ExpectedLength, ReturnLength ); - ok( shi->Count > 1, "Expected more than 1 handle, got %u\n", shi->Count ); + "Expected length %lu, got %lu\n", ExpectedLength, ReturnLength ); + ok( shi->Count > 1, "Expected more than 1 handle, got %lu\n", shi->Count ); ok( shi->Handle[1].HandleValue != 0x5555 || broken( shi->Handle[1].HandleValue == 0x5555 ), /* Vista / 2008 */ "Uninitialized second handle\n" ); if (shi->Handle[1].HandleValue == 0x5555) @@ -533,15 +900,22 @@ static void test_query_handle(void) goto done; } - for (i = 0, found = FALSE; i < shi->Count && !found; i++) - found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) && - ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); - ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() ); - - if (!found) - for (i = 0; i < shi->Count; i++) - trace( "%d: handle %x pid %x\n", i, shi->Handle[i].HandleValue, shi->Handle[i].OwnerPid ); + found = FALSE; + for (i = 0; i < shi->Count; i++) + { + if (shi->Handle[i].OwnerPid == GetCurrentProcessId() && + (HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle) + { + ok(shi->Handle[i].HandleFlags == (OBJ_INHERIT | OBJ_PROTECT_CLOSE), + "got attributes %#x\n", shi->Handle[i].HandleFlags); + found = TRUE; + break; + } + } + ok( found, "Expected to find event handle %p (pid %lx) in handle list\n", EventHandle, GetCurrentProcessId() ); + ret = SetHandleInformation(EventHandle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); + ok(ret, "got error %lu\n", GetLastError()); CloseHandle(EventHandle); ReturnLength = 0xdeadbeef; @@ -552,14 +926,14 @@ static void test_query_handle(void) shi = HeapReAlloc(GetProcessHeap(), 0, shi, SystemInformationLength); status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength); } - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); for (i = 0, found = FALSE; i < shi->Count && !found; i++) found = (shi->Handle[i].OwnerPid == GetCurrentProcessId()) && ((HANDLE)(ULONG_PTR)shi->Handle[i].HandleValue == EventHandle); ok( !found, "Unexpectedly found event handle in handle list\n" ); status = pNtQuerySystemInformation(SystemHandleInformation, NULL, SystemInformationLength, &ReturnLength); - ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status ); done: HeapFree( GetProcessHeap(), 0, shi); @@ -567,58 +941,80 @@ done: static void test_query_handle_ex(void) { + SYSTEM_HANDLE_INFORMATION_EX *info = malloc(sizeof(SYSTEM_HANDLE_INFORMATION_EX)); + ULONG size, expect_size; NTSTATUS status; - ULONG ExpectedLength, ReturnLength; - ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION_EX); - SYSTEM_HANDLE_INFORMATION_EX* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength); - HANDLE EventHandle; - BOOL found; - INT i; + unsigned int i; + HANDLE event; + BOOL found, ret; - EventHandle = CreateEventA(NULL, FALSE, FALSE, NULL); - ok( EventHandle != NULL, "CreateEventA failed %u\n", GetLastError() ); + event = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "failed to create event, error %lu\n", GetLastError()); + ret = SetHandleInformation(event, HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE, + HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE); + ok(ret, "got error %lu\n", GetLastError()); - ReturnLength = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( ReturnLength != 0xdeadbeef, "Expected valid ReturnLength\n" ); + size = 0; + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, info, sizeof(SYSTEM_HANDLE_INFORMATION_EX), &size); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status); + ok(size > sizeof(SYSTEM_HANDLE_INFORMATION_EX), "got size %lu\n", size); - SystemInformationLength = ReturnLength; - shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength); - memset(shi, 0x55, SystemInformationLength); - - ReturnLength = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); - ExpectedLength = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles[shi->NumberOfHandles]); - ok( ReturnLength == ExpectedLength, "Expected length %u, got %u\n", ExpectedLength, ReturnLength ); - ok( shi->NumberOfHandles > 1, "Expected more than 1 handle, got %u\n", (DWORD)shi->NumberOfHandles ); - - for (i = 0, found = FALSE; i < shi->NumberOfHandles && !found; i++) - found = (shi->Handles[i].UniqueProcessId == GetCurrentProcessId()) && - ((HANDLE)(ULONG_PTR)shi->Handles[i].HandleValue == EventHandle); - ok( found, "Expected to find event handle %p (pid %x) in handle list\n", EventHandle, GetCurrentProcessId() ); - - if (!found) + while (status == STATUS_INFO_LENGTH_MISMATCH) { - for (i = 0; i < shi->NumberOfHandles; i++) - trace( "%d: handle %x pid %x\n", i, (DWORD)shi->Handles[i].HandleValue, (DWORD)shi->Handles[i].UniqueProcessId ); + info = realloc(info, size); + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, info, size, &size); } + ok(!status, "got %#lx\n", status); + expect_size = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles[info->NumberOfHandles]); + ok(size == expect_size, "expected size %lu, got %lu\n", expect_size, size); + ok(info->NumberOfHandles > 1, "got %Iu handles\n", info->NumberOfHandles); - CloseHandle(EventHandle); + found = FALSE; + for (i = 0; i < info->NumberOfHandles; ++i) + { + if (info->Handles[i].UniqueProcessId == GetCurrentProcessId() + && (HANDLE)info->Handles[i].HandleValue == event) + { + ok(info->Handles[i].HandleAttributes == (OBJ_INHERIT | OBJ_PROTECT_CLOSE), + "got flags %#lx\n", info->Handles[i].HandleAttributes); + ok(info->Handles[i].GrantedAccess == EVENT_ALL_ACCESS, "got access %#lx\n", info->Handles[i].GrantedAccess); + found = TRUE; + } + ok(!info->Handles[i].CreatorBackTraceIndex, "got backtrace index %u\n", info->Handles[i].CreatorBackTraceIndex); + } + ok(found, "event handle not found\n"); - ReturnLength = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemExtendedHandleInformation, shi, SystemInformationLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); - for (i = 0, found = FALSE; i < shi->NumberOfHandles && !found; i++) - found = (shi->Handles[i].UniqueProcessId == GetCurrentProcessId()) && - ((HANDLE)(ULONG_PTR)shi->Handles[i].HandleValue == EventHandle); - ok( !found, "Unexpectedly found event handle in handle list\n" ); + ret = SetHandleInformation(event, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); + ok(ret, "got error %lu\n", GetLastError()); + CloseHandle(event); - status = pNtQuerySystemInformation(SystemExtendedHandleInformation, NULL, SystemInformationLength, &ReturnLength); - ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status ); + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, info, size, &size); + while (status == STATUS_INFO_LENGTH_MISMATCH) + { + info = realloc(info, size); + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, info, size, &size); + } + ok(!status, "got %#lx\n", status); + expect_size = FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles[info->NumberOfHandles]); + ok(size == expect_size, "expected size %lu, got %lu\n", expect_size, size); + ok(info->NumberOfHandles > 1, "got %Iu handles\n", info->NumberOfHandles); - HeapFree( GetProcessHeap(), 0, shi); + found = FALSE; + for (i = 0; i < info->NumberOfHandles; ++i) + { + if (info->Handles[i].UniqueProcessId == GetCurrentProcessId() + && (HANDLE)info->Handles[i].HandleValue == event) + { + found = TRUE; + break; + } + } + ok(!found, "event handle found\n"); + + status = pNtQuerySystemInformation(SystemExtendedHandleInformation, NULL, sizeof(SYSTEM_HANDLE_INFORMATION_EX), &size); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status ); + + free(info); } static void test_query_cache(void) @@ -630,14 +1026,13 @@ static void test_query_cache(void) ULONG expected; INT i; - /* the large SYSTEM_CACHE_INFORMATION on WIN64 is not documented */ expected = sizeof(SYSTEM_CACHE_INFORMATION); for (i = sizeof(buffer); i>= expected; i--) { ReturnLength = 0xdeadbeef; status = pNtQuerySystemInformation(SystemFileCacheInformation, sci, i, &ReturnLength); ok(!status && (ReturnLength == expected), - "%d: got 0x%x and %u (expected STATUS_SUCCESS and %u)\n", i, status, ReturnLength, expected); + "%d: got 0x%lx and %lu (expected STATUS_SUCCESS and %lu)\n", i, status, ReturnLength, expected); } /* buffer too small for the full result. @@ -645,13 +1040,13 @@ static void test_query_cache(void) status = pNtQuerySystemInformation(SystemFileCacheInformation, sci, i, &ReturnLength); if (!status) { - expected = offsetof(SYSTEM_CACHE_INFORMATION, MinimumWorkingSet); + expected = 3 * sizeof(ULONG); for (; i>= expected; i--) { ReturnLength = 0xdeadbeef; status = pNtQuerySystemInformation(SystemFileCacheInformation, sci, i, &ReturnLength); ok(!status && (ReturnLength == expected), - "%d: got 0x%x and %u (expected STATUS_SUCCESS and %u)\n", i, status, ReturnLength, expected); + "%d: got 0x%lx and %lu (expected STATUS_SUCCESS and %lu)\n", i, status, ReturnLength, expected); } } @@ -660,7 +1055,7 @@ static void test_query_cache(void) status = pNtQuerySystemInformation(SystemFileCacheInformation, sci, i, &ReturnLength); ok( status == STATUS_INFO_LENGTH_MISMATCH && ((ReturnLength == expected) || broken(!ReturnLength) || broken(ReturnLength == 0xfffffff0)), - "%d: got 0x%x and %u (expected STATUS_INFO_LENGTH_MISMATCH and %u)\n", i, status, ReturnLength, expected); + "%d: got 0x%lx and %lu (expected STATUS_INFO_LENGTH_MISMATCH and %lu)\n", i, status, ReturnLength, expected); if (0) { /* this crashes on some vista / win7 machines */ @@ -668,7 +1063,7 @@ static void test_query_cache(void) status = pNtQuerySystemInformation(SystemFileCacheInformation, sci, 0, &ReturnLength); ok( status == STATUS_INFO_LENGTH_MISMATCH && ((ReturnLength == expected) || broken(!ReturnLength) || broken(ReturnLength == 0xfffffff0)), - "0: got 0x%x and %u (expected STATUS_INFO_LENGTH_MISMATCH and %u)\n", status, ReturnLength, expected); + "0: got 0x%lx and %lu (expected STATUS_INFO_LENGTH_MISMATCH and %lu)\n", status, ReturnLength, expected); } } @@ -682,17 +1077,18 @@ static void test_query_interrupt(void) /* Find out the number of processors */ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); NeededLength = sbi.NumberOfProcessors * sizeof(SYSTEM_INTERRUPT_INFORMATION); sii = HeapAlloc(GetProcessHeap(), 0, NeededLength); status = pNtQuerySystemInformation(SystemInterruptInformation, sii, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok(ReturnLength == NeededLength, "got %lu\n", ReturnLength); /* Try it for all processors */ status = pNtQuerySystemInformation(SystemInterruptInformation, sii, NeededLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); /* Windows XP and W2K3 (and others?) always return 0 for the ReturnLength * No test added for this as it's highly unlikely that an app depends on this @@ -701,22 +1097,81 @@ static void test_query_interrupt(void) HeapFree( GetProcessHeap(), 0, sii); } +static void test_time_adjustment(void) +{ + SYSTEM_TIME_ADJUSTMENT_QUERY query; + SYSTEM_TIME_ADJUSTMENT adjust; + NTSTATUS status; + ULONG len; + + memset( &query, 0xcc, sizeof(query) ); + status = pNtQuerySystemInformation( SystemTimeAdjustmentInformation, &query, sizeof(query), &len ); + ok( status == STATUS_SUCCESS, "got %08lx\n", status ); + ok( len == sizeof(query) || broken(!len) /* winxp */, "wrong len %lu\n", len ); + ok( query.TimeAdjustmentDisabled == TRUE || query.TimeAdjustmentDisabled == FALSE, + "wrong value %x\n", query.TimeAdjustmentDisabled ); + + status = pNtQuerySystemInformation( SystemTimeAdjustmentInformation, &query, sizeof(query)-1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status ); + ok( len == sizeof(query) || broken(!len) /* winxp */, "wrong len %lu\n", len ); + + status = pNtQuerySystemInformation( SystemTimeAdjustmentInformation, &query, sizeof(query)+1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status ); + ok( len == sizeof(query) || broken(!len) /* winxp */, "wrong len %lu\n", len ); + + adjust.TimeAdjustment = query.TimeAdjustment; + adjust.TimeAdjustmentDisabled = query.TimeAdjustmentDisabled; + status = pNtSetSystemInformation( SystemTimeAdjustmentInformation, &adjust, sizeof(adjust) ); + ok( status == STATUS_SUCCESS || status == STATUS_PRIVILEGE_NOT_HELD, "got %08lx\n", status ); + status = pNtSetSystemInformation( SystemTimeAdjustmentInformation, &adjust, sizeof(adjust)-1 ); + todo_wine + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status ); + status = pNtSetSystemInformation( SystemTimeAdjustmentInformation, &adjust, sizeof(adjust)+1 ); + todo_wine + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status ); +} + static void test_query_kerndebug(void) { NTSTATUS status; ULONG ReturnLength; - SYSTEM_KERNEL_DEBUGGER_INFORMATION skdi; + /* some Windows version expect alignment */ + SYSTEM_KERNEL_DEBUGGER_INFORMATION_EX DECLSPEC_ALIGN(4) skdi_ex; + SYSTEM_KERNEL_DEBUGGER_INFORMATION DECLSPEC_ALIGN(4) skdi; status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, sizeof(skdi), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(skdi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(skdi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, sizeof(skdi) + 2, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(skdi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(skdi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); + + status = pNtQuerySystemInformation(SystemKernelDebuggerInformationEx, &skdi_ex, 0, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH + || status == STATUS_NOT_IMPLEMENTED /* before win7 */ + || status == STATUS_INVALID_INFO_CLASS /* wow64 on Win10 */, + "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + + if (status != STATUS_INFO_LENGTH_MISMATCH) + { + win_skip( "NtQuerySystemInformation(SystemKernelDebuggerInformationEx) is not implemented.\n" ); + } + else + { + status = pNtQuerySystemInformation(SystemKernelDebuggerInformationEx, &skdi_ex, + sizeof(skdi_ex), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(skdi_ex) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); + + status = pNtQuerySystemInformation(SystemKernelDebuggerInformationEx, &skdi_ex, + sizeof(skdi_ex) + 2, &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(skdi_ex) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); + } } static void test_query_regquota(void) @@ -726,15 +1181,15 @@ static void test_query_regquota(void) SYSTEM_REGISTRY_QUOTA_INFORMATION srqi; status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, 0, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, sizeof(srqi), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(srqi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(srqi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, sizeof(srqi) + 2, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(srqi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(srqi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); } static void test_query_logicalproc(void) @@ -747,22 +1202,17 @@ static void test_query_logicalproc(void) GetSystemInfo(&si); status = pNtQuerySystemInformation(SystemLogicalProcessorInformation, NULL, 0, &len); - if(status == STATUS_INVALID_INFO_CLASS) + if (status == STATUS_INVALID_INFO_CLASS) /* wow64 win8+, arm64 */ { - win_skip("SystemLogicalProcessorInformation is not supported\n"); + skip("SystemLogicalProcessorInformation is not supported\n"); return; } - if(status == STATUS_NOT_IMPLEMENTED) - { - todo_wine ok(0, "SystemLogicalProcessorInformation is not implemented\n"); - return; - } - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok(len%sizeof(*slpi) == 0, "Incorrect length %d\n", len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok(len%sizeof(*slpi) == 0, "Incorrect length %ld\n", len); slpi = HeapAlloc(GetProcessHeap(), 0, len); status = pNtQuerySystemInformation(SystemLogicalProcessorInformation, slpi, len, &len); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); proc_no = 0; for(i=0; i 0, "No processors were found\n"); if(si.dwNumberOfProcessors <= 32) - ok(proc_no == si.dwNumberOfProcessors, "Incorrect number of logical processors: %d, expected %d\n", + ok(proc_no == si.dwNumberOfProcessors, "Incorrect number of logical processors: %ld, expected %ld\n", proc_no, si.dwNumberOfProcessors); HeapFree(GetProcessHeap(), 0, slpi); @@ -786,106 +1236,415 @@ static void test_query_logicalproc(void) static void test_query_logicalprocex(void) { - SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infoex, *infoex2; - DWORD relationship, len2, len; + static const char * const names[] = { "Core", "NumaNode", "Cache", "Package", "Group", "Die", "NumaNodeEx", "Module" }; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *infoex, *infoex_public, *infoex_core, *infoex_numa, *infoex_cache, + *infoex_package, *infoex_group, *infoex_die, *infoex_numa_ex, + *infoex_module, *ex; + DWORD relationship, len, len_public, len_core, len_numa, len_cache, len_package, len_group, len_die, len_numa_ex, + len_module, len_union, ret_len; + unsigned int i, j; NTSTATUS status; BOOL ret; if (!pNtQuerySystemInformationEx) return; - len = 0; - relationship = RelationProcessorCore; - status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status); - ok(len > 0, "got %u\n", len); - len = 0; relationship = RelationAll; status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08x\n", status); - ok(len > 0, "got %u\n", len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len > 0, "got %lu\n", len); - len2 = 0; - ret = pGetLogicalProcessorInformationEx(RelationAll, NULL, &len2); - ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, error %d\n", ret, GetLastError()); - ok(len == len2, "got %u, expected %u\n", len2, len); + len_core = 0; + relationship = RelationProcessorCore; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_core); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len_core > 0, "got %lu\n", len_core); - if (len && len == len2) { - int j, i; + len_numa = 0; + relationship = RelationNumaNode; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_numa); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len_numa > 0, "got %lu\n", len_numa); - infoex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); - infoex2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); + len_cache = 0; + relationship = RelationCache; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_cache); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len_cache > 0, "got %lu\n", len_cache); - status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex, len, &len); - ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + len_package = 0; + relationship = RelationProcessorPackage; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_package); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len_package > 0, "got %lu\n", len_package); - ret = pGetLogicalProcessorInformationEx(RelationAll, infoex2, &len2); - ok(ret, "got %d, error %d\n", ret, GetLastError()); - ok(!memcmp(infoex, infoex2, len), "returned info data mismatch\n"); + len_group = 0; + relationship = RelationGroup; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_group); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got 0x%08lx\n", status); + ok(len_group > 0, "got %lu\n", len_group); - for(i = 0; status == STATUS_SUCCESS && i < len; ){ - SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *ex = (void*)(((char *)infoex) + i); + relationship = RelationProcessorDie; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_die); + todo_wine ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_UNSUCCESSFUL || broken(status == STATUS_SUCCESS), + "got 0x%08lx\n", status); - ok(ex->Relationship >= RelationProcessorCore && ex->Relationship <= RelationGroup, - "Got invalid relationship value: 0x%x\n", ex->Relationship); - if (!ex->Size) - { - ok(0, "got infoex[%u].Size=0\n", i); - break; - } + len_numa_ex = 0; + relationship = RelationNumaNodeEx; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_numa_ex); + todo_wine ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_UNSUCCESSFUL || broken(status == STATUS_SUCCESS), + "got 0x%08lx\n", status); - trace("infoex[%u].Size: %u\n", i, ex->Size); - switch(ex->Relationship){ - case RelationProcessorCore: - case RelationProcessorPackage: - trace("infoex[%u].Relationship: 0x%x (Core == 0x0 or Package == 0x3)\n", i, ex->Relationship); - trace("infoex[%u].Processor.Flags: 0x%x\n", i, ex->Processor.Flags); -#ifndef __REACTOS__ - trace("infoex[%u].Processor.EfficiencyClass: 0x%x\n", i, ex->Processor.EfficiencyClass); -#endif - trace("infoex[%u].Processor.GroupCount: 0x%x\n", i, ex->Processor.GroupCount); - for(j = 0; j < ex->Processor.GroupCount; ++j){ - trace("infoex[%u].Processor.GroupMask[%u].Mask: 0x%lx\n", i, j, ex->Processor.GroupMask[j].Mask); - trace("infoex[%u].Processor.GroupMask[%u].Group: 0x%x\n", i, j, ex->Processor.GroupMask[j].Group); - } - break; - case RelationNumaNode: - trace("infoex[%u].Relationship: 0x%x (NumaNode)\n", i, ex->Relationship); - trace("infoex[%u].NumaNode.NodeNumber: 0x%x\n", i, ex->NumaNode.NodeNumber); - trace("infoex[%u].NumaNode.GroupMask.Mask: 0x%lx\n", i, ex->NumaNode.GroupMask.Mask); - trace("infoex[%u].NumaNode.GroupMask.Group: 0x%x\n", i, ex->NumaNode.GroupMask.Group); - break; - case RelationCache: - trace("infoex[%u].Relationship: 0x%x (Cache)\n", i, ex->Relationship); - trace("infoex[%u].Cache.Level: 0x%x\n", i, ex->Cache.Level); - trace("infoex[%u].Cache.Associativity: 0x%x\n", i, ex->Cache.Associativity); - trace("infoex[%u].Cache.LineSize: 0x%x\n", i, ex->Cache.LineSize); - trace("infoex[%u].Cache.CacheSize: 0x%x\n", i, ex->Cache.CacheSize); - trace("infoex[%u].Cache.Type: 0x%x\n", i, ex->Cache.Type); - trace("infoex[%u].Cache.GroupMask.Mask: 0x%lx\n", i, ex->Cache.GroupMask.Mask); - trace("infoex[%u].Cache.GroupMask.Group: 0x%x\n", i, ex->Cache.GroupMask.Group); - break; - case RelationGroup: - trace("infoex[%u].Relationship: 0x%x (Group)\n", i, ex->Relationship); - trace("infoex[%u].Group.MaximumGroupCount: 0x%x\n", i, ex->Group.MaximumGroupCount); - trace("infoex[%u].Group.ActiveGroupCount: 0x%x\n", i, ex->Group.ActiveGroupCount); - for(j = 0; j < ex->Group.ActiveGroupCount; ++j){ - trace("infoex[%u].Group.GroupInfo[%u].MaximumProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].MaximumProcessorCount); - trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorCount); - trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorMask: 0x%lx\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorMask); - } - break; - default: - break; - } + len_module = 0; + relationship = RelationProcessorModule; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), NULL, 0, &len_module); + todo_wine ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_UNSUCCESSFUL || broken(status == STATUS_SUCCESS), + "got 0x%08lx\n", status); + len_public = 0; + ret = pGetLogicalProcessorInformationEx(RelationAll, NULL, &len_public); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "got %d, error %ld\n", ret, GetLastError()); + ok(len == len_public, "got %lu, expected %lu\n", len_public, len); + + infoex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); + infoex_public = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_public); + infoex_core = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_core); + infoex_numa = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_numa); + infoex_cache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_cache); + infoex_package = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_package); + infoex_group = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_group); + infoex_die = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_die); + infoex_numa_ex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_numa_ex); + infoex_module = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len_module); + + relationship = RelationAll; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex, len, &ret_len); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + ok(ret_len == len, "got %08lx expected %08lx\n", ret_len, len); + + ret = pGetLogicalProcessorInformationEx(RelationAll, infoex_public, &len_public); + ok(ret, "got %d, error %ld\n", ret, GetLastError()); + ok(!memcmp(infoex, infoex_public, len), "returned info data mismatch\n"); + + /* Test for RelationAll. */ + for (i = 0; status == STATUS_SUCCESS && i < len; ) + { + ex = (void *)(((char *)infoex) + i); + ok(ex->Size, "%u: got size 0\n", i); + + if (winetest_debug <= 1) + { i += ex->Size; + continue; } - HeapFree(GetProcessHeap(), 0, infoex); - HeapFree(GetProcessHeap(), 0, infoex2); + trace("infoex[%u].Size: %lu\n", i, ex->Size); + switch (ex->Relationship) + { + case RelationProcessorCore: + case RelationProcessorPackage: + case RelationProcessorDie: + case RelationProcessorModule: + trace("infoex[%u].Relationship: 0x%x (%s)\n", i, ex->Relationship, names[ex->Relationship]); + trace("infoex[%u].Processor.Flags: 0x%x\n", i, ex->Processor.Flags); + trace("infoex[%u].Processor.EfficiencyClass: 0x%x\n", i, ex->Processor.EfficiencyClass); + trace("infoex[%u].Processor.GroupCount: 0x%x\n", i, ex->Processor.GroupCount); + for (j = 0; j < ex->Processor.GroupCount; ++j) + { + trace("infoex[%u].Processor.GroupMask[%u].Mask: 0x%Ix\n", i, j, ex->Processor.GroupMask[j].Mask); + trace("infoex[%u].Processor.GroupMask[%u].Group: 0x%x\n", i, j, ex->Processor.GroupMask[j].Group); + } + break; + case RelationNumaNode: + case RelationNumaNodeEx: + trace("infoex[%u].Relationship: 0x%x (%s)\n", i, ex->Relationship, names[ex->Relationship]); + trace("infoex[%u].NumaNode.NodeNumber: 0x%lx\n", i, ex->NumaNode.NodeNumber); + trace("infoex[%u].NumaNode.GroupMask.Mask: 0x%Ix\n", i, ex->NumaNode.GroupMask.Mask); + trace("infoex[%u].NumaNode.GroupMask.Group: 0x%x\n", i, ex->NumaNode.GroupMask.Group); + break; + case RelationCache: + trace("infoex[%u].Relationship: 0x%x (Cache)\n", i, ex->Relationship); + trace("infoex[%u].Cache.Level: 0x%x\n", i, ex->Cache.Level); + trace("infoex[%u].Cache.Associativity: 0x%x\n", i, ex->Cache.Associativity); + trace("infoex[%u].Cache.LineSize: 0x%x\n", i, ex->Cache.LineSize); + trace("infoex[%u].Cache.CacheSize: 0x%lx\n", i, ex->Cache.CacheSize); + trace("infoex[%u].Cache.Type: 0x%x\n", i, ex->Cache.Type); + trace("infoex[%u].Cache.GroupMask.Mask: 0x%Ix\n", i, ex->Cache.GroupMask.Mask); + trace("infoex[%u].Cache.GroupMask.Group: 0x%x\n", i, ex->Cache.GroupMask.Group); + break; + case RelationGroup: + trace("infoex[%u].Relationship: 0x%x (Group)\n", i, ex->Relationship); + trace("infoex[%u].Group.MaximumGroupCount: 0x%x\n", i, ex->Group.MaximumGroupCount); + trace("infoex[%u].Group.ActiveGroupCount: 0x%x\n", i, ex->Group.ActiveGroupCount); + for (j = 0; j < ex->Group.ActiveGroupCount; ++j) + { + trace("infoex[%u].Group.GroupInfo[%u].MaximumProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].MaximumProcessorCount); + trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorCount: 0x%x\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorCount); + trace("infoex[%u].Group.GroupInfo[%u].ActiveProcessorMask: 0x%Ix\n", i, j, ex->Group.GroupInfo[j].ActiveProcessorMask); + } + break; + default: + ok(0, "Got invalid relationship value: 0x%x\n", ex->Relationship); + break; + } + + i += ex->Size; } + + /* Test Relationship filtering. */ + + relationship = RelationProcessorCore; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex_core, len_core, &len_core); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + + for (i = 0; status == STATUS_SUCCESS && i < len_core;) + { + ex = (void *)(((char*)infoex_core) + i); + ok(ex->Size, "%u: got size 0\n", i); + ok(ex->Relationship == RelationProcessorCore, "%u: got relationship %#x\n", i, ex->Relationship); + i += ex->Size; + } + + relationship = RelationNumaNode; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex_numa, len_numa, &len_numa); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + + for (i = 0; status == STATUS_SUCCESS && i < len_numa;) + { + ex = (void *)(((char*)infoex_numa) + i); + ok(ex->Size, "%u: got size 0\n", i); + ok(ex->Relationship == RelationNumaNode, "%u: got relationship %#x\n", i, ex->Relationship); + i += ex->Size; + } + + relationship = RelationCache; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex_cache, len_cache, &len_cache); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + + for (i = 0; status == STATUS_SUCCESS && i < len_cache;) + { + ex = (void *)(((char*)infoex_cache) + i); + ok(ex->Size, "%u: got size 0\n", i); + ok(ex->Relationship == RelationCache, "%u: got relationship %#x\n", i, ex->Relationship); + i += ex->Size; + } + + relationship = RelationProcessorPackage; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex_package, len_package, &len_package); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + + for (i = 0; status == STATUS_SUCCESS && i < len_package;) + { + ex = (void *)(((char*)infoex_package) + i); + ok(ex->Size, "%u: got size 0\n", i); + ok(ex->Relationship == RelationProcessorPackage, "%u: got relationship %#x\n", i, ex->Relationship); + i += ex->Size; + } + + relationship = RelationGroup; + status = pNtQuerySystemInformationEx(SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship), infoex_group, len_group, &len_group); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + + for (i = 0; status == STATUS_SUCCESS && i < len_group;) + { + ex = (void *)(((char *)infoex_group) + i); + ok(ex->Size, "%u: got size 0\n", i); + ok(ex->Relationship == RelationGroup, "%u: got relationship %#x\n", i, ex->Relationship); + i += ex->Size; + } + + len_union = len_core + len_numa + len_cache + len_package + len_group + len_module; + ok(len == len_union, "Expected %lu, got %lu\n", len, len_union); + + HeapFree(GetProcessHeap(), 0, infoex); + HeapFree(GetProcessHeap(), 0, infoex_public); + HeapFree(GetProcessHeap(), 0, infoex_core); + HeapFree(GetProcessHeap(), 0, infoex_numa); + HeapFree(GetProcessHeap(), 0, infoex_cache); + HeapFree(GetProcessHeap(), 0, infoex_package); + HeapFree(GetProcessHeap(), 0, infoex_group); + HeapFree(GetProcessHeap(), 0, infoex_die); + HeapFree(GetProcessHeap(), 0, infoex_numa_ex); + HeapFree(GetProcessHeap(), 0, infoex_module); +} + +static void test_query_cpusetinfo(void) +{ + SYSTEM_CPU_SET_INFORMATION *info; + unsigned int i, cpu_count; + ULONG len, expected_len; + NTSTATUS status; + SYSTEM_INFO si; + HANDLE process; + + if (!pNtQuerySystemInformationEx) + return; + + GetSystemInfo(&si); + cpu_count = si.dwNumberOfProcessors; + expected_len = cpu_count * sizeof(*info); + + process = GetCurrentProcess(); + + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len); + if (status == STATUS_INVALID_INFO_CLASS) + { + win_skip("SystemCpuSetInformation is not supported\n"); + return; + } + + ok(status == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#lx.\n", status); + ok(len == expected_len, "Got unexpected length %lu.\n", len); + + len = 0xdeadbeef; + status = pNtQuerySystemInformation(SystemCpuSetInformation, NULL, 0, &len); + ok(status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_INFO_CLASS, + "Got unexpected status %#lx.\n", status); + ok(len == 0xdeadbeef, "Got unexpected len %lu.\n", len); + + len = 0xdeadbeef; + process = (HANDLE)0xdeadbeef; + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + ok(len == 0xdeadbeef, "Got unexpected length %lu.\n", len); + + len = 0xdeadbeef; + process = NULL; + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, 4 * sizeof(process), NULL, 0, &len); + ok((status == STATUS_INVALID_PARAMETER && len == 0xdeadbeef) + || (status == STATUS_BUFFER_TOO_SMALL && len == expected_len), + "Got unexpected status %#lx, length %lu.\n", status, len); + + len = 0xdeadbeef; + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, NULL, sizeof(process), NULL, 0, &len); + ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); + ok(len == 0xdeadbeef, "Got unexpected length %lu.\n", len); + + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, 0, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#lx.\n", status); + ok(len == expected_len, "Got unexpected length %lu.\n", len); + + len = 0xdeadbeef; + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), NULL, + expected_len, &len); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); + ok(len == 0xdeadbeef, "Got unexpected length %lu.\n", len); + + info = malloc(expected_len); + len = 0; + status = pNtQuerySystemInformationEx(SystemCpuSetInformation, &process, sizeof(process), info, expected_len, &len); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(len == expected_len, "Got unexpected length %lu.\n", len); + + for (i = 0; i < cpu_count; ++i) + { + SYSTEM_CPU_SET_INFORMATION *d = &info[i]; + + ok(d->Size == sizeof(*d), "Got unexpected size %lu, i %u.\n", d->Size, i); + ok(d->Type == CpuSetInformation, "Got unexpected type %u, i %u.\n", d->Type, i); + ok(d->CpuSet.Id == 0x100 + i, "Got unexpected Id %#lx, i %u.\n", d->CpuSet.Id, i); + ok(!d->CpuSet.Group, "Got unexpected Group %u, i %u.\n", d->CpuSet.Group, i); + ok(d->CpuSet.LogicalProcessorIndex == i, "Got unexpected LogicalProcessorIndex %u, i %u.\n", + d->CpuSet.LogicalProcessorIndex, i); + ok(!d->CpuSet.AllFlags, "Got unexpected AllFlags %#x, i %u.\n", d->CpuSet.AllFlags, i); + } + free(info); +} + +static void test_query_firmware(void) +{ + static const ULONG min_sfti_len = FIELD_OFFSET(SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer); + ULONG len1, len2; + NTSTATUS status; + SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti; + + sfti = HeapAlloc(GetProcessHeap(), 0, sizeof(*sfti)); + ok(!!sfti, "Failed to allocate memory\n"); + + sfti->ProviderSignature = 0; + sfti->Action = 0; + sfti->TableID = 0; + + status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, min_sfti_len - 1, &len1); + ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* xp */, + "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + if (len1 == 0) /* xp, 2003 */ + { + win_skip("SystemFirmwareTableInformation is not available\n"); + HeapFree(GetProcessHeap(), 0, sfti); + return; + } + ok(len1 == min_sfti_len, "Expected length %lu, got %lu\n", min_sfti_len, len1); + + status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, min_sfti_len, &len1); + ok(status == STATUS_NOT_IMPLEMENTED, "Expected STATUS_NOT_IMPLEMENTED, got %08lx\n", status); + ok(len1 == 0, "Expected length 0, got %lu\n", len1); + + sfti->ProviderSignature = RSMB; + sfti->Action = SystemFirmwareTable_Get; + + status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, min_sfti_len, &len1); + ok(status == STATUS_BUFFER_TOO_SMALL, "Expected STATUS_BUFFER_TOO_SMALL, got %08lx\n", status); + ok(len1 >= min_sfti_len, "Expected length >= %lu, got %lu\n", min_sfti_len, len1); + ok(sfti->TableBufferLength == len1 - min_sfti_len, + "Expected length %lu, got %lu\n", len1 - min_sfti_len, sfti->TableBufferLength); + + sfti = HeapReAlloc(GetProcessHeap(), 0, sfti, len1); + ok(!!sfti, "Failed to allocate memory\n"); + + status = pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, len1, &len2); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok(len2 == len1, "Expected length %lu, got %lu\n", len1, len2); + ok(sfti->TableBufferLength == len1 - min_sfti_len, + "Expected length %lu, got %lu\n", len1 - min_sfti_len, sfti->TableBufferLength); + + HeapFree(GetProcessHeap(), 0, sfti); +} + +static void test_query_battery(void) +{ + SYSTEM_BATTERY_STATE bs; + NTSTATUS status; + DWORD time_left; + + memset(&bs, 0x23, sizeof(bs)); + status = NtPowerInformation(SystemBatteryState, NULL, 0, &bs, sizeof(bs)); + if (status == STATUS_NOT_IMPLEMENTED) + { + skip("SystemBatteryState not implemented\n"); + return; + } + ok(status == STATUS_SUCCESS, "expected success\n"); + + if (winetest_debug > 1) + { + trace("Battery state:\n"); + trace("AcOnLine : %u\n", bs.AcOnLine); + trace("BatteryPresent : %u\n", bs.BatteryPresent); + trace("Charging : %u\n", bs.Charging); + trace("Discharging : %u\n", bs.Discharging); + trace("Tag : %u\n", bs.Tag); + trace("MaxCapacity : %lu\n", bs.MaxCapacity); + trace("RemainingCapacity : %lu\n", bs.RemainingCapacity); + trace("Rate : %ld\n", (LONG)bs.Rate); + trace("EstimatedTime : %lu\n", bs.EstimatedTime); + trace("DefaultAlert1 : %lu\n", bs.DefaultAlert1); + trace("DefaultAlert2 : %lu\n", bs.DefaultAlert2); + } + + ok(bs.MaxCapacity >= bs.RemainingCapacity, + "expected MaxCapacity %lu to be greater than or equal to RemainingCapacity %lu\n", + bs.MaxCapacity, bs.RemainingCapacity); + + if (!bs.BatteryPresent) + time_left = 0; + else if (!bs.Charging && (LONG)bs.Rate < 0) + time_left = 3600 * bs.RemainingCapacity / -(LONG)bs.Rate; + else + time_left = ~0u; + ok(bs.EstimatedTime == time_left, + "expected %lu minutes remaining got %lu minutes\n", time_left, bs.EstimatedTime); } static void test_query_processor_power_info(void) @@ -927,12 +1686,12 @@ static void test_query_processor_power_info(void) "Expected all but the last record to be overwritten.\n"); status = pNtPowerInformation(ProcessorInformation, 0, 0, 0, size); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); for(i = 0; i < si.dwNumberOfProcessors; i++) ppi[i].Number = 0xDEADBEEF; status = pNtPowerInformation(ProcessorInformation, 0, 0, ppi, sizeof(PROCESSOR_POWER_INFORMATION) - 1); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); for(i = 0; i < si.dwNumberOfProcessors; i++) if (ppi[i].Number != 0xDEADBEEF) break; ok( i == si.dwNumberOfProcessors, "Expected untouched buffer\n"); @@ -941,13 +1700,13 @@ static void test_query_processor_power_info(void) { /* picky version found on newer Windows like Win7 */ ok( ppi[1].Number == 0xDEADBEEF, "Expected untouched buffer.\n"); - ok( status == STATUS_BUFFER_TOO_SMALL, "Expected STATUS_BUFFER_TOO_SMALL, got %08x\n", status); + ok( status == STATUS_BUFFER_TOO_SMALL, "Expected STATUS_BUFFER_TOO_SMALL, got %08lx\n", status); status = pNtPowerInformation(ProcessorInformation, 0, 0, 0, size); - ok( status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, "Got %08x\n", status); + ok( status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, "Got %08lx\n", status); status = pNtPowerInformation(ProcessorInformation, 0, 0, ppi, 0); - ok( status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INVALID_PARAMETER, "Got %08x\n", status); + ok( status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INVALID_PARAMETER, "Got %08lx\n", status); } } else @@ -956,7 +1715,7 @@ static void test_query_processor_power_info(void) } status = pNtPowerInformation(ProcessorInformation, 0, 0, ppi, size); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); HeapFree(GetProcessHeap(), 0, ppi); } @@ -970,40 +1729,40 @@ static void test_query_process_wow64(void) memset(&dummy, 0xcc, sizeof(dummy)); /* Do not give a handle and buffer */ - status = pNtQueryInformationProcess(NULL, ProcessWow64Information, NULL, 0, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessWow64Information, NULL, 0, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); /* Use a correct info class and buffer size, but still no handle and buffer */ - status = pNtQueryInformationProcess(NULL, ProcessWow64Information, NULL, sizeof(ULONG_PTR), NULL); + status = NtQueryInformationProcess(NULL, ProcessWow64Information, NULL, sizeof(ULONG_PTR), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE, got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE, got %08lx\n", status); /* Use a correct info class, buffer size and handle, but no buffer */ - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, NULL, sizeof(ULONG_PTR), NULL); - ok( status == STATUS_ACCESS_VIOLATION , "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, NULL, sizeof(ULONG_PTR), NULL); + ok( status == STATUS_ACCESS_VIOLATION , "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); /* Use a correct info class, buffer and buffer size, but no handle */ pbi[0] = pbi[1] = dummy; - status = pNtQueryInformationProcess(NULL, ProcessWow64Information, pbi, sizeof(ULONG_PTR), NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); - ok( pbi[0] == dummy, "pbi[0] changed to %lx\n", pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); + status = NtQueryInformationProcess(NULL, ProcessWow64Information, pbi, sizeof(ULONG_PTR), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); + ok( pbi[0] == dummy, "pbi[0] changed to %Ix\n", pbi[0]); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); /* Use a greater buffer size */ pbi[0] = pbi[1] = dummy; - status = pNtQueryInformationProcess(NULL, ProcessWow64Information, pbi, sizeof(ULONG_PTR) + 1, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( pbi[0] == dummy, "pbi[0] changed to %lx\n", pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); + status = NtQueryInformationProcess(NULL, ProcessWow64Information, pbi, sizeof(ULONG_PTR) + 1, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( pbi[0] == dummy, "pbi[0] changed to %Ix\n", pbi[0]); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); /* Use no ReturnLength */ pbi[0] = pbi[1] = dummy; - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - trace( "Platform is_wow64 %d, ProcessInformation of ProcessWow64Information %lx\n", is_wow64, pbi[0]); - ok( is_wow64 == (pbi[0] != 0), "is_wow64 %x, pbi[0] %lx\n", is_wow64, pbi[0]); - ok( pbi[0] != dummy, "pbi[0] %lx\n", pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( is_wow64 == (pbi[0] != 0), "is_wow64 %x, pbi[0] %Ix\n", is_wow64, pbi[0]); + if (is_wow64) + ok( (void *)pbi[0] == NtCurrentTeb()->Peb, "pbi[0] %Ix / %p\n", pbi[0], NtCurrentTeb()->Peb); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); /* Test written size on 64 bit by checking high 32 bit buffer */ if (sizeof(ULONG_PTR) > sizeof(DWORD)) { @@ -1014,29 +1773,31 @@ static void test_query_process_wow64(void) /* Finally some correct calls */ pbi[0] = pbi[1] = dummy; ReturnLength = 0xdeadbeef; - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( is_wow64 == (pbi[0] != 0), "is_wow64 %x, pbi[0] %lx\n", is_wow64, pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); - ok( ReturnLength == sizeof(ULONG_PTR), "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( is_wow64 == (pbi[0] != 0), "is_wow64 %x, pbi[0] %Ix\n", is_wow64, pbi[0]); + if (is_wow64) + ok( (void *)pbi[0] == NtCurrentTeb()->Peb, "pbi[0] %Ix / %p\n", pbi[0], NtCurrentTeb()->Peb); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); + ok( ReturnLength == sizeof(ULONG_PTR), "Inconsistent length %ld\n", ReturnLength); /* Everything is correct except a too small buffer size */ pbi[0] = pbi[1] = dummy; ReturnLength = 0xdeadbeef; - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR) - 1, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( pbi[0] == dummy, "pbi[0] changed to %lx\n", pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); - todo_wine ok( ReturnLength == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", ReturnLength); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR) - 1, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( pbi[0] == dummy, "pbi[0] changed to %Ix\n", pbi[0]); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); + ok( ReturnLength == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", ReturnLength); /* Everything is correct except a too large buffer size */ pbi[0] = pbi[1] = dummy; ReturnLength = 0xdeadbeef; - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR) + 1, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( pbi[0] == dummy, "pbi[0] changed to %lx\n", pbi[0]); - ok( pbi[1] == dummy, "pbi[1] changed to %lx\n", pbi[1]); - todo_wine ok( ReturnLength == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", ReturnLength); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessWow64Information, pbi, sizeof(ULONG_PTR) + 1, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( pbi[0] == dummy, "pbi[0] changed to %Ix\n", pbi[0]); + ok( pbi[1] == dummy, "pbi[1] changed to %Ix\n", pbi[1]); + ok( ReturnLength == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", ReturnLength); } static void test_query_process_basic(void) @@ -1059,179 +1820,156 @@ static void test_query_process_basic(void) * every information class */ - /* Use a nonexistent info class */ - trace("Check nonexistent info class\n"); - status = pNtQueryInformationProcess(NULL, -1, NULL, 0, NULL); + status = NtQueryInformationProcess(NULL, -1, NULL, 0, NULL); ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED /* vista */, - "Expected STATUS_INVALID_INFO_CLASS or STATUS_NOT_IMPLEMENTED, got %08x\n", status); + "Expected STATUS_INVALID_INFO_CLASS or STATUS_NOT_IMPLEMENTED, got %08lx\n", status); - /* Do not give a handle and buffer */ - trace("Check NULL handle and buffer and zero-length buffersize\n"); - status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, 0, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, 0, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - /* Use a correct info class and buffer size, but still no handle and buffer */ - trace("Check NULL handle and buffer\n"); - status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, sizeof(pbi), NULL); + status = NtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, sizeof(pbi), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status); - /* Use a correct info class and buffer size, but still no handle */ - trace("Check NULL handle\n"); - status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - /* Use a greater buffer size */ - trace("Check NULL handle and too large buffersize\n"); - status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi) * 2, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi) * 2, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - /* Use no ReturnLength */ - trace("Check NULL ReturnLength\n"); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); - /* Finally some correct calls */ - trace("Check with correct parameters\n"); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(pbi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(pbi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); - /* Everything is correct except a too large buffersize */ - trace("Too large buffersize\n"); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi) * 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( sizeof(pbi) == ReturnLength, "Inconsistent length %d\n", ReturnLength); - - /* Check if we have some return values */ - trace("ProcessID : %lx\n", pbi.UniqueProcessId); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi) * 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( sizeof(pbi) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); + + if (winetest_debug > 1) trace("ProcessID : %Ix\n", pbi.UniqueProcessId); ok( pbi.UniqueProcessId > 0, "Expected a ProcessID > 0, got 0\n"); } -static void dump_vm_counters(const char *header, const VM_COUNTERS *pvi) +static void dump_vm_counters(const char *header, const VM_COUNTERS_EX *pvi) { trace("%s:\n", header); - trace("PeakVirtualSize : %lu\n", pvi->PeakVirtualSize); - trace("VirtualSize : %lu\n", pvi->VirtualSize); - trace("PageFaultCount : %u\n", pvi->PageFaultCount); - trace("PeakWorkingSetSize : %lu\n", pvi->PeakWorkingSetSize); - trace("WorkingSetSize : %lu\n", pvi->WorkingSetSize); - trace("QuotaPeakPagedPoolUsage : %lu\n", pvi->QuotaPeakPagedPoolUsage); - trace("QuotaPagedPoolUsage : %lu\n", pvi->QuotaPagedPoolUsage); - trace("QuotaPeakNonPagePoolUsage : %lu\n", pvi->QuotaPeakNonPagedPoolUsage); - trace("QuotaNonPagePoolUsage : %lu\n", pvi->QuotaNonPagedPoolUsage); - trace("PagefileUsage : %lu\n", pvi->PagefileUsage); - trace("PeakPagefileUsage : %lu\n", pvi->PeakPagefileUsage); + trace("PeakVirtualSize : %Iu\n", pvi->PeakVirtualSize); + trace("VirtualSize : %Iu\n", pvi->VirtualSize); + trace("PageFaultCount : %lu\n", pvi->PageFaultCount); + trace("PeakWorkingSetSize : %Iu\n", pvi->PeakWorkingSetSize); + trace("WorkingSetSize : %Iu\n", pvi->WorkingSetSize); + trace("QuotaPeakPagedPoolUsage : %Iu\n", pvi->QuotaPeakPagedPoolUsage); + trace("QuotaPagedPoolUsage : %Iu\n", pvi->QuotaPagedPoolUsage); + trace("QuotaPeakNonPagePoolUsage : %Iu\n", pvi->QuotaPeakNonPagedPoolUsage); + trace("QuotaNonPagePoolUsage : %Iu\n", pvi->QuotaNonPagedPoolUsage); + trace("PagefileUsage : %Iu\n", pvi->PagefileUsage); + trace("PeakPagefileUsage : %Iu\n", pvi->PeakPagefileUsage); } static void test_query_process_vm(void) { NTSTATUS status; ULONG ReturnLength; - VM_COUNTERS pvi; -#ifndef __REACTOS__ - ULONG old_size = FIELD_OFFSET(VM_COUNTERS,PrivatePageCount); -#endif + VM_COUNTERS_EX pvi; HANDLE process; SIZE_T prev_size; const SIZE_T alloc_size = 16 * 1024 * 1024; void *ptr; - status = pNtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL); + status = NtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status); -#ifndef __REACTOS__ - status = pNtQueryInformationProcess(NULL, ProcessVmCounters, &pvi, old_size, NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); -#endif + status = NtQueryInformationProcess(NULL, ProcessVmCounters, &pvi, sizeof(VM_COUNTERS), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - /* Windows XP and W2K3 will report success for a size of 44 AND 48 ! - Windows W2K will only report success for 44. - For now we only care for 44, which is FIELD_OFFSET(VM_COUNTERS,PrivatePageCount)) - */ + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 24, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 24, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(VM_COUNTERS), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( ReturnLength == sizeof(VM_COUNTERS), "Inconsistent length %ld\n", ReturnLength); -#ifndef __REACTOS__ - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, old_size, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( old_size == ReturnLength, "Inconsistent length %d\n", ReturnLength); -#endif - - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 46, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); -#ifndef __REACTOS__ - ok( ReturnLength == old_size || ReturnLength == sizeof(pvi), "Inconsistent length %d\n", ReturnLength); -#endif + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 46, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + todo_wine ok( ReturnLength == sizeof(VM_COUNTERS), "wrong size %ld\n", ReturnLength); /* Check if we have some return values */ - dump_vm_counters("VM counters for GetCurrentProcess", &pvi); + if (winetest_debug > 1) + dump_vm_counters("VM counters for GetCurrentProcess", &pvi); ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n"); process = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId()); - status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got %08x\n", status); + status = NtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got %08lx\n", status); CloseHandle(process); process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId()); - status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS || broken(!process) /* XP */, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS || broken(!process) /* XP */, "Expected STATUS_SUCCESS, got %08lx\n", status); CloseHandle(process); memset(&pvi, 0, sizeof(pvi)); process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); - status = pNtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(process, ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); /* Check if we have some return values */ - dump_vm_counters("VM counters for GetCurrentProcessId", &pvi); + if (winetest_debug > 1) + dump_vm_counters("VM counters for GetCurrentProcessId", &pvi); ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n"); ok( pvi.PagefileUsage > 0, "Expected a PagefileUsage > 0\n"); CloseHandle(process); /* Check if we have real counters */ - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); prev_size = pvi.VirtualSize; if (winetest_debug > 1) dump_vm_counters("VM counters before VirtualAlloc", &pvi); ptr = VirtualAlloc(NULL, alloc_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError()); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( ptr != NULL, "VirtualAlloc failed, err %lu\n", GetLastError()); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); if (winetest_debug > 1) dump_vm_counters("VM counters after VirtualAlloc", &pvi); todo_wine ok( pvi.VirtualSize >= prev_size + alloc_size, - "Expected to be greater than %lu, got %lu\n", prev_size + alloc_size, pvi.VirtualSize); + "Expected to be greater than %Iu, got %Iu\n", prev_size + alloc_size, pvi.VirtualSize); VirtualFree( ptr, 0, MEM_RELEASE); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); prev_size = pvi.VirtualSize; if (winetest_debug > 1) dump_vm_counters("VM counters before VirtualAlloc", &pvi); ptr = VirtualAlloc(NULL, alloc_size, MEM_RESERVE, PAGE_READWRITE); - ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError()); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( ptr != NULL, "VirtualAlloc failed, err %lu\n", GetLastError()); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); if (winetest_debug > 1) dump_vm_counters("VM counters after VirtualAlloc(MEM_RESERVE)", &pvi); todo_wine ok( pvi.VirtualSize >= prev_size + alloc_size, - "Expected to be greater than %lu, got %lu\n", prev_size + alloc_size, pvi.VirtualSize); + "Expected to be greater than %Iu, got %Iu\n", prev_size + alloc_size, pvi.VirtualSize); prev_size = pvi.VirtualSize; ptr = VirtualAlloc(ptr, alloc_size, MEM_COMMIT, PAGE_READWRITE); - ok( ptr != NULL, "VirtualAlloc failed, err %u\n", GetLastError()); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( ptr != NULL, "VirtualAlloc failed, err %lu\n", GetLastError()); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), NULL); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( pvi.PrivateUsage == pvi.PagefileUsage, "wrong value %Iu/%Iu\n", pvi.PrivateUsage, pvi.PagefileUsage ); if (winetest_debug > 1) dump_vm_counters("VM counters after VirtualAlloc(MEM_COMMIT)", &pvi); ok( pvi.VirtualSize == prev_size, - "Expected to equal to %lu, got %lu\n", prev_size, pvi.VirtualSize); + "Expected to equal to %Iu, got %Iu\n", prev_size, pvi.VirtualSize); VirtualFree( ptr, 0, MEM_RELEASE); } @@ -1241,34 +1979,26 @@ static void test_query_process_io(void) ULONG ReturnLength; IO_COUNTERS pii; - /* NT4 doesn't support this information class, so check for it */ - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii), &ReturnLength); - if (status == STATUS_NOT_SUPPORTED) - { - win_skip("ProcessIoCounters information class is not supported\n"); - return; - } - - status = pNtQueryInformationProcess(NULL, ProcessIoCounters, NULL, sizeof(pii), NULL); + status = NtQueryInformationProcess(NULL, ProcessIoCounters, NULL, sizeof(pii), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status); - status = pNtQueryInformationProcess(NULL, ProcessIoCounters, &pii, sizeof(pii), NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessIoCounters, &pii, sizeof(pii), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, 24, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, 24, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(pii) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(pii) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii) * 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( sizeof(pii) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii) * 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + ok( sizeof(pii) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); /* Check if we have some return values */ - trace("OtherOperationCount : 0x%s\n", wine_dbgstr_longlong(pii.OtherOperationCount)); + if (winetest_debug > 1) trace("OtherOperationCount : 0x%s\n", wine_dbgstr_longlong(pii.OtherOperationCount)); todo_wine { ok( pii.OtherOperationCount > 0, "Expected an OtherOperationCount > 0\n"); @@ -1283,53 +2013,52 @@ static void test_query_process_times(void) SYSTEMTIME UTC, Local; KERNEL_USER_TIMES spti; - status = pNtQueryInformationProcess(NULL, ProcessTimes, NULL, sizeof(spti), NULL); + status = NtQueryInformationProcess(NULL, ProcessTimes, NULL, sizeof(spti), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status); - status = pNtQueryInformationProcess(NULL, ProcessTimes, &spti, sizeof(spti), NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessTimes, &spti, sizeof(spti), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, 24, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, 24, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, one_before_last_pid); if (!process) { - trace("Could not open process with ID : %d, error : %u. Going to use current one.\n", one_before_last_pid, GetLastError()); + if (winetest_debug > 1) trace("Could not open process with ID : %ld, error : %lu. Going to use current one.\n", one_before_last_pid, GetLastError()); process = GetCurrentProcess(); - trace("ProcessTimes for current process\n"); } else - trace("ProcessTimes for process with ID : %d\n", one_before_last_pid); + trace("ProcessTimes for process with ID : %ld\n", one_before_last_pid); - status = pNtQueryInformationProcess( process, ProcessTimes, &spti, sizeof(spti), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(spti) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess( process, ProcessTimes, &spti, sizeof(spti), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(spti) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); CloseHandle(process); FileTimeToSystemTime((const FILETIME *)&spti.CreateTime, &UTC); SystemTimeToTzSpecificLocalTime(NULL, &UTC, &Local); - trace("CreateTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear, + if (winetest_debug > 1) trace("CreateTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear, Local.wHour, Local.wMinute, Local.wSecond); FileTimeToSystemTime((const FILETIME *)&spti.ExitTime, &UTC); SystemTimeToTzSpecificLocalTime(NULL, &UTC, &Local); - trace("ExitTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear, + if (winetest_debug > 1) trace("ExitTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear, Local.wHour, Local.wMinute, Local.wSecond); FileTimeToSystemTime((const FILETIME *)&spti.KernelTime, &Local); - trace("KernelTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds); + if (winetest_debug > 1) trace("KernelTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds); FileTimeToSystemTime((const FILETIME *)&spti.UserTime, &Local); - trace("UserTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds); + if (winetest_debug > 1) trace("UserTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, sizeof(spti) * 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, sizeof(spti) * 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); ok( sizeof(spti) == ReturnLength || ReturnLength == 0 /* vista */ || broken(is_wow64), /* returns garbage on wow64 */ - "Inconsistent length %d\n", ReturnLength); + "Inconsistent length %ld\n", ReturnLength); } static void test_query_process_debug_port(int argc, char **argv) @@ -1340,68 +2069,202 @@ static void test_query_process_debug_port(int argc, char **argv) STARTUPINFOA si = { 0 }; NTSTATUS status; BOOL ret; + ULONG len; sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee"); si.cb = sizeof(si); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); - ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); + ok(ret, "CreateProcess failed, last error %#lx.\n", GetLastError()); if (!ret) return; - status = pNtQueryInformationProcess(NULL, ProcessDebugPort, + status = NtQueryInformationProcess(NULL, ProcessDebugPort, NULL, 0, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); - status = pNtQueryInformationProcess(NULL, ProcessDebugPort, + status = NtQueryInformationProcess(NULL, ProcessDebugPort, NULL, sizeof(debug_port), NULL); - ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION, - "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); + ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION /* XP */, "got %#lx\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, NULL, sizeof(debug_port), NULL); - ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %#lx.\n", status); - status = pNtQueryInformationProcess(NULL, ProcessDebugPort, - &debug_port, sizeof(debug_port), NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); + len = 0xdeadbeef; + status = NtQueryInformationProcess(NULL, ProcessDebugPort, + &debug_port, sizeof(debug_port), &len); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %#lx.\n", status); + ok(len == 0xdeadbeef || broken(len != sizeof(debug_port)), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, - &debug_port, sizeof(debug_port) - 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + len = 0xdeadbeef; + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, + &debug_port, sizeof(debug_port) - 1, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); + ok(len == 0xdeadbeef || broken(len != sizeof(debug_port)), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, - &debug_port, sizeof(debug_port) + 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + len = 0xdeadbeef; + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, + &debug_port, sizeof(debug_port) + 1, &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); + ok(len == 0xdeadbeef || broken(len != sizeof(debug_port)), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, - &debug_port, sizeof(debug_port), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_port == 0, "Expected port 0, got %#lx.\n", debug_port); + len = 0xdeadbeef; + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort, + &debug_port, sizeof(debug_port), &len); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_port == 0, "Expected port 0, got %#Ix.\n", debug_port); + ok(len == sizeof(debug_port), "len set to %lx\n", len ); - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugPort, - &debug_port, sizeof(debug_port), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_port == ~(DWORD_PTR)0, "Expected port %#lx, got %#lx.\n", ~(DWORD_PTR)0, debug_port); + len = 0xdeadbeef; + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugPort, + &debug_port, sizeof(debug_port), &len); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_port == ~(DWORD_PTR)0, "Expected port %#Ix, got %#Ix.\n", ~(DWORD_PTR)0, debug_port); + ok(len == sizeof(debug_port), "len set to %lx\n", len ); for (;;) { DEBUG_EVENT ev; ret = WaitForDebugEvent(&ev, INFINITE); - ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); + ok(ret, "WaitForDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); + ok(ret, "ContinueDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; } ret = CloseHandle(pi.hThread); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); ret = CloseHandle(pi.hProcess); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); +} + +static void subtest_query_process_debug_port_custom_dacl(int argc, char **argv, ACCESS_MASK access, PSID sid) +{ + HANDLE old_debug_obj, debug_obj; + OBJECT_ATTRIBUTES attr; + SECURITY_DESCRIPTOR sd; + union { + ACL acl; + DWORD buffer[(sizeof(ACL) + + (offsetof(ACCESS_ALLOWED_ACE, SidStart) + SECURITY_MAX_SID_SIZE) + + sizeof(DWORD) - 1) / sizeof(DWORD)]; + } acl; + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFOA si; + DEBUG_EVENT ev; + NTSTATUS status; + BOOL ret; + + InitializeAcl(&acl.acl, sizeof(acl), ACL_REVISION); + AddAccessAllowedAce(&acl.acl, ACL_REVISION, access, sid); + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, &acl.acl, FALSE); + + InitializeObjectAttributes(&attr, NULL, 0, NULL, &sd); + status = NtCreateDebugObject(&debug_obj, MAXIMUM_ALLOWED, &attr, DEBUG_KILL_ON_CLOSE); + ok(SUCCEEDED(status), "Failed to create debug object: %#010lx\n", status); + if (FAILED(status)) return; + + old_debug_obj = pDbgUiGetThreadDebugObject(); + pDbgUiSetThreadDebugObject(debug_obj); + + sprintf(cmdline, "%s %s %s %lu", argv[0], argv[1], "debuggee:dbgport", access); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, + DEBUG_PROCESS, NULL, NULL, &si, &pi); + ok(ret, "CreateProcess failed, last error %#lx.\n", GetLastError()); + if (!ret) goto close_debug_obj; + + do + { + ret = WaitForDebugEvent(&ev, INFINITE); + ok(ret, "WaitForDebugEvent failed, last error %#lx.\n", GetLastError()); + if (!ret) break; + + ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); + ok(ret, "ContinueDebugEvent failed, last error %#lx.\n", GetLastError()); + if (!ret) break; + } while (ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); + + wait_child_process(pi.hProcess); + ret = CloseHandle(pi.hThread); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); + +close_debug_obj: + pDbgUiSetThreadDebugObject(old_debug_obj); + NtClose(debug_obj); +} + +static TOKEN_OWNER *get_current_owner(void) +{ + TOKEN_OWNER *owner; + ULONG length = 0; + HANDLE token; + BOOL ret; + + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); + ok(ret, "Failed to get process token: %lu\n", GetLastError()); + + ret = GetTokenInformation(token, TokenOwner, NULL, 0, &length); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "GetTokenInformation failed: %lu\n", GetLastError()); + ok(length != 0, "Failed to get token owner information length: %lu\n", GetLastError()); + + owner = HeapAlloc(GetProcessHeap(), 0, length); + ret = GetTokenInformation(token, TokenOwner, owner, length, &length); + ok(ret, "Failed to get token owner information: %lu)\n", GetLastError()); + + CloseHandle(token); + return owner; +} + +static void test_query_process_debug_port_custom_dacl(int argc, char **argv) +{ + static const ACCESS_MASK all_access_masks[] = { + GENERIC_ALL, + DEBUG_ALL_ACCESS, + STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE, + }; + TOKEN_OWNER *owner; + int i; + + if (!pDbgUiSetThreadDebugObject) + { + win_skip("DbgUiGetThreadDebugObject not found\n"); + return; + } + + if (!pDbgUiGetThreadDebugObject) + { + win_skip("DbgUiSetThreadDebugObject not found\n"); + return; + } + + owner = get_current_owner(); + + for (i = 0; i < ARRAY_SIZE(all_access_masks); i++) + { + ACCESS_MASK access = all_access_masks[i]; + + winetest_push_context("debug object access %08lx", access); + subtest_query_process_debug_port_custom_dacl(argc, argv, access, owner->Owner); + winetest_pop_context(); + } + + HeapFree(GetProcessHeap(), 0, owner); } static void test_query_process_priority(void) @@ -1412,30 +2275,30 @@ static void test_query_process_priority(void) NTSTATUS status; BOOL ret; - status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, NULL, sizeof(priority[0]), NULL); + status = NtQueryInformationProcess(NULL, ProcessPriorityClass, NULL, sizeof(priority[0]), NULL); ok(status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_HANDLE) /* w2k3 */, - "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); - status = pNtQueryInformationProcess(NULL, ProcessPriorityClass, &priority, sizeof(priority[0]), NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessPriorityClass, &priority, sizeof(priority[0]), NULL); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, 1, &ReturnLength); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, 1, &ReturnLength); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority), &ReturnLength); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority), &ReturnLength); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); orig_priority = GetPriorityClass(GetCurrentProcess()); ret = SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); - ok(ret, "Failed to set priority class: %u\n", GetLastError()); + ok(ret, "Failed to set priority class: %lu\n", GetLastError()); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority[0]), &ReturnLength); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessPriorityClass, &priority, sizeof(priority[0]), &ReturnLength); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); ok(priority[0].PriorityClass == PROCESS_PRIOCLASS_BELOW_NORMAL, "Expected PROCESS_PRIOCLASS_BELOW_NORMAL, got %u\n", priority[0].PriorityClass); ret = SetPriorityClass(GetCurrentProcess(), orig_priority); - ok(ret, "Failed to reset priority class: %u\n", GetLastError()); + ok(ret, "Failed to reset priority class: %lu\n", GetLastError()); } static void test_query_process_handlecount(void) @@ -1446,38 +2309,37 @@ static void test_query_process_handlecount(void) BYTE buffer[2 * sizeof(DWORD)]; HANDLE process; - status = pNtQueryInformationProcess(NULL, ProcessHandleCount, NULL, sizeof(handlecount), NULL); + status = NtQueryInformationProcess(NULL, ProcessHandleCount, NULL, sizeof(handlecount), NULL); ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status); - status = pNtQueryInformationProcess(NULL, ProcessHandleCount, &handlecount, sizeof(handlecount), NULL); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + status = NtQueryInformationProcess(NULL, ProcessHandleCount, &handlecount, sizeof(handlecount), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, &handlecount, 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, &handlecount, 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, one_before_last_pid); if (!process) { - trace("Could not open process with ID : %d, error : %u. Going to use current one.\n", one_before_last_pid, GetLastError()); + trace("Could not open process with ID : %ld, error : %lu. Going to use current one.\n", one_before_last_pid, GetLastError()); process = GetCurrentProcess(); - trace("ProcessHandleCount for current process\n"); } else - trace("ProcessHandleCount for process with ID : %d\n", one_before_last_pid); + if (winetest_debug > 1) trace("ProcessHandleCount for process with ID : %ld\n", one_before_last_pid); - status = pNtQueryInformationProcess( process, ProcessHandleCount, &handlecount, sizeof(handlecount), &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(handlecount) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + status = NtQueryInformationProcess( process, ProcessHandleCount, &handlecount, sizeof(handlecount), &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(handlecount) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); CloseHandle(process); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, buffer, sizeof(buffer), &ReturnLength); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, buffer, sizeof(buffer), &ReturnLength); ok( status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_SUCCESS, - "Expected STATUS_INFO_LENGTH_MISMATCH or STATUS_SUCCESS, got %08x\n", status); - ok( sizeof(handlecount) == ReturnLength, "Inconsistent length %d\n", ReturnLength); + "Expected STATUS_INFO_LENGTH_MISMATCH or STATUS_SUCCESS, got %08lx\n", status); + ok( sizeof(handlecount) == ReturnLength, "Inconsistent length %ld\n", ReturnLength); /* Check if we have some return values */ - trace("HandleCount : %d\n", handlecount); + if (winetest_debug > 1) trace("HandleCount : %ld\n", handlecount); todo_wine { ok( handlecount > 0, "Expected some handles, got 0\n"); @@ -1486,39 +2348,88 @@ static void test_query_process_handlecount(void) static void test_query_process_image_file_name(void) { + static const WCHAR deviceW[] = {'\\','D','e','v','i','c','e','\\'}; NTSTATUS status; ULONG ReturnLength; UNICODE_STRING image_file_name; - void *buffer; - char *file_nameA; - INT len; + UNICODE_STRING *buffer = NULL; - status = pNtQueryInformationProcess(NULL, ProcessImageFileName, &image_file_name, sizeof(image_file_name), NULL); + status = NtQueryInformationProcess(NULL, ProcessImageFileName, &image_file_name, sizeof(image_file_name), NULL); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, &image_file_name, 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, &image_file_name, sizeof(image_file_name), &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); + + buffer = malloc(ReturnLength); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, buffer, ReturnLength, &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + todo_wine + ok(!memcmp(buffer->Buffer, deviceW, sizeof(deviceW)), + "Expected image name to begin with \\Device\\, got %s\n", + wine_dbgstr_wn(buffer->Buffer, buffer->Length / sizeof(WCHAR))); + free(buffer); + + status = NtQueryInformationProcess(NULL, ProcessImageFileNameWin32, &image_file_name, sizeof(image_file_name), NULL); if (status == STATUS_INVALID_INFO_CLASS) { - win_skip("ProcessImageFileName is not supported\n"); + win_skip("ProcessImageFileNameWin32 is not supported\n"); return; } - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, &image_file_name, 2, &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileNameWin32, &image_file_name, 2, &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, &image_file_name, sizeof(image_file_name), &ReturnLength); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileNameWin32, &image_file_name, sizeof(image_file_name), &ReturnLength); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - buffer = HeapAlloc(GetProcessHeap(), 0, ReturnLength); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, buffer, ReturnLength, &ReturnLength); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - memcpy(&image_file_name, buffer, sizeof(image_file_name)); - len = WideCharToMultiByte(CP_ACP, 0, image_file_name.Buffer, image_file_name.Length/sizeof(WCHAR), NULL, 0, NULL, NULL); - file_nameA = HeapAlloc(GetProcessHeap(), 0, len + 1); - WideCharToMultiByte(CP_ACP, 0, image_file_name.Buffer, image_file_name.Length/sizeof(WCHAR), file_nameA, len, NULL, NULL); - file_nameA[len] = '\0'; - HeapFree(GetProcessHeap(), 0, buffer); - trace("process image file name: %s\n", file_nameA); - todo_wine ok(strncmp(file_nameA, "\\Device\\", 8) == 0, "Process image name should be an NT path beginning with \\Device\\ (is %s)\n", file_nameA); - HeapFree(GetProcessHeap(), 0, file_nameA); + buffer = malloc(ReturnLength); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileNameWin32, buffer, ReturnLength, &ReturnLength); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok(memcmp(buffer->Buffer, deviceW, sizeof(deviceW)), + "Expected image name not to begin with \\Device\\, got %s\n", + wine_dbgstr_wn(buffer->Buffer, buffer->Length / sizeof(WCHAR))); + free(buffer); +} + +static void test_query_process_image_info(void) +{ + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); + NTSTATUS status; + SECTION_IMAGE_INFORMATION info; + ULONG len; + + status = NtQueryInformationProcess( NULL, ProcessImageInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_PARAMETER), /* winxp */ + "got %08lx\n", status); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageInformation, &info, sizeof(info)-1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageInformation, &info, sizeof(info)+1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status); + + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageInformation, &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "got %08lx\n", status); + ok( len == sizeof(info), "wrong len %lu\n", len ); + + ok( info.MajorSubsystemVersion == nt->OptionalHeader.MajorSubsystemVersion, + "wrong major version %x/%x\n", + info.MajorSubsystemVersion, nt->OptionalHeader.MajorSubsystemVersion ); + ok( info.MinorSubsystemVersion == nt->OptionalHeader.MinorSubsystemVersion, + "wrong minor version %x/%x\n", + info.MinorSubsystemVersion, nt->OptionalHeader.MinorSubsystemVersion ); + ok( info.MajorOperatingSystemVersion == nt->OptionalHeader.MajorOperatingSystemVersion || + broken( !info.MajorOperatingSystemVersion ), /* <= win8 */ + "wrong major OS version %x/%x\n", + info.MajorOperatingSystemVersion, nt->OptionalHeader.MajorOperatingSystemVersion ); + ok( info.MinorOperatingSystemVersion == nt->OptionalHeader.MinorOperatingSystemVersion, + "wrong minor OS version %x/%x\n", + info.MinorOperatingSystemVersion, nt->OptionalHeader.MinorOperatingSystemVersion ); } static void test_query_process_debug_object_handle(int argc, char **argv) @@ -1529,101 +2440,103 @@ static void test_query_process_debug_object_handle(int argc, char **argv) BOOL ret; HANDLE debug_object; NTSTATUS status; + ULONG len; sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee"); si.cb = sizeof(si); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); - ok(ret, "CreateProcess failed with last error %u\n", GetLastError()); + ok(ret, "CreateProcess failed with last error %lu\n", GetLastError()); if (!ret) return; - status = pNtQueryInformationProcess(NULL, ProcessDebugObjectHandle, NULL, - 0, NULL); - if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) - { - win_skip("ProcessDebugObjectHandle is not supported\n"); - return; - } + len = 0xdeadbeef; + status = NtQueryInformationProcess(NULL, ProcessDebugObjectHandle, NULL, 0, &len); ok(status == STATUS_INFO_LENGTH_MISMATCH, - "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08x\n", + "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08lx\n", status); + ok(len == 0xdeadbeef || broken(len == 0xfffffffc || len == 0xffc), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(NULL, ProcessDebugObjectHandle, NULL, - sizeof(debug_object), NULL); + len = 0xdeadbeef; + status = NtQueryInformationProcess(NULL, ProcessDebugObjectHandle, NULL, sizeof(debug_object), &len); ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION, /* XP */ - "Expected NtQueryInformationProcess to return STATUS_INVALID_HANDLE, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_INVALID_HANDLE, got 0x%08lx\n", status); + ok(len == 0xdeadbeef || broken(len == 0xfffffffc || len == 0xffc), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(GetCurrentProcess(), - ProcessDebugObjectHandle, NULL, sizeof(debug_object), NULL); + status = NtQueryInformationProcess(GetCurrentProcess(), + ProcessDebugObjectHandle, NULL, sizeof(debug_object), &len); ok(status == STATUS_ACCESS_VIOLATION, - "Expected NtQueryInformationProcess to return STATUS_ACCESS_VIOLATION, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_ACCESS_VIOLATION, got 0x%08lx\n", status); + ok(len == 0xdeadbeef || broken(len == 0xfffffffc || len == 0xffc), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(NULL, ProcessDebugObjectHandle, + status = NtQueryInformationProcess(NULL, ProcessDebugObjectHandle, &debug_object, sizeof(debug_object), NULL); ok(status == STATUS_INVALID_HANDLE, - "Expected NtQueryInformationProcess to return STATUS_ACCESS_VIOLATION, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_ACCESS_VIOLATION, got 0x%08lx\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), - ProcessDebugObjectHandle, &debug_object, - sizeof(debug_object) - 1, NULL); + len = 0xdeadbeef; + status = NtQueryInformationProcess(GetCurrentProcess(), + ProcessDebugObjectHandle, &debug_object, sizeof(debug_object) - 1, &len); ok(status == STATUS_INFO_LENGTH_MISMATCH, - "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08lx\n", status); + ok(len == 0xdeadbeef || broken(len == 0xfffffffc || len == 0xffc), /* wow64 */ + "len set to %lx\n", len ); - status = pNtQueryInformationProcess(GetCurrentProcess(), - ProcessDebugObjectHandle, &debug_object, - sizeof(debug_object) + 1, NULL); + len = 0xdeadbeef; + status = NtQueryInformationProcess(GetCurrentProcess(), + ProcessDebugObjectHandle, &debug_object, sizeof(debug_object) + 1, &len); ok(status == STATUS_INFO_LENGTH_MISMATCH, - "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_INFO_LENGTH_MISMATCH, got 0x%08lx\n", status); + ok(len == 0xdeadbeef || broken(len == 0xfffffffc || len == 0xffc), /* wow64 */ + "len set to %lx\n", len ); + len = 0xdeadbeef; debug_object = (HANDLE)0xdeadbeef; - status = pNtQueryInformationProcess(GetCurrentProcess(), + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugObjectHandle, &debug_object, - sizeof(debug_object), NULL); + sizeof(debug_object), &len); ok(status == STATUS_PORT_NOT_SET, - "Expected NtQueryInformationProcess to return STATUS_PORT_NOT_SET, got 0x%08x\n", status); + "Expected NtQueryInformationProcess to return STATUS_PORT_NOT_SET, got 0x%08lx\n", status); ok(debug_object == NULL || broken(debug_object == (HANDLE)0xdeadbeef), /* Wow64 */ "Expected debug object handle to be NULL, got %p\n", debug_object); + ok(len == sizeof(debug_object), "len set to %lx\n", len ); + len = 0xdeadbeef; debug_object = (HANDLE)0xdeadbeef; - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugObjectHandle, - &debug_object, sizeof(debug_object), NULL); -#ifndef __REACTOS__ - todo_wine -#endif + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugObjectHandle, + &debug_object, sizeof(debug_object), &len); ok(status == STATUS_SUCCESS, - "Expected NtQueryInformationProcess to return STATUS_SUCCESS, got 0x%08x\n", status); -#ifndef __REACTOS__ - todo_wine -#endif + "Expected NtQueryInformationProcess to return STATUS_SUCCESS, got 0x%08lx\n", status); ok(debug_object != NULL, "Expected debug object handle to be non-NULL, got %p\n", debug_object); -#ifdef __REACTOS__ + ok(len == sizeof(debug_object), "len set to %lx\n", len ); status = NtClose( debug_object ); - ok( !status, "NtClose failed %x\n", status ); -#endif + ok( !status, "NtClose failed %lx\n", status ); for (;;) { DEBUG_EVENT ev; ret = WaitForDebugEvent(&ev, INFINITE); - ok(ret, "WaitForDebugEvent failed with last error %u\n", GetLastError()); + ok(ret, "WaitForDebugEvent failed with last error %lu\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - ok(ret, "ContinueDebugEvent failed with last error %u\n", GetLastError()); + ok(ret, "ContinueDebugEvent failed with last error %lu\n", GetLastError()); if (!ret) break; } ret = CloseHandle(pi.hThread); - ok(ret, "CloseHandle failed with last error %u\n", GetLastError()); + ok(ret, "CloseHandle failed with last error %lu\n", GetLastError()); ret = CloseHandle(pi.hProcess); - ok(ret, "CloseHandle failed with last error %u\n", GetLastError()); + ok(ret, "CloseHandle failed with last error %lu\n", GetLastError()); } static void test_query_process_debug_flags(int argc, char **argv) @@ -1643,64 +2556,64 @@ static void test_query_process_debug_flags(int argc, char **argv) int i, j; /* test invalid arguments */ - status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, 0, NULL); + status = NtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, 0, NULL); ok(status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, - "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); - status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, sizeof(debug_flags), NULL); + status = NtQueryInformationProcess(NULL, ProcessDebugFlags, NULL, sizeof(debug_flags), NULL); ok(status == STATUS_INVALID_HANDLE || status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, - "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); + "Expected STATUS_INVALID_HANDLE, got %#lx.\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, NULL, sizeof(debug_flags), NULL); - ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %#x.\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %#lx.\n", status); - status = pNtQueryInformationProcess(NULL, ProcessDebugFlags, + status = NtQueryInformationProcess(NULL, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); ok(status == STATUS_INVALID_HANDLE || broken(status == STATUS_INVALID_INFO_CLASS) /* WOW64 */, - "Expected STATUS_INVALID_HANDLE, got %#x.\n", status); + "Expected STATUS_INVALID_HANDLE, got %#lx.\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags) - 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags) + 1, NULL); - ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#x.\n", status); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx.\n", status); /* test ProcessDebugFlags of current process */ - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == TRUE, "Expected flag TRUE, got %lx.\n", debug_flags); - for (i = 0; i < sizeof(test_flags)/sizeof(test_flags[0]); i++) + for (i = 0; i < ARRAY_SIZE(test_flags); i++) { DWORD expected_flags = !(test_flags[i] & DEBUG_ONLY_THIS_PROCESS); sprintf(cmdline, "%s %s %s", argv[0], argv[1], "debuggee"); si.cb = sizeof(si); ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, test_flags[i], NULL, NULL, &si, &pi); - ok(ret, "CreateProcess failed, last error %#x.\n", GetLastError()); + ok(ret, "CreateProcess failed, last error %#lx.\n", GetLastError()); if (!(test_flags[i] & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))) { /* test ProcessDebugFlags before attaching with debugger */ - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == TRUE, "Expected flag TRUE, got %x.\n", debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == TRUE, "Expected flag TRUE, got %lx.\n", debug_flags); ret = DebugActiveProcess(pi.dwProcessId); - ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError()); + ok(ret, "DebugActiveProcess failed, last error %#lx.\n", GetLastError()); expected_flags = FALSE; } /* test ProcessDebugFlags after attaching with debugger */ - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == expected_flags, "Expected flag %lx, got %lx.\n", expected_flags, debug_flags); if (!(test_flags[i] & CREATE_SUSPENDED)) { @@ -1709,42 +2622,40 @@ static void test_query_process_debug_flags(int argc, char **argv) for (;;) { ret = WaitForDebugEvent(&ev, 1000); - disable_success_count - ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); + ok(ret, "WaitForDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) break; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - disable_success_count - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); + ok(ret, "ContinueDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; } result = SuspendThread(pi.hThread); - ok(result == 0, "Expected 0, got %u.\n", result); + ok(result == 0, "Expected 0, got %lu.\n", result); } ret = DebugActiveProcessStop(pi.dwProcessId); - ok(ret, "DebugActiveProcessStop failed, last error %#x.\n", GetLastError()); + ok(ret, "DebugActiveProcessStop failed, last error %#lx.\n", GetLastError()); /* test ProcessDebugFlags after detaching debugger */ - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == expected_flags, "Expected flag %x, got %x.\n", expected_flags, debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == expected_flags, "Expected flag %lx, got %lx.\n", expected_flags, debug_flags); ret = DebugActiveProcess(pi.dwProcessId); - ok(ret, "DebugActiveProcess failed, last error %#x.\n", GetLastError()); + ok(ret, "DebugActiveProcess failed, last error %#lx.\n", GetLastError()); /* test ProcessDebugFlags after re-attaching debugger */ - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == FALSE, "Expected flag FALSE, got %lx.\n", debug_flags); result = ResumeThread(pi.hThread); - todo_wine ok(result == 2, "Expected 2, got %u.\n", result); + todo_wine ok(result == 2, "Expected 2, got %lu.\n", result); /* Wait until the process is terminated. On Windows XP the process randomly * gets stuck in a non-continuable exception, so stop after 100 iterations. @@ -1753,33 +2664,84 @@ static void test_query_process_debug_flags(int argc, char **argv) for (j = 0; j < 100; j++) { ret = WaitForDebugEvent(&ev, 1000); - disable_success_count ok(ret || broken(GetLastError() == ERROR_SEM_TIMEOUT), - "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); + "WaitForDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; if (ev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - disable_success_count - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); + ok(ret, "ContinueDebugEvent failed, last error %#lx.\n", GetLastError()); if (!ret) break; } ok(j < 100 || broken(j >= 100) /* Win XP */, "Expected less than 100 debug events.\n"); /* test ProcessDebugFlags after process has terminated */ - status = pNtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, + status = NtQueryInformationProcess(pi.hProcess, ProcessDebugFlags, &debug_flags, sizeof(debug_flags), NULL); - ok(!status, "NtQueryInformationProcess failed, status %#x.\n", status); - ok(debug_flags == FALSE, "Expected flag FALSE, got %x.\n", debug_flags); + ok(!status, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(debug_flags == FALSE, "Expected flag FALSE, got %lx.\n", debug_flags); ret = CloseHandle(pi.hThread); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); ret = CloseHandle(pi.hProcess); - ok(ret, "CloseHandle failed, last error %#x.\n", GetLastError()); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); } } +static void test_query_process_quota_limits(void) +{ + QUOTA_LIMITS qlimits; + NTSTATUS status; + HANDLE process; + ULONG ret_len; + + status = NtQueryInformationProcess(NULL, ProcessQuotaLimits, NULL, sizeof(qlimits), NULL); + ok(status == STATUS_INVALID_HANDLE, "NtQueryInformationProcess failed, status %#lx.\n", status); + + status = NtQueryInformationProcess(NULL, ProcessQuotaLimits, &qlimits, sizeof(qlimits), NULL); + ok(status == STATUS_INVALID_HANDLE, "NtQueryInformationProcess failed, status %#lx.\n", status); + + process = GetCurrentProcess(); + status = NtQueryInformationProcess( process, ProcessQuotaLimits, &qlimits, 2, &ret_len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationProcess failed, status %#lx.\n", status); + + memset(&qlimits, 0, sizeof(qlimits)); + status = NtQueryInformationProcess( process, ProcessQuotaLimits, &qlimits, sizeof(qlimits), &ret_len); + ok(status == STATUS_SUCCESS, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(sizeof(qlimits) == ret_len, "len set to %lx\n", ret_len); + ok(qlimits.MinimumWorkingSetSize == 204800,"Expected MinimumWorkingSetSize = 204800, got %s\n", + wine_dbgstr_longlong(qlimits.MinimumWorkingSetSize)); + ok(qlimits.MaximumWorkingSetSize == 1413120,"Expected MaximumWorkingSetSize = 1413120, got %s\n", + wine_dbgstr_longlong(qlimits.MaximumWorkingSetSize)); + ok(qlimits.PagefileLimit == ~0,"Expected PagefileLimit = ~0, got %s\n", + wine_dbgstr_longlong(qlimits.PagefileLimit)); + ok(qlimits.TimeLimit.QuadPart == ~0,"Expected TimeLimit = ~0, got %s\n", + wine_dbgstr_longlong(qlimits.TimeLimit.QuadPart)); + + if (winetest_debug > 1) + { + trace("Quota Limits:\n"); + trace("PagedPoolLimit: %s\n", wine_dbgstr_longlong(qlimits.PagedPoolLimit)); + trace("NonPagedPoolLimit: %s\n", wine_dbgstr_longlong(qlimits.NonPagedPoolLimit)); + } + + memset(&qlimits, 0, sizeof(qlimits)); + status = NtQueryInformationProcess( process, ProcessQuotaLimits, &qlimits, sizeof(qlimits) * 2, &ret_len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(sizeof(qlimits) == ret_len, "len set to %lx\n", ret_len); + + memset(&qlimits, 0, sizeof(qlimits)); + status = NtQueryInformationProcess( process, ProcessQuotaLimits, &qlimits, sizeof(qlimits) - 1, &ret_len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(sizeof(qlimits) == ret_len, "len set to %lx\n", ret_len); + + memset(&qlimits, 0, sizeof(qlimits)); + status = NtQueryInformationProcess( process, ProcessQuotaLimits, &qlimits, sizeof(qlimits) + 1, &ret_len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationProcess failed, status %#lx.\n", status); + ok(sizeof(qlimits) == ret_len, "len set to %lx\n", ret_len); +} + static void test_readvirtualmemory(void) { HANDLE process; @@ -1793,40 +2755,42 @@ static void test_readvirtualmemory(void) /* normal operation */ status = pNtReadVirtualMemory(process, teststring, buffer, 12, &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == 12, "Expected to read 12 bytes, got %ld\n",readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == 12, "Expected to read 12 bytes, got %Id\n",readcount); ok( strcmp(teststring, buffer) == 0, "Expected read memory to be the same as original memory\n"); /* no number of bytes */ memset(buffer, 0, 12); status = pNtReadVirtualMemory(process, teststring, buffer, 12, NULL); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); ok( strcmp(teststring, buffer) == 0, "Expected read memory to be the same as original memory\n"); /* illegal remote address */ todo_wine{ status = pNtReadVirtualMemory(process, (void *) 0x1234, buffer, 12, &readcount); - ok( status == STATUS_PARTIAL_COPY || broken(status == STATUS_ACCESS_VIOLATION), "Expected STATUS_PARTIAL_COPY, got %08x\n", status); + ok( status == STATUS_PARTIAL_COPY, "Expected STATUS_PARTIAL_COPY, got %08lx\n", status); if (status == STATUS_PARTIAL_COPY) - ok( readcount == 0, "Expected to read 0 bytes, got %ld\n",readcount); + ok( readcount == 0, "Expected to read 0 bytes, got %Id\n",readcount); } /* 0 handle */ status = pNtReadVirtualMemory(0, teststring, buffer, 12, &readcount); - ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08x\n", status); - ok( readcount == 0, "Expected to read 0 bytes, got %ld\n",readcount); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status); + ok( readcount == 0, "Expected to read 0 bytes, got %Id\n",readcount); /* pseudo handle for current process*/ memset(buffer, 0, 12); status = pNtReadVirtualMemory((HANDLE)-1, teststring, buffer, 12, &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == 12, "Expected to read 12 bytes, got %ld\n",readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == 12, "Expected to read 12 bytes, got %Id\n",readcount); ok( strcmp(teststring, buffer) == 0, "Expected read memory to be the same as original memory\n"); /* illegal local address */ status = pNtReadVirtualMemory(process, teststring, (void *)0x1234, 12, &readcount); - ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08x\n", status); - ok( readcount == 0, "Expected to read 0 bytes, got %ld\n",readcount); + ok( status == STATUS_ACCESS_VIOLATION || broken(status == STATUS_PARTIAL_COPY) /* Win10 */, + "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); + if (status == STATUS_ACCESS_VIOLATION) + ok( readcount == 0, "Expected to read 0 bytes, got %Id\n",readcount); CloseHandle(process); } @@ -1841,20 +2805,36 @@ static void test_mapprotection(void) NTSTATUS status; SIZE_T retlen, count; void (*f)(void); + BOOL reset_flags = FALSE; - if (!pNtClose) { - skip("No NtClose ... Win98\n"); - return; - } /* Switch to being a noexec unaware process */ - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &oldflags, sizeof (oldflags), &flagsize); - if (status == STATUS_INVALID_PARAMETER) { - skip("Invalid Parameter on ProcessExecuteFlags query?\n"); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &oldflags, sizeof (oldflags), &flagsize); + if (status == STATUS_INVALID_PARAMETER) + { + skip("Unable to query process execute flags on this platform\n"); return; } - ok( (status == STATUS_SUCCESS) || (status == STATUS_INVALID_INFO_CLASS), "Expected STATUS_SUCCESS, got %08x\n", status); - status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags) ); - ok( (status == STATUS_SUCCESS) || (status == STATUS_INVALID_INFO_CLASS), "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + if (winetest_debug > 1) trace("Process execute flags %08lx\n", oldflags); + + if (!(oldflags & MEM_EXECUTE_OPTION_ENABLE)) + { + if (oldflags & MEM_EXECUTE_OPTION_PERMANENT) + { + skip("Unable to turn off noexec\n"); + return; + } + + if (pGetSystemDEPPolicy && pGetSystemDEPPolicy() == AlwaysOn) + { + skip("System policy requires noexec\n"); + return; + } + + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &flags, sizeof(flags) ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + reset_flags = TRUE; + } size.u.LowPart = 0x2000; size.u.HighPart = 0; @@ -1866,14 +2846,14 @@ static void test_mapprotection(void) SEC_COMMIT | SEC_NOCACHE, 0 ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); offset.u.LowPart = 0; offset.u.HighPart = 0; count = 0x2000; addr = NULL; status = pNtMapViewOfSection ( h, GetCurrentProcess(), &addr, 0, 0, &offset, &count, ViewShare, 0, PAGE_READWRITE); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); #if defined(__x86_64__) || defined(__i386__) *(unsigned char*)addr = 0xc3; /* lret ... in both i386 and x86_64 */ @@ -1884,161 +2864,269 @@ static void test_mapprotection(void) #else ok(0, "Add a return opcode for your architecture or expect a crash in this test\n"); #endif - trace("trying to execute code in the readwrite only mapped anon file...\n"); + if (winetest_debug > 1) trace("trying to execute code in the readwrite only mapped anon file...\n"); f = addr;f(); - trace("...done.\n"); + if (winetest_debug > 1) trace("...done.\n"); status = pNtQueryVirtualMemory( GetCurrentProcess(), addr, MemoryBasicInformation, &info, sizeof(info), &retlen ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( retlen == sizeof(info), "Expected STATUS_SUCCESS, got %08x\n", status); - ok((info.Protect & ~PAGE_NOCACHE) == PAGE_READWRITE, "addr.Protect is not PAGE_READWRITE, but 0x%x\n", info.Protect); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( retlen == sizeof(info), "Expected STATUS_SUCCESS, got %08lx\n", status); + ok((info.Protect & ~PAGE_NOCACHE) == PAGE_READWRITE, "addr.Protect is not PAGE_READWRITE, but 0x%lx\n", info.Protect); status = pNtUnmapViewOfSection( GetCurrentProcess(), (char *)addr + 0x1050 ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); pNtClose (h); - /* Switch back */ - pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &oldflags, sizeof(oldflags) ); + if (reset_flags) + pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &oldflags, sizeof(oldflags) ); +} + +static void test_threadstack(void) +{ + PROCESS_STACK_ALLOCATION_INFORMATION info = { 0x100000, 0, (void *)0xdeadbeef }; + PROCESS_STACK_ALLOCATION_INFORMATION_EX info_ex = { 0 }; + MEMORY_BASIC_INFORMATION meminfo; + SIZE_T retlen; + NTSTATUS status; + + info.ReserveSize = 0x100000; + info.StackBase = (void *)0xdeadbeef; + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, &info, sizeof(info) ); + ok( !status, "NtSetInformationProcess failed %08lx\n", status ); + ok( info.StackBase != (void *)0xdeadbeef, "stackbase not set\n" ); + + status = pNtQueryVirtualMemory( GetCurrentProcess(), info.StackBase, MemoryBasicInformation, + &meminfo, sizeof(meminfo), &retlen ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( retlen == sizeof(meminfo), "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( meminfo.AllocationBase == info.StackBase, "wrong base %p/%p\n", + meminfo.AllocationBase, info.StackBase ); + ok( meminfo.RegionSize == info.ReserveSize, "wrong size %Ix/%Ix\n", + meminfo.RegionSize, info.ReserveSize ); + ok( meminfo.State == MEM_RESERVE, "wrong state %lx\n", meminfo.State ); + ok( meminfo.Protect == 0, "wrong protect %lx\n", meminfo.Protect ); + ok( meminfo.Type == MEM_PRIVATE, "wrong type %lx\n", meminfo.Type ); + + info_ex.AllocInfo = info; + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, + &info_ex, sizeof(info_ex) ); + if (status != STATUS_INVALID_PARAMETER) + { + ok( !status, "NtSetInformationProcess failed %08lx\n", status ); + ok( info_ex.AllocInfo.StackBase != info.StackBase, "stackbase not set\n" ); + status = pNtQueryVirtualMemory( GetCurrentProcess(), info_ex.AllocInfo.StackBase, + MemoryBasicInformation, &meminfo, sizeof(meminfo), &retlen ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( retlen == sizeof(meminfo), "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( meminfo.AllocationBase == info_ex.AllocInfo.StackBase, "wrong base %p/%p\n", + meminfo.AllocationBase, info_ex.AllocInfo.StackBase ); + ok( meminfo.RegionSize == info_ex.AllocInfo.ReserveSize, "wrong size %Ix/%Ix\n", + meminfo.RegionSize, info_ex.AllocInfo.ReserveSize ); + ok( meminfo.State == MEM_RESERVE, "wrong state %lx\n", meminfo.State ); + ok( meminfo.Protect == 0, "wrong protect %lx\n", meminfo.Protect ); + ok( meminfo.Type == MEM_PRIVATE, "wrong type %lx\n", meminfo.Type ); + VirtualFree( info_ex.AllocInfo.StackBase, 0, MEM_FREE ); + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, + &info, sizeof(info) - 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %08lx\n", status ); + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, + &info, sizeof(info) + 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %08lx\n", status ); + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, + &info_ex, sizeof(info_ex) - 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %08lx\n", status ); + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation, + &info_ex, sizeof(info_ex) + 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %08lx\n", status ); + } + else win_skip( "ProcessThreadStackAllocation ex not supported\n" ); + + VirtualFree( info.StackBase, 0, MEM_FREE ); } static void test_queryvirtualmemory(void) { NTSTATUS status; - SIZE_T readcount; - static const WCHAR windowsW[] = {'w','i','n','d','o','w','s'}; + SIZE_T readcount, prev; static const char teststring[] = "test string"; static char datatestbuf[42] = "abc"; static char rwtestbuf[42]; MEMORY_BASIC_INFORMATION mbi; char stackbuf[42]; HMODULE module; - char buffer_name[sizeof(MEMORY_SECTION_NAME) + MAX_PATH * sizeof(WCHAR)]; -#ifndef __REACTOS__ - MEMORY_SECTION_NAME *msn = (MEMORY_SECTION_NAME *)buffer_name; -#endif - BOOL found; - int i; -#ifdef __REACTOS__ - MEMORY_SECTION_NAME *msn = HeapAlloc(GetProcessHeap(), 0, sizeof(buffer_name)); -#endif + void *user_shared_data = (void *)0x7ffe0000; + void *buffer[256]; + MEMORY_SECTION_NAME *name = (MEMORY_SECTION_NAME *)buffer; + SYSTEM_BASIC_INFORMATION sbi; module = GetModuleHandleA( "ntdll.dll" ); - trace("Check flags of the PE header of NTDLL.DLL at %p\n", module); status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); ok (mbi.AllocationBase == module, "mbi.AllocationBase is 0x%p, expected 0x%p\n", mbi.AllocationBase, module); - ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%x\n", mbi.State, MEM_COMMIT); - ok (mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%x, expected 0x%x\n", mbi.Protect, PAGE_READONLY); - ok (mbi.Type == MEM_IMAGE, "mbi.Type is 0x%x, expected 0x%x\n", mbi.Type, MEM_IMAGE); + ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%x\n", mbi.State, MEM_COMMIT); + ok (mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%lx, expected 0x%x\n", mbi.Protect, PAGE_READONLY); + ok (mbi.Type == MEM_IMAGE, "mbi.Type is 0x%lx, expected 0x%x\n", mbi.Type, MEM_IMAGE); - trace("Check flags of a function entry in NTDLL.DLL at %p\n", pNtQueryVirtualMemory); module = GetModuleHandleA( "ntdll.dll" ); status = pNtQueryVirtualMemory(NtCurrentProcess(), pNtQueryVirtualMemory, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); ok (mbi.AllocationBase == module, "mbi.AllocationBase is 0x%p, expected 0x%p\n", mbi.AllocationBase, module); - ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%x\n", mbi.State, MEM_COMMIT); - ok (mbi.Protect == PAGE_EXECUTE_READ, "mbi.Protect is 0x%x, expected 0x%x\n", mbi.Protect, PAGE_EXECUTE_READ); + ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%x\n", mbi.State, MEM_COMMIT); + ok (mbi.Protect == PAGE_EXECUTE_READ, "mbi.Protect is 0x%lx, expected 0x%x\n", mbi.Protect, PAGE_EXECUTE_READ); - trace("Check flags of heap at %p\n", GetProcessHeap()); status = pNtQueryVirtualMemory(NtCurrentProcess(), GetProcessHeap(), MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); ok (mbi.AllocationProtect == PAGE_READWRITE || mbi.AllocationProtect == PAGE_EXECUTE_READWRITE, - "mbi.AllocationProtect is 0x%x\n", mbi.AllocationProtect); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%x\n", mbi.State, MEM_COMMIT); + "mbi.AllocationProtect is 0x%lx\n", mbi.AllocationProtect); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%x\n", mbi.State, MEM_COMMIT); ok (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_EXECUTE_READWRITE, - "mbi.Protect is 0x%x\n", mbi.Protect); + "mbi.Protect is 0x%lx\n", mbi.Protect); - trace("Check flags of stack at %p\n", stackbuf); status = pNtQueryVirtualMemory(NtCurrentProcess(), stackbuf, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); - ok (mbi.AllocationProtect == PAGE_READWRITE, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_READWRITE); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%x\n", mbi.State, MEM_COMMIT); - ok (mbi.Protect == PAGE_READWRITE, "mbi.Protect is 0x%x, expected 0x%x\n", mbi.Protect, PAGE_READWRITE); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok (mbi.AllocationProtect == PAGE_READWRITE, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_READWRITE); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%x\n", mbi.State, MEM_COMMIT); + ok (mbi.Protect == PAGE_READWRITE, "mbi.Protect is 0x%lx, expected 0x%x\n", mbi.Protect, PAGE_READWRITE); - trace("Check flags of read-only data at %p\n", teststring); module = GetModuleHandleA( NULL ); status = pNtQueryVirtualMemory(NtCurrentProcess(), teststring, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); ok (mbi.AllocationBase == module, "mbi.AllocationBase is 0x%p, expected 0x%p\n", mbi.AllocationBase, module); - ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%X\n", mbi.State, MEM_COMMIT); - if (mbi.Protect != PAGE_READONLY) - todo_wine ok( mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%x, expected 0x%X\n", mbi.Protect, PAGE_READONLY); + ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%X\n", mbi.State, MEM_COMMIT); + ok (mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%lx, expected 0x%X\n", mbi.Protect, PAGE_READONLY); - trace("Check flags of read-write data at %p\n", datatestbuf); status = pNtQueryVirtualMemory(NtCurrentProcess(), datatestbuf, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); ok (mbi.AllocationBase == module, "mbi.AllocationBase is 0x%p, expected 0x%p\n", mbi.AllocationBase, module); - ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%X\n", mbi.State, MEM_COMMIT); + ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%X\n", mbi.State, MEM_COMMIT); ok (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_WRITECOPY, - "mbi.Protect is 0x%x\n", mbi.Protect); + "mbi.Protect is 0x%lx\n", mbi.Protect); - trace("Check flags of read-write uninitialized data (.bss) at %p\n", rwtestbuf); status = pNtQueryVirtualMemory(NtCurrentProcess(), rwtestbuf, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %ld\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); if (mbi.AllocationBase == module) { - ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%x, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); - ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%x, expected 0x%X\n", mbi.State, MEM_COMMIT); + ok (mbi.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_EXECUTE_WRITECOPY); + ok (mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%X\n", mbi.State, MEM_COMMIT); ok (mbi.Protect == PAGE_READWRITE || mbi.Protect == PAGE_WRITECOPY, - "mbi.Protect is 0x%x\n", mbi.Protect); + "mbi.Protect is 0x%lx\n", mbi.Protect); } else skip( "bss is outside of module\n" ); /* this can happen on Mac OS */ - /* check error code when addr is higher than working set limit */ + status = pNtQueryVirtualMemory(NtCurrentProcess(), user_shared_data, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &readcount); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok(readcount == sizeof(MEMORY_BASIC_INFORMATION), "Expected to read %d bytes, got %Id\n",(int)sizeof(MEMORY_BASIC_INFORMATION),readcount); + ok(mbi.AllocationBase == user_shared_data, "mbi.AllocationBase is 0x%p, expected 0x%p\n", mbi.AllocationBase, user_shared_data); + ok(mbi.AllocationProtect == PAGE_READONLY, "mbi.AllocationProtect is 0x%lx, expected 0x%x\n", mbi.AllocationProtect, PAGE_READONLY); + ok(mbi.State == MEM_COMMIT, "mbi.State is 0x%lx, expected 0x%X\n", mbi.State, MEM_COMMIT); + ok(mbi.Protect == PAGE_READONLY, "mbi.Protect is 0x%lx\n", mbi.Protect); + ok(mbi.Type == MEM_PRIVATE, "mbi.Type is 0x%lx, expected 0x%x\n", mbi.Type, MEM_PRIVATE); + ok(mbi.RegionSize == 0x1000, "mbi.RegionSize is 0x%Ix, expected 0x%x\n", mbi.RegionSize, 0x1000); + + /* check error code when addr is higher than user space limit */ + status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + status = pNtQueryVirtualMemory(NtCurrentProcess(), sbi.LowestUserAddress, MemoryBasicInformation, &mbi, sizeof(mbi), &readcount); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)sbi.LowestUserAddress-1, MemoryBasicInformation, &mbi, sizeof(mbi), &readcount); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + status = pNtQueryVirtualMemory(NtCurrentProcess(), sbi.HighestUserAddress, MemoryBasicInformation, &mbi, sizeof(mbi), &readcount); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)sbi.HighestUserAddress+1, MemoryBasicInformation, &mbi, sizeof(mbi), &readcount); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); status = pNtQueryVirtualMemory(NtCurrentProcess(), (void *)~0, MemoryBasicInformation, &mbi, sizeof(mbi), &readcount); - ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); + + /* check error code when len is less than MEMORY_BASIC_INFORMATION size */ + status = pNtQueryVirtualMemory(NtCurrentProcess(), GetProcessHeap(), MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION) - 1, &readcount); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status); - trace("Check section name of NTDLL.DLL with invalid size\n"); module = GetModuleHandleA( "ntdll.dll" ); - memset(msn, 0, sizeof(*msn)); - readcount = 0; - status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryMappedFilenameInformation, msn, sizeof(*msn), &readcount); - ok( status == STATUS_BUFFER_OVERFLOW, "Expected STATUS_BUFFER_OVERFLOW, got %08x\n", status); - ok( readcount > 0, "Expected readcount to be > 0\n"); + memset(buffer, 0xcc, sizeof(buffer)); + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryMappedFilenameInformation, + name, sizeof(*name) + 16, &readcount); + ok(status == STATUS_BUFFER_OVERFLOW, "got %08lx\n", status); + ok(name->SectionFileName.Length == 0xcccc || broken(!name->SectionFileName.Length), /* vista64 */ + "Wrong len %u\n", name->SectionFileName.Length); + ok(readcount > sizeof(*name), "Wrong count %Iu\n", readcount); - trace("Check section name of NTDLL.DLL with invalid size\n"); - module = GetModuleHandleA( "ntdll.dll" ); - memset(msn, 0, sizeof(*msn)); - readcount = 0; - status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryMappedFilenameInformation, msn, sizeof(*msn) - 1, &readcount); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); - ok( readcount > 0, "Expected readcount to be > 0\n"); + memset(buffer, 0xcc, sizeof(buffer)); + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)module + 1234, MemoryMappedFilenameInformation, + name, sizeof(buffer), &readcount); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(name->SectionFileName.Buffer == (WCHAR *)(name + 1), "Wrong ptr %p/%p\n", + name->SectionFileName.Buffer, name + 1 ); + ok(name->SectionFileName.Length != 0xcccc, "Wrong len %u\n", name->SectionFileName.Length); + ok(name->SectionFileName.MaximumLength == name->SectionFileName.Length + sizeof(WCHAR), + "Wrong maxlen %u/%u\n", name->SectionFileName.MaximumLength, name->SectionFileName.Length); + ok(readcount == sizeof(name->SectionFileName) + name->SectionFileName.MaximumLength, + "Wrong count %Iu/%u\n", readcount, name->SectionFileName.MaximumLength); + ok( !name->SectionFileName.Buffer[name->SectionFileName.Length / sizeof(WCHAR)], + "buffer not null-terminated\n" ); - trace("Check section name of NTDLL.DLL\n"); - module = GetModuleHandleA( "ntdll.dll" ); - memset(msn, 0x55, sizeof(*msn)); - memset(buffer_name, 0x77, sizeof(buffer_name)); - readcount = 0; - status = pNtQueryVirtualMemory(NtCurrentProcess(), module, MemoryMappedFilenameInformation, msn, sizeof(buffer_name), &readcount); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( readcount > 0, "Expected readcount to be > 0\n"); - trace ("Section Name: %s\n", wine_dbgstr_w(msn->SectionFileName.Buffer)); - pRtlDowncaseUnicodeString( &msn->SectionFileName, &msn->SectionFileName, FALSE ); - for (found = FALSE, i = (msn->SectionFileName.Length - sizeof(windowsW)) / sizeof(WCHAR); i >= 0; i--) - found |= !memcmp( &msn->SectionFileName.Buffer[i], windowsW, sizeof(windowsW) ); - ok( found, "Section name does not contain \"Windows\"\n"); + memset(buffer, 0xcc, sizeof(buffer)); + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)module + 1234, MemoryMappedFilenameInformation, + name, sizeof(buffer), NULL); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); - trace("Check section name of non mapped memory\n"); - memset(msn, 0, sizeof(buffer_name)); - readcount = 0; - status = pNtQueryVirtualMemory(NtCurrentProcess(), &buffer_name, MemoryMappedFilenameInformation, msn, sizeof(buffer_name), &readcount); - ok( status == STATUS_INVALID_ADDRESS, "Expected STATUS_INVALID_ADDRESS, got %08x\n", status); - ok( readcount == 0 || broken(readcount != 0) /* wow64 */, "Expected readcount to be 0\n"); + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)module + 1234, MemoryMappedFilenameInformation, + NULL, sizeof(buffer), NULL); + ok(status == STATUS_ACCESS_VIOLATION, "got %08lx\n", status); -#ifdef __REACTOS__ - HeapFree(GetProcessHeap(), 0, msn); -#endif + memset(buffer, 0xcc, sizeof(buffer)); + prev = readcount; + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), (char *)module + 321, MemoryMappedFilenameInformation, + name, sizeof(*name) - 1, &readcount); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "got %08lx\n", status); + ok(name->SectionFileName.Length == 0xcccc, "Wrong len %u\n", name->SectionFileName.Length); + ok(readcount == prev, "Wrong count %Iu\n", readcount); + + memset(buffer, 0xcc, sizeof(buffer)); + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory((HANDLE)0xdead, (char *)module + 1234, MemoryMappedFilenameInformation, + name, sizeof(buffer), &readcount); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + ok(readcount == 0xdeadbeef || broken(readcount == 1024 + sizeof(*name)), /* wow64 */ + "Wrong count %Iu\n", readcount); + + memset(buffer, 0xcc, sizeof(buffer)); + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), buffer, MemoryMappedFilenameInformation, + name, sizeof(buffer), &readcount); + ok(status == STATUS_INVALID_ADDRESS, "got %08lx\n", status); + ok(name->SectionFileName.Length == 0xcccc, "Wrong len %u\n", name->SectionFileName.Length); + ok(readcount == 0xdeadbeef || broken(readcount == 1024 + sizeof(*name)), /* wow64 */ + "Wrong count %Iu\n", readcount); + + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), (void *)0x1234, MemoryMappedFilenameInformation, + name, sizeof(buffer), &readcount); + ok(status == STATUS_INVALID_ADDRESS, "got %08lx\n", status); + ok(name->SectionFileName.Length == 0xcccc, "Wrong len %u\n", name->SectionFileName.Length); + ok(readcount == 0xdeadbeef || broken(readcount == 1024 + sizeof(*name)), /* wow64 */ + "Wrong count %Iu\n", readcount); + + readcount = 0xdeadbeef; + status = pNtQueryVirtualMemory(NtCurrentProcess(), (void *)0x1234, MemoryMappedFilenameInformation, + name, sizeof(*name) - 1, &readcount); + ok(status == STATUS_INVALID_ADDRESS, "got %08lx\n", status); + ok(name->SectionFileName.Length == 0xcccc, "Wrong len %u\n", name->SectionFileName.Length); + ok(readcount == 0xdeadbeef || broken(readcount == 15), /* wow64 */ + "Wrong count %Iu\n", readcount); } static void test_affinity(void) @@ -2050,44 +3138,70 @@ static void test_affinity(void) SYSTEM_INFO si; GetSystemInfo(&si); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); proc_affinity = pbi.AffinityMask; - ok( proc_affinity == (1 << si.dwNumberOfProcessors) - 1, "Unexpected process affinity\n" ); - proc_affinity = 1 << si.dwNumberOfProcessors; - status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) ); - ok( status == STATUS_INVALID_PARAMETER, - "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); - + ok( proc_affinity == get_affinity_mask( si.dwNumberOfProcessors ), "Unexpected process affinity\n" ); + if (si.dwNumberOfProcessors < 8 * sizeof(DWORD_PTR)) + { + proc_affinity = (DWORD_PTR)1 << si.dwNumberOfProcessors; + status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) ); + ok( status == STATUS_INVALID_PARAMETER, + "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); + } proc_affinity = 0; status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) ); ok( status == STATUS_INVALID_PARAMETER, - "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); + "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( tbi.AffinityMask == (1 << si.dwNumberOfProcessors) - 1, "Unexpected thread affinity\n" ); - thread_affinity = 1 << si.dwNumberOfProcessors; - status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); - ok( status == STATUS_INVALID_PARAMETER, - "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( tbi.AffinityMask == get_affinity_mask( si.dwNumberOfProcessors ), "Unexpected thread affinity\n" ); + if (si.dwNumberOfProcessors < 8 * sizeof(DWORD_PTR)) + { + thread_affinity = (DWORD_PTR)1 << si.dwNumberOfProcessors; + status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); + ok( status == STATUS_INVALID_PARAMETER, + "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); + } thread_affinity = 0; status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); ok( status == STATUS_INVALID_PARAMETER, - "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); + "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); thread_affinity = 1; status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); ok( tbi.AffinityMask == 1, "Unexpected thread affinity\n" ); + /* NOTE: Pre-Vista does not allow bits to be set that are higher than the highest set bit in process affinity mask */ + thread_affinity = (pbi.AffinityMask << 1) | pbi.AffinityMask; + status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); + ok( broken(status == STATUS_INVALID_PARAMETER) || (status == STATUS_SUCCESS), "Expected STATUS_SUCCESS, got %08lx\n", status ); + if (status == STATUS_SUCCESS) + { + status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + ok( tbi.AffinityMask == pbi.AffinityMask, "Unexpected thread affinity. Expected %Ix, got %Ix\n", pbi.AffinityMask, tbi.AffinityMask ); + } + + thread_affinity = ~(DWORD_PTR)0 - 1; + status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); + ok( broken(status == STATUS_INVALID_PARAMETER) || (status == STATUS_SUCCESS), "Expected STATUS_SUCCESS, got %08lx\n", status ); + if (status == STATUS_SUCCESS) + { + status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + ok( tbi.AffinityMask == (pbi.AffinityMask & (~(DWORD_PTR)0 - 1)), "Unexpected thread affinity. Expected %Ix, got %Ix\n", pbi.AffinityMask & (~(DWORD_PTR)0 - 1), tbi.AffinityMask ); + } + /* NOTE: Pre-Vista does not recognize the "all processors" flag (all bits set) */ thread_affinity = ~(DWORD_PTR)0; status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); ok( broken(status == STATUS_INVALID_PARAMETER) || status == STATUS_SUCCESS, - "Expected STATUS_SUCCESS, got %08x\n", status); + "Expected STATUS_SUCCESS, got %08lx\n", status); if (si.dwNumberOfProcessors <= 1) { @@ -2099,40 +3213,103 @@ static void test_affinity(void) if (status == STATUS_SUCCESS) { status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( broken(tbi.AffinityMask == 1) || tbi.AffinityMask == (1 << si.dwNumberOfProcessors) - 1, - "Unexpected thread affinity\n" ); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( tbi.AffinityMask == get_affinity_mask( si.dwNumberOfProcessors ), "unexpected affinity %#Ix\n", tbi.AffinityMask ); } else skip("Cannot test thread affinity mask for 'all processors' flag\n"); proc_affinity = 2; status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); proc_affinity = pbi.AffinityMask; ok( proc_affinity == 2, "Unexpected process affinity\n" ); /* Setting the process affinity changes the thread affinity to match */ status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); ok( tbi.AffinityMask == 2, "Unexpected thread affinity\n" ); /* The thread affinity is restricted to the process affinity */ thread_affinity = 1; status = pNtSetInformationThread( GetCurrentThread(), ThreadAffinityMask, &thread_affinity, sizeof(thread_affinity) ); ok( status == STATUS_INVALID_PARAMETER, - "Expected STATUS_INVALID_PARAMETER, got %08x\n", status); + "Expected STATUS_INVALID_PARAMETER, got %08lx\n", status); - proc_affinity = (1 << si.dwNumberOfProcessors) - 1; + proc_affinity = get_affinity_mask( si.dwNumberOfProcessors ); status = pNtSetInformationProcess( GetCurrentProcess(), ProcessAffinityMask, &proc_affinity, sizeof(proc_affinity) ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); /* Resetting the process affinity also resets the thread affinity */ status = pNtQueryInformationThread( GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok( tbi.AffinityMask == (1 << si.dwNumberOfProcessors) - 1, + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok( tbi.AffinityMask == get_affinity_mask( si.dwNumberOfProcessors ), "Unexpected thread affinity\n" ); } +static DWORD WINAPI hide_from_debugger_thread(void *arg) +{ + HANDLE stop_event = arg; + WaitForSingleObject( stop_event, INFINITE ); + return 0; +} + +static void test_HideFromDebugger(void) +{ + NTSTATUS status; + HANDLE thread, stop_event; + ULONG dummy; + + dummy = 0; + status = pNtSetInformationThread( GetCurrentThread(), ThreadHideFromDebugger, &dummy, sizeof(ULONG) ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + dummy = 0; + status = pNtSetInformationThread( GetCurrentThread(), ThreadHideFromDebugger, &dummy, 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + status = pNtSetInformationThread( (HANDLE)0xdeadbeef, ThreadHideFromDebugger, NULL, 0 ); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status ); + status = pNtSetInformationThread( GetCurrentThread(), ThreadHideFromDebugger, NULL, 0 ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + dummy = 0; + status = NtQueryInformationThread( GetCurrentThread(), ThreadHideFromDebugger, &dummy, sizeof(ULONG), NULL ); + if (status == STATUS_INVALID_INFO_CLASS) + { + win_skip("ThreadHideFromDebugger not available\n"); + return; + } + + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + dummy = 0; + status = NtQueryInformationThread( (HANDLE)0xdeadbeef, ThreadHideFromDebugger, &dummy, sizeof(ULONG), NULL ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + dummy = 0; + status = NtQueryInformationThread( GetCurrentThread(), ThreadHideFromDebugger, &dummy, 1, NULL ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + ok( dummy == 1, "Expected dummy == 1, got %08lx\n", dummy ); + + stop_event = CreateEventA( NULL, FALSE, FALSE, NULL ); + ok( stop_event != NULL, "CreateEvent failed\n" ); + thread = CreateThread( NULL, 0, hide_from_debugger_thread, stop_event, 0, NULL ); + ok( thread != INVALID_HANDLE_VALUE, "CreateThread failed with %ld\n", GetLastError() ); + + dummy = 0; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 1, NULL ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( dummy == 0, "Expected dummy == 0, got %08lx\n", dummy ); + + status = pNtSetInformationThread( thread, ThreadHideFromDebugger, NULL, 0 ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + + dummy = 0; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 1, NULL ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( dummy == 1, "Expected dummy == 1, got %08lx\n", dummy ); + + SetEvent( stop_event ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( stop_event ); +} + static void test_NtGetCurrentProcessorNumber(void) { NTSTATUS status; @@ -2152,41 +3329,72 @@ static void test_NtGetCurrentProcessorNumber(void) GetSystemInfo(&si); current_cpu = pNtGetCurrentProcessorNumber(); - trace("dwNumberOfProcessors: %d, current processor: %d\n", si.dwNumberOfProcessors, current_cpu); + if (winetest_debug > 1) trace("dwNumberOfProcessors: %ld, current processor: %ld\n", si.dwNumberOfProcessors, current_cpu); - status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL); + status = NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL); old_process_mask = pbi.AffinityMask; - ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status); + ok(status == STATUS_SUCCESS, "got 0x%lx (expected STATUS_SUCCESS)\n", status); status = pNtQueryInformationThread(GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL); old_thread_mask = tbi.AffinityMask; - ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status); + ok(status == STATUS_SUCCESS, "got 0x%lx (expected STATUS_SUCCESS)\n", status); /* allow the test to run on all processors */ - new_mask = (1 << si.dwNumberOfProcessors) - 1; + new_mask = get_affinity_mask( si.dwNumberOfProcessors ); status = pNtSetInformationProcess(GetCurrentProcess(), ProcessAffinityMask, &new_mask, sizeof(new_mask)); - ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status); + ok(status == STATUS_SUCCESS, "got 0x%lx (expected STATUS_SUCCESS)\n", status); for (i = 0; i < si.dwNumberOfProcessors; i++) { - new_mask = 1 << i; + new_mask = (DWORD_PTR)1 << i; status = pNtSetInformationThread(GetCurrentThread(), ThreadAffinityMask, &new_mask, sizeof(new_mask)); - ok(status == STATUS_SUCCESS, "%d: got 0x%x (expected STATUS_SUCCESS)\n", i, status); + ok(status == STATUS_SUCCESS, "%ld: got 0x%lx (expected STATUS_SUCCESS)\n", i, status); status = pNtQueryInformationThread(GetCurrentThread(), ThreadBasicInformation, &tbi, sizeof(tbi), NULL); - ok(status == STATUS_SUCCESS, "%d: got 0x%x (expected STATUS_SUCCESS)\n", i, status); + ok(status == STATUS_SUCCESS, "%ld: got 0x%lx (expected STATUS_SUCCESS)\n", i, status); current_cpu = pNtGetCurrentProcessorNumber(); - ok((current_cpu == i), "%d (new_mask 0x%lx): running on processor %d (AffinityMask: 0x%lx)\n", + ok((current_cpu == i), "%ld (new_mask 0x%Ix): running on processor %ld (AffinityMask: 0x%Ix)\n", i, new_mask, current_cpu, tbi.AffinityMask); } /* restore old values */ status = pNtSetInformationProcess(GetCurrentProcess(), ProcessAffinityMask, &old_process_mask, sizeof(old_process_mask)); - ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status); + ok(status == STATUS_SUCCESS, "got 0x%lx (expected STATUS_SUCCESS)\n", status); status = pNtSetInformationThread(GetCurrentThread(), ThreadAffinityMask, &old_thread_mask, sizeof(old_thread_mask)); - ok(status == STATUS_SUCCESS, "got 0x%x (expected STATUS_SUCCESS)\n", status); + ok(status == STATUS_SUCCESS, "got 0x%lx (expected STATUS_SUCCESS)\n", status); +} + +static void test_ThreadEnableAlignmentFaultFixup(void) +{ + NTSTATUS status; + ULONG dummy; + + dummy = 0; + status = NtQueryInformationThread( GetCurrentThread(), ThreadEnableAlignmentFaultFixup, &dummy, sizeof(ULONG), NULL ); + ok( status == STATUS_INVALID_INFO_CLASS, "Expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status ); + status = NtQueryInformationThread( GetCurrentThread(), ThreadEnableAlignmentFaultFixup, &dummy, 1, NULL ); + ok( status == STATUS_INVALID_INFO_CLASS, "Expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status ); + + dummy = 1; + status = pNtSetInformationThread( GetCurrentThread(), ThreadEnableAlignmentFaultFixup, &dummy, sizeof(ULONG) ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + status = pNtSetInformationThread( (HANDLE)0xdeadbeef, ThreadEnableAlignmentFaultFixup, NULL, 0 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + status = pNtSetInformationThread( (HANDLE)0xdeadbeef, ThreadEnableAlignmentFaultFixup, NULL, 1 ); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status ); + status = pNtSetInformationThread( (HANDLE)0xdeadbeef, ThreadEnableAlignmentFaultFixup, &dummy, 1 ); + todo_wine ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status ); + status = pNtSetInformationThread( GetCurrentProcess(), ThreadEnableAlignmentFaultFixup, &dummy, 1 ); + todo_wine ok( status == STATUS_OBJECT_TYPE_MISMATCH, "Expected STATUS_OBJECT_TYPE_MISMATCH, got %08lx\n", status ); + dummy = 1; + status = pNtSetInformationThread( GetCurrentThread(), ThreadEnableAlignmentFaultFixup, &dummy, 1 ); + ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status ); + + dummy = 0; + status = pNtSetInformationThread( GetCurrentProcess(), ThreadEnableAlignmentFaultFixup, &dummy, 8 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); } static DWORD WINAPI start_address_thread(void *arg) @@ -2199,8 +3407,8 @@ static DWORD WINAPI start_address_thread(void *arg) ret = 0xdeadbeef; status = pNtQueryInformationThread(GetCurrentThread(), ThreadQuerySetWin32StartAddress, &entry, sizeof(entry), &ret); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok(ret == sizeof(entry), "NtQueryInformationThread returned %u bytes\n", ret); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(ret == sizeof(entry), "NtQueryInformationThread returned %lu bytes\n", ret); ok(entry == (void *)start_address_thread, "expected %p, got %p\n", start_address_thread, entry); return 0; } @@ -2223,8 +3431,8 @@ static void test_thread_start_address(void) ret = 0xdeadbeef; status = pNtQueryInformationThread(GetCurrentThread(), ThreadQuerySetWin32StartAddress, &entry, sizeof(entry), &ret); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok(ret == sizeof(entry), "NtQueryInformationThread returned %u bytes\n", ret); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(ret == sizeof(entry), "NtQueryInformationThread returned %lu bytes\n", ret); expected_entry = (void *)((char *)module + nt->OptionalHeader.AddressOfEntryPoint); ok(entry == expected_entry, "expected %p, got %p\n", expected_entry, entry); @@ -2232,7 +3440,7 @@ static void test_thread_start_address(void) status = pNtSetInformationThread(GetCurrentThread(), ThreadQuerySetWin32StartAddress, &entry, sizeof(entry)); ok(status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, /* >= Vista */ - "expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08x\n", status); + "expected STATUS_SUCCESS or STATUS_INVALID_PARAMETER, got %08lx\n", status); if (status == STATUS_SUCCESS) { @@ -2240,29 +3448,650 @@ static void test_thread_start_address(void) ret = 0xdeadbeef; status = pNtQueryInformationThread(GetCurrentThread(), ThreadQuerySetWin32StartAddress, &entry, sizeof(entry), &ret); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok(ret == sizeof(entry), "NtQueryInformationThread returned %u bytes\n", ret); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(ret == sizeof(entry), "NtQueryInformationThread returned %lu bytes\n", ret); ok(entry == (void *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", entry); } thread = CreateThread(NULL, 0, start_address_thread, NULL, 0, NULL); - ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError()); + ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %ld\n", GetLastError()); ret = WaitForSingleObject(thread, 1000); - ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", ret); + ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %lu\n", ret); CloseHandle(thread); } static void test_query_data_alignment(void) { - ULONG ReturnLength; + SYSTEM_CPU_INFORMATION sci; + ULONG len; NTSTATUS status; DWORD value; value = 0xdeadbeef; - status = pNtQuerySystemInformation(SystemRecommendedSharedDataAlignment, &value, sizeof(value), &ReturnLength); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - ok(sizeof(value) == ReturnLength, "Inconsistent length %u\n", ReturnLength); - ok(value == 64, "Expected 64, got %u\n", value); + status = pNtQuerySystemInformation(SystemRecommendedSharedDataAlignment, &value, sizeof(value), &len); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status); + ok(sizeof(value) == len, "Inconsistent length %lu\n", len); + + pRtlGetNativeSystemInformation(SystemCpuInformation, &sci, sizeof(sci), &len); + switch (sci.ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_ARM: + ok(value == 32, "Expected 32, got %lu\n", value); + break; + case PROCESSOR_ARCHITECTURE_ARM64: + ok(value == 128, "Expected 128, got %lu\n", value); + break; + default: + ok(value == 64, "Expected 64, got %lu\n", value); + break; + } +} + +static void test_thread_lookup(void) +{ + OBJECT_BASIC_INFORMATION obj_info; + THREAD_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; + HANDLE handle; + NTSTATUS status; + + InitializeObjectAttributes( &attr, NULL, 0, NULL, NULL ); + cid.UniqueProcess = ULongToHandle(GetCurrentProcessId()); + cid.UniqueThread = ULongToHandle(GetCurrentThreadId()); + status = pNtOpenThread(&handle, THREAD_QUERY_INFORMATION, &attr, &cid); + ok(!status, "NtOpenThread returned %#lx\n", status); + status = pNtOpenThread((HANDLE *)0xdeadbee0, THREAD_QUERY_INFORMATION, &attr, &cid); + ok( status == STATUS_ACCESS_VIOLATION, "NtOpenThread returned %#lx\n", status); + + status = pNtQueryObject(handle, ObjectBasicInformation, &obj_info, sizeof(obj_info), NULL); + ok(!status, "NtQueryObject returned: %#lx\n", status); + ok(obj_info.GrantedAccess == (THREAD_QUERY_LIMITED_INFORMATION | THREAD_QUERY_INFORMATION) + || broken(obj_info.GrantedAccess == THREAD_QUERY_INFORMATION), /* winxp */ + "GrantedAccess = %lx\n", obj_info.GrantedAccess); + + status = pNtQueryInformationThread(handle, ThreadBasicInformation, &info, sizeof(info), NULL); + ok(!status, "NtQueryInformationThread returned %#lx\n", status); + ok(info.ClientId.UniqueProcess == ULongToHandle(GetCurrentProcessId()), + "UniqueProcess = %p expected %lx\n", info.ClientId.UniqueProcess, GetCurrentProcessId()); + ok(info.ClientId.UniqueThread == ULongToHandle(GetCurrentThreadId()), + "UniqueThread = %p expected %lx\n", info.ClientId.UniqueThread, GetCurrentThreadId()); + pNtClose(handle); + + cid.UniqueProcess = 0; + cid.UniqueThread = ULongToHandle(GetCurrentThreadId()); + status = pNtOpenThread(&handle, THREAD_QUERY_INFORMATION, &attr, &cid); + ok(!status, "NtOpenThread returned %#lx\n", status); + status = pNtQueryInformationThread(handle, ThreadBasicInformation, &info, sizeof(info), NULL); + ok(!status, "NtQueryInformationThread returned %#lx\n", status); + ok(info.ClientId.UniqueProcess == ULongToHandle(GetCurrentProcessId()), + "UniqueProcess = %p expected %lx\n", info.ClientId.UniqueProcess, GetCurrentProcessId()); + ok(info.ClientId.UniqueThread == ULongToHandle(GetCurrentThreadId()), + "UniqueThread = %p expected %lx\n", info.ClientId.UniqueThread, GetCurrentThreadId()); + pNtClose(handle); + + cid.UniqueProcess = ULongToHandle(0xdeadbeef); + cid.UniqueThread = ULongToHandle(GetCurrentThreadId()); + handle = (HANDLE)0xdeadbeef; + status = NtOpenThread(&handle, THREAD_QUERY_INFORMATION, &attr, &cid); + todo_wine + ok(status == STATUS_INVALID_CID, "NtOpenThread returned %#lx\n", status); + todo_wine + ok( !handle || broken(handle == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle ); + if (!status) pNtClose(handle); + + cid.UniqueProcess = 0; + cid.UniqueThread = ULongToHandle(0xdeadbeef); + handle = (HANDLE)0xdeadbeef; + status = pNtOpenThread(&handle, THREAD_QUERY_INFORMATION, &attr, &cid); + ok(status == STATUS_INVALID_CID || broken(status == STATUS_INVALID_PARAMETER) /* winxp */, + "NtOpenThread returned %#lx\n", status); + ok( !handle || broken(handle == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle ); +} + +static void test_thread_ideal_processor(void) +{ + ULONG number, len; + PROCESSOR_NUMBER processor; + NTSTATUS status; + + number = 0; + status = pNtSetInformationThread( GetCurrentThread(), ThreadIdealProcessor, &number, sizeof(number) ); + ok(NT_SUCCESS(status), "Unexpected status %#lx.\n", status); + + number = 64 + 1; + status = pNtSetInformationThread( GetCurrentThread(), ThreadIdealProcessor, &number, sizeof(number) ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); + + number = 0; + status = pNtSetInformationThread( GetCurrentThread(), ThreadIdealProcessor, &number, sizeof(number) ); + ok(!status, "Unexpected status %#lx.\n", status); + + status = pNtQueryInformationThread( GetCurrentThread(), ThreadIdealProcessor, &number, sizeof(number), &len ); + ok(status == STATUS_INVALID_INFO_CLASS, "Unexpected status %#lx.\n", status); + + status = pNtQueryInformationThread( GetCurrentThread(), ThreadIdealProcessorEx, &processor, sizeof(processor) + 1, &len ); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status); + + status = pNtQueryInformationThread( GetCurrentThread(), ThreadIdealProcessorEx, &processor, sizeof(processor), &len ); + ok(status == STATUS_SUCCESS, "Unexpected status %#lx.\n", status); +} + +static void test_thread_info(void) +{ + NTSTATUS status; + ULONG len, data; + + len = 0xdeadbeef; + data = 0xcccccccc; + status = pNtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread, + &data, sizeof(data), &len ); + ok( !status, "failed %lx\n", status ); + ok( data == 0 || data == 1, "wrong data %lx\n", data ); + ok( len == sizeof(data), "wrong len %lu\n", len ); + + len = 0xdeadbeef; + data = 0xcccccccc; + status = pNtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread, + &data, sizeof(data) - 1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "failed %lx\n", status ); + ok( data == 0xcccccccc, "wrong data %lx\n", data ); + ok( len == 0xdeadbeef, "wrong len %lu\n", len ); + + len = 0xdeadbeef; + data = 0xcccccccc; + status = pNtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread, + &data, sizeof(data) + 1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "failed %lx\n", status ); + ok( data == 0xcccccccc, "wrong data %lx\n", data ); + ok( len == 0xdeadbeef, "wrong len %lu\n", len ); +} + +static void test_debug_object(void) +{ + NTSTATUS status; + HANDLE handle; + OBJECT_ATTRIBUTES attr = { sizeof(attr) }; + ULONG len, flag = 0; + DBGUI_WAIT_STATE_CHANGE state; + DEBUG_EVENT event; + + status = pNtCreateDebugObject( &handle, DEBUG_ALL_ACCESS, &attr, 0 ); + ok( !status, "NtCreateDebugObject failed %lx\n", status ); + status = pNtSetInformationDebugObject( handle, 0, &flag, sizeof(ULONG), &len ); + ok( status == STATUS_INVALID_PARAMETER, "NtSetInformationDebugObject failed %lx\n", status ); + status = pNtSetInformationDebugObject( handle, 2, &flag, sizeof(ULONG), &len ); + ok( status == STATUS_INVALID_PARAMETER, "NtSetInformationDebugObject failed %lx\n", status ); + status = pNtSetInformationDebugObject( (HANDLE)0xdead, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG), &len ); + ok( status == STATUS_INVALID_HANDLE, "NtSetInformationDebugObject failed %lx\n", status ); + + len = 0xdead; + status = pNtSetInformationDebugObject( handle, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG) + 1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationDebugObject failed %lx\n", status ); + ok( len == sizeof(ULONG), "wrong len %lu\n", len ); + + len = 0xdead; + status = pNtSetInformationDebugObject( handle, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG) - 1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationDebugObject failed %lx\n", status ); + ok( len == sizeof(ULONG), "wrong len %lu\n", len ); + + len = 0xdead; + status = pNtSetInformationDebugObject( handle, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG), &len ); + ok( !status, "NtSetInformationDebugObject failed %lx\n", status ); + ok( !len, "wrong len %lu\n", len ); + + flag = DEBUG_KILL_ON_CLOSE; + status = pNtSetInformationDebugObject( handle, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG), &len ); + ok( !status, "NtSetInformationDebugObject failed %lx\n", status ); + ok( !len, "wrong len %lu\n", len ); + + for (flag = 2; flag; flag <<= 1) + { + status = pNtSetInformationDebugObject( handle, DebugObjectKillProcessOnExitInformation, + &flag, sizeof(ULONG), &len ); + ok( status == STATUS_INVALID_PARAMETER, "NtSetInformationDebugObject failed %lx\n", status ); + } + + pNtClose( handle ); + + memset( &state, 0xdd, sizeof(state) ); + state.NewState = DbgIdle; + memset( &event, 0xcc, sizeof(event) ); + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( status == STATUS_UNSUCCESSFUL, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + + state.NewState = DbgReplyPending; + memset( &event, 0xcc, sizeof(event) ); + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( status == STATUS_UNSUCCESSFUL, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + + state.NewState = 11; + memset( &event, 0xcc, sizeof(event) ); + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( status == STATUS_UNSUCCESSFUL, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + + state.NewState = DbgExitProcessStateChange; + state.StateInfo.ExitProcess.ExitStatus = 0x123456; + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( !status, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + ok( event.u.ExitProcess.dwExitCode == 0x123456, "event not updated %lx\n", event.u.ExitProcess.dwExitCode ); + + memset( &state, 0xdd, sizeof(state) ); + state.NewState = DbgCreateProcessStateChange; + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( !status, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + ok( event.u.CreateProcessInfo.nDebugInfoSize == 0xdddddddd, "event not updated %lx\n", event.u.CreateProcessInfo.nDebugInfoSize ); + ok( event.u.CreateProcessInfo.lpThreadLocalBase == NULL, "event not updated %p\n", event.u.CreateProcessInfo.lpThreadLocalBase ); + ok( event.u.CreateProcessInfo.lpImageName == NULL, "event not updated %p\n", event.u.CreateProcessInfo.lpImageName ); + ok( event.u.CreateProcessInfo.fUnicode == TRUE, "event not updated %x\n", event.u.CreateProcessInfo.fUnicode ); + + memset( &state, 0xdd, sizeof(state) ); + state.NewState = DbgLoadDllStateChange; + status = pDbgUiConvertStateChangeStructure( &state, &event ); + ok( !status, "DbgUiConvertStateChangeStructure failed %lx\n", status ); + ok( event.dwProcessId == 0xdddddddd, "event not updated %lx\n", event.dwProcessId ); + ok( event.dwThreadId == 0xdddddddd, "event not updated %lx\n", event.dwThreadId ); + ok( event.u.LoadDll.nDebugInfoSize == 0xdddddddd, "event not updated %lx\n", event.u.LoadDll.nDebugInfoSize ); + ok( PtrToUlong(event.u.LoadDll.lpImageName) == 0xdddddddd, "event not updated %p\n", event.u.LoadDll.lpImageName ); + ok( event.u.LoadDll.fUnicode == TRUE, "event not updated %x\n", event.u.LoadDll.fUnicode ); +} + +static void test_process_instrumentation_callback(void) +{ + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + NTSTATUS status; + + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, NULL, 0 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH /* Win10 */ || status == STATUS_INVALID_INFO_CLASS + || status == STATUS_NOT_SUPPORTED, "Got unexpected status %#lx.\n", status ); + if (status != STATUS_INFO_LENGTH_MISMATCH) + { + win_skip( "ProcessInstrumentationCallback is not supported.\n" ); + return; + } + + memset(&info, 0, sizeof(info)); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS /* Win 10 */ || broken( status == STATUS_PRIVILEGE_NOT_HELD ) + || broken( status == STATUS_INFO_LENGTH_MISMATCH ), "Got unexpected status %#lx.\n", status ); + + memset(&info, 0, sizeof(info)); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, 2 * sizeof(info) ); + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "Got unexpected status %#lx.\n", status ); + + if (status) + { + win_skip( "NtSetInformationProcess failed, skipping further tests.\n" ); + return; + } + + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) + 4 ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) / 2 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); +} + +static void test_debuggee_dbgport(int argc, char **argv) +{ + NTSTATUS status, expect_status; + DWORD_PTR debug_port = 0xdeadbeef; + DWORD debug_flags = 0xdeadbeef; + HANDLE handle; + ACCESS_MASK access; + + if (argc < 2) + { + ok(0, "insufficient arguments for child process\n"); + return; + } + + access = strtoul(argv[1], NULL, 0); + winetest_push_context("debug object access %08lx", access); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugPort, + &debug_port, sizeof(debug_port), NULL ); + ok( !status, "NtQueryInformationProcess ProcessDebugPort failed, status %#lx.\n", status ); + ok( debug_port == ~(DWORD_PTR)0, "Expected port %#Ix, got %#Ix.\n", ~(DWORD_PTR)0, debug_port ); + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugFlags, + &debug_flags, sizeof(debug_flags), NULL ); + ok( !status, "NtQueryInformationProcess ProcessDebugFlags failed, status %#lx.\n", status ); + + expect_status = access ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessDebugObjectHandle, + &handle, sizeof(handle), NULL ); + ok( status == expect_status, "NtQueryInformationProcess ProcessDebugObjectHandle expected status %#lx, actual %#lx.\n", expect_status, status ); + if (SUCCEEDED( status )) NtClose( handle ); + + winetest_pop_context(); +} + +static DWORD WINAPI test_ThreadIsTerminated_thread( void *stop_event ) +{ + WaitForSingleObject( stop_event, INFINITE ); + return STATUS_PENDING; +} + +static void test_ThreadIsTerminated(void) +{ + HANDLE thread, stop_event; + ULONG terminated; + NTSTATUS status; + + stop_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, test_ThreadIsTerminated_thread, stop_event, 0, NULL ); + ok( thread != INVALID_HANDLE_VALUE, "failed, error %ld\n", GetLastError() ); + + status = pNtQueryInformationThread( thread, ThreadIsTerminated, &terminated, sizeof(terminated) * 2, NULL ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); + + terminated = 0xdeadbeef; + status = pNtQueryInformationThread( thread, ThreadIsTerminated, &terminated, sizeof(terminated), NULL ); + ok( !status, "got %#lx.\n", status ); + ok( !terminated, "got %lu.\n", terminated ); + + SetEvent( stop_event ); + WaitForSingleObject( thread, INFINITE ); + + status = pNtQueryInformationThread( thread, ThreadIsTerminated, &terminated, sizeof(terminated), NULL ); + ok( !status, "got %#lx.\n", status ); + ok( terminated == 1, "got %lu.\n", terminated ); + + CloseHandle(stop_event); + CloseHandle(thread); + + status = pNtQueryInformationThread( thread, ThreadIsTerminated, &terminated, sizeof(terminated), NULL ); + ok( status == STATUS_INVALID_HANDLE, "got %#lx.\n", status ); +} + +static void test_system_debug_control(void) +{ + NTSTATUS status; + int class; + + for (class = 0; class < SysDbgMaxInfoClass; ++class) + { + status = pNtSystemDebugControl( class, NULL, 0, NULL, 0, NULL ); + if (is_wow64) + { + /* Most of the calls return STATUS_NOT_IMPLEMENTED on wow64. */ + ok( status == STATUS_DEBUGGER_INACTIVE || status == STATUS_NOT_IMPLEMENTED || status == STATUS_INFO_LENGTH_MISMATCH, + "class %d, got %#lx.\n", class, status ); + } + else + { + ok( status == STATUS_DEBUGGER_INACTIVE || status == STATUS_ACCESS_DENIED || status == STATUS_INFO_LENGTH_MISMATCH, + "class %d, got %#lx.\n", class, status ); + } + } +} + +static void test_process_token(int argc, char **argv) +{ + STARTUPINFOA si = {.cb = sizeof(si)}; + PROCESS_ACCESS_TOKEN token_info = {0}; + TOKEN_STATISTICS stats1, stats2; + HANDLE token, their_token; + PROCESS_INFORMATION pi; + char cmdline[MAX_PATH]; + NTSTATUS status; + DWORD size; + BOOL ret; + + token_info.Thread = (HANDLE)0xdeadbeef; + + sprintf( cmdline, "%s %s dummy", argv[0], argv[1] ); + + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi ); + ok( ret, "got error %lu\n", GetLastError() ); + + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) - 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status ); + + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) ); + ok( status == STATUS_INVALID_HANDLE, "got %#lx\n", status ); + + ret = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY | READ_CONTROL | TOKEN_DUPLICATE + | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT, &token ); + ok( ret, "got error %lu\n", GetLastError() ); + + token_info.Token = token; + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) ); + todo_wine ok( status == STATUS_TOKEN_ALREADY_IN_USE, "got %#lx\n", status ); + + ret = DuplicateTokenEx( token, TOKEN_ALL_ACCESS, NULL, SecurityAnonymous, TokenImpersonation, &token_info.Token ); + ok( ret, "got error %lu\n", GetLastError() ); + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) ); + todo_wine ok( status == STATUS_BAD_IMPERSONATION_LEVEL, "got %#lx\n", status ); + CloseHandle( token_info.Token ); + + ret = DuplicateTokenEx( token, TOKEN_QUERY, NULL, SecurityAnonymous, TokenPrimary, &token_info.Token ); + ok( ret, "got error %lu\n", GetLastError() ); + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) ); + ok( status == STATUS_ACCESS_DENIED, "got %#lx\n", status ); + CloseHandle( token_info.Token ); + + ret = DuplicateTokenEx( token, TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, NULL, SecurityAnonymous, TokenPrimary, &token_info.Token ); + ok(ret, "got error %lu\n", GetLastError()); + status = pNtSetInformationProcess( pi.hProcess, ProcessAccessToken, &token_info, sizeof(token_info) ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + + ret = OpenProcessToken( pi.hProcess, TOKEN_QUERY, &their_token ); + ok( ret, "got error %lu\n", GetLastError() ); + + /* The tokens should be the same. */ + ret = GetTokenInformation( token_info.Token, TokenStatistics, &stats1, sizeof(stats1), &size ); + ok( ret, "got error %lu\n", GetLastError() ); + ret = GetTokenInformation( their_token, TokenStatistics, &stats2, sizeof(stats2), &size ); + ok( ret, "got error %lu\n", GetLastError() ); + ok( !memcmp( &stats1.TokenId, &stats2.TokenId, sizeof(LUID) ), "expected same IDs\n" ); + + CloseHandle( token_info.Token ); + CloseHandle( their_token ); + + ResumeThread( pi.hThread ); + ret = WaitForSingleObject( pi.hProcess, 1000 ); + ok( !ret, "got %d\n", ret ); + + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + CloseHandle( token ); +} + +static void test_process_id(void) +{ + char image_name_buffer[1024 * sizeof(WCHAR)]; + UNICODE_STRING *image_name = (UNICODE_STRING *)image_name_buffer; + SYSTEM_PROCESS_ID_INFORMATION info; + unsigned int i, length; + DWORD pids[2048]; + WCHAR name[2048]; + NTSTATUS status; + HANDLE process; + ULONG len; + BOOL bret; + + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessImageFileName, image_name, + sizeof(image_name_buffer), NULL ); + ok( !status, "got %#lx.\n", status ); + length = image_name->Length; + image_name->Buffer[length] = 0; + + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessIdInformation, NULL, 0, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH || (is_wow64 && status == STATUS_ACCESS_VIOLATION), "got %#lx.\n", status ); + ok( len == sizeof(info) || (is_wow64 && len == 0xdeadbeef), "got %#lx.\n", len ); + + info.ProcessId = 0xdeadbeef; + info.ImageName.Length = info.ImageName.MaximumLength = 0; + info.ImageName.Buffer = NULL; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_CID, "got %#lx.\n", status ); + ok( !info.ImageName.Length, "got %#x.\n", info.ImageName.Length ); + ok( !info.ImageName.MaximumLength, "got %#x.\n", info.ImageName.MaximumLength ); + + info.ProcessId = GetCurrentProcessId(); + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); + ok( len == sizeof(info), "got %#lx.\n", len ); + ok( !info.ImageName.Length, "got %#x.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == length + 2 || (is_wow64 && !info.ImageName.MaximumLength), + "got %#x.\n", info.ImageName.MaximumLength ); + + info.ImageName.MaximumLength = sizeof(name); + len = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_ACCESS_VIOLATION, "got %#lx.\n", status ); + ok( len == sizeof(info), "got %#lx.\n", len ); + ok( info.ImageName.Length == length || (is_wow64 && !info.ImageName.Length), + "got %u.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == length + 2 || (is_wow64 && !info.ImageName.Length), + "got %#x.\n", info.ImageName.MaximumLength ); + + info.ProcessId = 0xdeadbeef; + info.ImageName.MaximumLength = sizeof(name); + info.ImageName.Buffer = name; + info.ImageName.Length = 0; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_CID, "got %#lx.\n", status ); + ok( !info.ImageName.Length, "got %#x.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == sizeof(name), "got %#x.\n", info.ImageName.MaximumLength ); + ok( info.ImageName.Buffer == name, "got %p, %p.\n", info.ImageName.Buffer, name ); + + info.ProcessId = 0; + info.ImageName.MaximumLength = sizeof(name); + info.ImageName.Buffer = name; + info.ImageName.Length = 0; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_CID, "got %#lx.\n", status ); + ok( !info.ImageName.Length, "got %#x.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == sizeof(name), "got %#x.\n", info.ImageName.MaximumLength ); + ok( info.ImageName.Buffer == name, "got non NULL.\n" ); + + info.ProcessId = 0; + info.ImageName.MaximumLength = sizeof(name); + info.ImageName.Buffer = name; + info.ImageName.Length = 4; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( info.ImageName.Length == 4, "got %#x.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == sizeof(name), "got %#x.\n", info.ImageName.MaximumLength ); + ok( info.ImageName.Buffer == name, "got non NULL.\n" ); + + info.ProcessId = GetCurrentProcessId(); + info.ImageName.MaximumLength = sizeof(name); + info.ImageName.Buffer = name; + info.ImageName.Length = 4; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), NULL ); + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( info.ImageName.Length == 4, "got %#x.\n", info.ImageName.Length ); + ok( info.ImageName.MaximumLength == sizeof(name), "got %#x.\n", info.ImageName.MaximumLength ); + + info.ImageName.Length = 0; + memset( name, 0xcc, sizeof(name) ); + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( !status, "got %#lx.\n", status ); + ok( info.ImageName.Length == length, "got %#x.\n", info.ImageName.Length ); + ok( len == sizeof(info), "got %#lx.\n", len ); + ok( info.ImageName.MaximumLength == info.ImageName.Length + 2, "got %#x.\n", info.ImageName.MaximumLength ); + ok( !name[info.ImageName.Length / 2], "got %#x.\n", name[info.ImageName.Length / 2] ); + + ok( info.ImageName.Length == image_name->Length, "got %#x, %#x.\n", info.ImageName.Length, image_name->Length ); + ok( !wcscmp( name, image_name->Buffer ), "got %s, %s.\n", debugstr_w(name), debugstr_w(image_name->Buffer) ); + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x601) + bret = EnumProcesses( pids, sizeof(pids), &len ); + ok( bret, "got error %lu.\n", GetLastError() ); + for (i = 0; i < len / sizeof(*pids); ++i) + { + process = OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pids[i] ); + if (pids[i] && !process && GetLastError() != ERROR_ACCESS_DENIED) + { + /* process is gone already. */ + continue; + } + info.ProcessId = pids[i]; + info.ImageName.Length = 0; + info.ImageName.MaximumLength = sizeof(name); + info.ImageName.Buffer = name; + status = pNtQuerySystemInformation( SystemProcessIdInformation, &info, sizeof(info), &len ); + ok( info.ImageName.Buffer == name || (!info.ImageName.MaximumLength && !info.ImageName.Length), + "got %p, %p, pid %lu, lengh %u / %u.\n", info.ImageName.Buffer, name, pids[i], + info.ImageName.Length, info.ImageName.MaximumLength ); + if (pids[i]) + ok( !status, "got %#lx, pid %lu.\n", status, pids[i] ); + else + ok( status == STATUS_INVALID_CID, "got %#lx, pid %lu.\n", status, pids[i] ); + if (process) CloseHandle( process ); + } +#endif +} + +static void test_processor_idle_cycle_time(void) +{ + unsigned int cpu_count = NtCurrentTeb()->Peb->NumberOfProcessors; + ULONG64 buffer[64]; + NTSTATUS status; + USHORT group_id; + ULONG size; + + size = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorIdleCycleTimeInformation, NULL, 0, &size ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %#lx.\n", status ); + ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); + + size = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorIdleCycleTimeInformation, buffer, 7, &size ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %#lx.\n", status ); + ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); + + size = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorIdleCycleTimeInformation, NULL, sizeof(buffer), &size ); + ok( status == STATUS_ACCESS_VIOLATION, "got %#lx.\n", status ); + ok( size == 0xdeadbeef, "got %#lx.\n", size ); + + size = 0xdeadbeef; + status = pNtQuerySystemInformation( SystemProcessorIdleCycleTimeInformation, buffer, sizeof(buffer), &size ); + ok( !status, "got %#lx.\n", status ); + ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); + + memset( buffer, 0xcc, sizeof(buffer) ); + size = 0xdeadbeef; + status = pNtQuerySystemInformationEx( SystemProcessorIdleCycleTimeInformation, NULL, 0, buffer, sizeof(buffer), &size ); + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( size == 0xdeadbeef, "got %#lx.\n", size ); + group_id = 50; + size = 0xdeadbeef; + status = pNtQuerySystemInformationEx( SystemProcessorIdleCycleTimeInformation, &group_id, sizeof(group_id), buffer, sizeof(buffer), &size ); + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( size == 0xdeadbeef, "got %#lx.\n", size ); + group_id = 0; + size = 0xdeadbeef; + status = pNtQuerySystemInformationEx( SystemProcessorIdleCycleTimeInformation, &group_id, sizeof(group_id), buffer, sizeof(buffer), &size ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); } START_TEST(info) @@ -2270,142 +4099,79 @@ START_TEST(info) char **argv; int argc; - argc = winetest_get_mainargs(&argv); - if (argc >= 3) return; /* Child */ + InitFunctionPtrs(); - if (!InitFunctionPtrs()) - return; + argc = winetest_get_mainargs(&argv); + if (argc >= 3) + { + if (strcmp(argv[2], "debuggee:dbgport") == 0) test_debuggee_dbgport(argc - 2, argv + 2); + return; /* Child */ + } /* NtQuerySystemInformation */ - - /* 0x0 SystemBasicInformation */ - trace("Starting test_query_basic()\n"); test_query_basic(); - - /* 0x1 SystemCpuInformation */ - trace("Starting test_query_cpu()\n"); test_query_cpu(); - - /* 0x2 SystemPerformanceInformation */ - trace("Starting test_query_performance()\n"); test_query_performance(); - - /* 0x3 SystemTimeOfDayInformation */ - trace("Starting test_query_timeofday()\n"); test_query_timeofday(); - - /* 0x5 SystemProcessInformation */ - trace("Starting test_query_process()\n"); - test_query_process(); - - /* 0x8 SystemProcessorPerformanceInformation */ - trace("Starting test_query_procperf()\n"); + test_query_process( TRUE ); + test_query_process( FALSE ); test_query_procperf(); - - /* 0xb SystemModuleInformation */ - trace("Starting test_query_module()\n"); test_query_module(); - - /* 0x10 SystemHandleInformation */ - trace("Starting test_query_handle()\n"); test_query_handle(); - - /* 0x40 SystemHandleInformation */ - trace("Starting test_query_handle_ex()\n"); test_query_handle_ex(); - - /* 0x15 SystemCacheInformation */ - trace("Starting test_query_cache()\n"); test_query_cache(); - - /* 0x17 SystemInterruptInformation */ - trace("Starting test_query_interrupt()\n"); test_query_interrupt(); - - /* 0x23 SystemKernelDebuggerInformation */ - trace("Starting test_query_kerndebug()\n"); + test_time_adjustment(); test_query_kerndebug(); - - /* 0x25 SystemRegistryQuotaInformation */ - trace("Starting test_query_regquota()\n"); test_query_regquota(); - - /* 0x49 SystemLogicalProcessorInformation */ - trace("Starting test_query_logicalproc()\n"); test_query_logicalproc(); test_query_logicalprocex(); + test_query_cpusetinfo(); + test_query_firmware(); + test_query_data_alignment(); /* NtPowerInformation */ - - /* 0xb ProcessorInformation */ - trace("Starting test_query_processor_power_info()\n"); + test_query_battery(); test_query_processor_power_info(); /* NtQueryInformationProcess */ - - /* 0x0 ProcessBasicInformation */ - trace("Starting test_query_process_basic()\n"); test_query_process_basic(); - - /* 0x2 ProcessIoCounters */ - trace("Starting test_query_process_io()\n"); test_query_process_io(); - - /* 0x3 ProcessVmCounters */ - trace("Starting test_query_process_vm()\n"); test_query_process_vm(); - - /* 0x4 ProcessTimes */ - trace("Starting test_query_process_times()\n"); test_query_process_times(); - - /* 0x7 ProcessDebugPort */ - trace("Starting test_process_debug_port()\n"); test_query_process_debug_port(argc, argv); - - /* 0x12 ProcessPriorityClass */ - trace("Starting test_query_process_priority()\n"); + test_query_process_debug_port_custom_dacl(argc, argv); test_query_process_priority(); - - /* 0x14 ProcessHandleCount */ - trace("Starting test_query_process_handlecount()\n"); test_query_process_handlecount(); - - /* 0x1A ProcessWow64Information */ - trace("Starting test_query_process_wow64()\n"); test_query_process_wow64(); - - /* 0x1B ProcessImageFileName */ - trace("Starting test_query_process_image_file_name()\n"); test_query_process_image_file_name(); - - /* 0x1E ProcessDebugObjectHandle */ - trace("Starting test_query_process_debug_object_handle()\n"); test_query_process_debug_object_handle(argc, argv); - - /* 0x1F ProcessDebugFlags */ - trace("Starting test_process_debug_flags()\n"); test_query_process_debug_flags(argc, argv); + test_query_process_image_info(); + test_query_process_quota_limits(); + test_mapprotection(); + test_threadstack(); + + /* NtQueryInformationThread */ + test_thread_info(); + test_HideFromDebugger(); + test_thread_start_address(); + test_thread_lookup(); + test_thread_ideal_processor(); + test_ThreadIsTerminated(); + + test_affinity(); + test_debug_object(); /* belongs to its own file */ - trace("Starting test_readvirtualmemory()\n"); test_readvirtualmemory(); - - trace("Starting test_queryvirtualmemory()\n"); test_queryvirtualmemory(); - - trace("Starting test_mapprotection()\n"); - test_mapprotection(); - - trace("Starting test_affinity()\n"); - test_affinity(); - - trace("Starting test_NtGetCurrentProcessorNumber()\n"); test_NtGetCurrentProcessorNumber(); - trace("Starting test_thread_start_address()\n"); - test_thread_start_address(); - - trace("Starting test_query_data_alignment()\n"); - test_query_data_alignment(); + test_ThreadEnableAlignmentFaultFixup(); + test_process_instrumentation_callback(); + test_system_debug_control(); + test_process_token(argc, argv); + test_process_id(); + test_processor_idle_cycle_time(); } diff --git a/modules/rostests/winetests/ntdll/large_int.c b/modules/rostests/winetests/ntdll/large_int.c index 4562e6768e0..d6d9c824002 100644 --- a/modules/rostests/winetests/ntdll/large_int.c +++ b/modules/rostests/winetests/ntdll/large_int.c @@ -22,8 +22,14 @@ */ #include +#include -#include "ntdll_test.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/test.h" /* Function ptrs for ntdll calls */ @@ -33,9 +39,13 @@ static VOID (WINAPI *pRtlFreeAnsiString)(PSTRING); static NTSTATUS (WINAPI *pRtlInt64ToUnicodeString)(ULONGLONG, ULONG, UNICODE_STRING *); static NTSTATUS (WINAPI *pRtlLargeIntegerToChar)(ULONGLONG *, ULONG, ULONG, PCHAR); static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN); -static void (WINAPI *p_alldvrm)(LONGLONG, LONGLONG); -static void (WINAPI *p_aulldvrm)(ULONGLONG, ULONGLONG); +static LONGLONG (WINAPI *p_alldiv)( LONGLONG a, LONGLONG b ); +static LONGLONG (WINAPI *p_allrem)( LONGLONG a, LONGLONG b ); +static LONGLONG (WINAPI *p_allmul)( LONGLONG a, LONGLONG b ); +static ULONGLONG (WINAPI *p_aulldiv)( ULONGLONG a, ULONGLONG b ); +static ULONGLONG (WINAPI *p_aullrem)( ULONGLONG a, ULONGLONG b ); +static void *p_allshl, *p_allshr, *p_aullshr; static void InitFunctionPtrs(void) { @@ -47,8 +57,15 @@ static void InitFunctionPtrs(void) pRtlInt64ToUnicodeString = (void *)GetProcAddress(hntdll, "RtlInt64ToUnicodeString"); pRtlLargeIntegerToChar = (void *)GetProcAddress(hntdll, "RtlLargeIntegerToChar"); pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString"); - p_alldvrm = (void *)GetProcAddress(hntdll, "_alldvrm"); - p_aulldvrm = (void *)GetProcAddress(hntdll, "_aulldvrm"); + + p_alldiv = (void *)GetProcAddress(hntdll, "_alldiv"); + p_allrem = (void *)GetProcAddress(hntdll, "_allrem"); + p_allmul = (void *)GetProcAddress(hntdll, "_allmul"); + p_allshl = (void *)GetProcAddress(hntdll, "_allshl"); + p_allshr = (void *)GetProcAddress(hntdll, "_allshr"); + p_aulldiv = (void *)GetProcAddress(hntdll, "_aulldiv"); + p_aullrem = (void *)GetProcAddress(hntdll, "_aullrem"); + p_aullshr = (void *)GetProcAddress(hntdll, "_aullshr"); } /* if */ } @@ -106,7 +123,6 @@ static const magic_divide_t magic_divide[] = { { ULL(0x74ae3b5f,0x1558c800), ULL(0x2f1e28fd,0x1b5cca00), 41, 0xabcde}, }; -#define NB_MAGIC_DIVIDE (sizeof(magic_divide)/sizeof(*magic_divide)) static void test_RtlExtendedMagicDivide(void) @@ -114,7 +130,7 @@ static void test_RtlExtendedMagicDivide(void) int i; LONGLONG result; - for (i = 0; i < NB_MAGIC_DIVIDE; i++) { + for (i = 0; i < ARRAY_SIZE(magic_divide); i++) { result = pRtlExtendedMagicDivide(magic_divide[i].a, magic_divide[i].b, magic_divide[i].shift); ok(result == magic_divide[i].result, "call failed: RtlExtendedMagicDivide(0x%s, 0x%s, %d) has result 0x%s, expected 0x%s\n", @@ -133,6 +149,7 @@ typedef struct { USHORT MaximumLength; const char *Buffer; NTSTATUS result; + int broken_len; } largeint2str_t; /* @@ -261,21 +278,20 @@ static const largeint2str_t largeint2str[] = { */ { 2, 32768, 16, 17, "1000000000000000\0--------------------------------------------------", STATUS_SUCCESS}, - { 2, 32768, 16, 16, "1000000000000000---------------------------------------------------", STATUS_SUCCESS}, + { 2, 32768, 16, 16, "1000000000000000---------------------------------------------------", STATUS_SUCCESS, 1}, { 2, 65536, 17, 18, "10000000000000000\0-------------------------------------------------", STATUS_SUCCESS}, - { 2, 65536, 17, 17, "10000000000000000--------------------------------------------------", STATUS_SUCCESS}, + { 2, 65536, 17, 17, "10000000000000000--------------------------------------------------", STATUS_SUCCESS, 1}, { 2, 131072, 18, 19, "100000000000000000\0------------------------------------------------", STATUS_SUCCESS}, - { 2, 131072, 18, 18, "100000000000000000-------------------------------------------------", STATUS_SUCCESS}, + { 2, 131072, 18, 18, "100000000000000000-------------------------------------------------", STATUS_SUCCESS, 1}, {16, 0xffffffff, 8, 9, "FFFFFFFF\0----------------------------------------------------------", STATUS_SUCCESS}, - {16, 0xffffffff, 8, 8, "FFFFFFFF-----------------------------------------------------------", STATUS_SUCCESS}, - {16, 0xffffffff, 8, 7, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW}, + {16, 0xffffffff, 8, 8, "FFFFFFFF-----------------------------------------------------------", STATUS_SUCCESS, 1}, + {16, 0xffffffff, 8, 7, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW, 1}, {16, 0xa, 1, 2, "A\0-----------------------------------------------------------------", STATUS_SUCCESS}, - {16, 0xa, 1, 1, "A------------------------------------------------------------------", STATUS_SUCCESS}, - {16, 0, 1, 0, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW}, + {16, 0xa, 1, 1, "A------------------------------------------------------------------", STATUS_SUCCESS, 1}, + {16, 0, 1, 0, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW, 1}, {20, 0xdeadbeef, 0, 9, "-------------------------------------------------------------------", STATUS_INVALID_PARAMETER}, {-8, 07654321, 0, 12, "-------------------------------------------------------------------", STATUS_INVALID_PARAMETER}, }; -#define NB_LARGEINT2STR (sizeof(largeint2str)/sizeof(*largeint2str)) static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t *largeint2str) @@ -292,7 +308,7 @@ static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t #ifdef _WIN64 if (largeint2str->value >> 32 == 0xffffffff) /* this crashes on 64-bit Vista */ { - skip( "Value ffffffff%08x broken on 64-bit windows\n", (DWORD)largeint2str->value ); + skip( "Value ffffffff%08lx broken on 64-bit windows\n", (DWORD)largeint2str->value ); return; } #endif @@ -335,7 +351,7 @@ static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t } /* if */ } else { ok(result == largeint2str->result, - "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) has result %x, expected: %x\n", + "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) has result %lx, expected: %lx\n", test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base, result, largeint2str->result); if (result == STATUS_SUCCESS) { ok(unicode_string.Buffer[unicode_string.Length/sizeof(WCHAR)] == '\0', @@ -344,10 +360,10 @@ static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t } /* if */ } /* if */ ok(memcmp(unicode_string.Buffer, expected_unicode_string.Buffer, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0, - "(test %d): RtlInt64ToUnicodeString(0x%x%08x, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", - test_num, (DWORD)(largeint2str->value >>32), (DWORD)largeint2str->value, largeint2str->base, - ansi_str.Buffer, expected_ansi_str.Buffer); - ok(unicode_string.Length == expected_unicode_string.Length, + "(test %d): RtlInt64ToUnicodeString(0x%I64x, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", + test_num, largeint2str->value, largeint2str->base, ansi_str.Buffer, expected_ansi_str.Buffer); + ok(unicode_string.Length == expected_unicode_string.Length || + broken(largeint2str->broken_len && !unicode_string.Length) /* win11 */, "(test %d): RtlInt64ToUnicodeString(0x%s, %d, [out]) string has Length %d, expected: %d\n", test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base, unicode_string.Length, expected_unicode_string.Length); @@ -364,7 +380,7 @@ static void test_RtlInt64ToUnicodeString(void) { int test_num; - for (test_num = 0; test_num < NB_LARGEINT2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(largeint2str); test_num++) { one_RtlInt64ToUnicodeString_test(test_num, &largeint2str[test_num]); } /* for */ } @@ -379,7 +395,7 @@ static void one_RtlLargeIntegerToChar_test(int test_num, const largeint2str_t *l #ifdef _WIN64 if (largeint2str->value >> 32 == 0xffffffff) /* this crashes on 64-bit Vista */ { - skip( "Value ffffffff%08x broken on 64-bit windows\n", (DWORD)largeint2str->value ); + skip( "Value ffffffff%08lx broken on 64-bit windows\n", (DWORD)largeint2str->value ); return; } #endif @@ -393,7 +409,7 @@ static void one_RtlLargeIntegerToChar_test(int test_num, const largeint2str_t *l result = pRtlLargeIntegerToChar(&value, largeint2str->base, largeint2str->MaximumLength, dest_str); } /* if */ ok(result == largeint2str->result, - "(test %d): RtlLargeIntegerToChar(0x%s, %d, %d, [out]) has result %x, expected: %x\n", + "(test %d): RtlLargeIntegerToChar(0x%s, %d, %d, [out]) has result %lx, expected: %lx\n", test_num, wine_dbgstr_longlong(largeint2str->value), largeint2str->base, largeint2str->MaximumLength, result, largeint2str->result); ok(memcmp(dest_str, largeint2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0, @@ -409,95 +425,159 @@ static void test_RtlLargeIntegerToChar(void) int test_num; ULONGLONG value; - for (test_num = 0; test_num < NB_LARGEINT2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(largeint2str); test_num++) { one_RtlLargeIntegerToChar_test(test_num, &largeint2str[test_num]); } /* for */ value = largeint2str[0].value; result = pRtlLargeIntegerToChar(&value, 20, largeint2str[0].MaximumLength, NULL); ok(result == STATUS_INVALID_PARAMETER, - "(test a): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n", + "(test a): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %lx, expected: %lx\n", wine_dbgstr_longlong(largeint2str[0].value), 20, largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER); result = pRtlLargeIntegerToChar(&value, 20, 0, NULL); ok(result == STATUS_INVALID_PARAMETER, - "(test b): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n", + "(test b): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %lx, expected: %lx\n", wine_dbgstr_longlong(largeint2str[0].value), 20, largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER); result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, 0, NULL); ok(result == STATUS_BUFFER_OVERFLOW, - "(test c): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n", + "(test c): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %lx, expected: %lx\n", wine_dbgstr_longlong(largeint2str[0].value), largeint2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW); result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, largeint2str[0].MaximumLength, NULL); ok(result == STATUS_ACCESS_VIOLATION, - "(test d): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %x, expected: %x\n", + "(test d): RtlLargeIntegerToChar(0x%s, %d, %d, NULL) has result %lx, expected: %lx\n", wine_dbgstr_longlong(largeint2str[0].value), largeint2str[0].base, largeint2str[0].MaximumLength, result, STATUS_ACCESS_VIOLATION); } - +static void test_builtins(void) +{ #ifdef __i386__ + void *code_mem; + ULONGLONG u; + LONGLONG l; -#include "pshpack1.h" -struct lldvrm_thunk -{ - BYTE push_ebx; /* pushl %ebx */ - DWORD push_esp1; /* pushl 24(%esp) */ - DWORD push_esp2; /* pushl 24(%esp) */ - DWORD push_esp3; /* pushl 24(%esp) */ - DWORD push_esp4; /* pushl 24(%esp) */ - DWORD call; /* call 24(%esp) */ - WORD mov_ecx_eax; /* movl %ecx,%eax */ - WORD mov_ebx_edx; /* movl %ebx,%edx */ - BYTE pop_ebx; /* popl %ebx */ - BYTE ret; /* ret */ -}; -#include "poppack.h" + static const BYTE call_shift_code[] = + { + 0x55, /* pushl %ebp */ + 0x89, 0xe5, /* movl %esp,%ebp */ + 0x31, 0xc0, /* xorl %eax,%eax */ + 0x31, 0xd2, /* xorl %edx,%edx */ + 0x31, 0xc9, /* xorl %ecx,%ecx */ + 0x87, 0x45, 0x0c, /* xchgl 12(%ebp),%eax */ + 0x87, 0x55, 0x10, /* xchgl 16(%ebp),%edx */ + 0x87, 0x4d, 0x14, /* xchgl 20(%ebp),%ecx */ + 0xff, 0x55, 0x08, /* call *8(%ebp) */ + 0x39, 0xe5, /* cmpl %esp,%ebp */ + 0x74, 0x05, /* je 1f */ + 0xb8, 0xef, 0xbe, 0xad, 0xde, /* movl $0xdeadbeef,%eax */ + 0xc9, /* leave */ + 0xc3, /* ret */ + }; + LONGLONG (__cdecl *call_shift_func)(void *func, LONGLONG a, LONG b); -static void test__alldvrm(void) -{ - struct lldvrm_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE); - ULONGLONG (CDECL *call_lldvrm_func)(void *func, ULONGLONG, ULONGLONG) = (void *)thunk; - ULONGLONG ret; + code_mem = VirtualAlloc(NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - memset(thunk, 0x90, sizeof(*thunk)); - thunk->push_ebx = 0x53; /* pushl %ebx */ - thunk->push_esp1 = 0x182474ff; /* pushl 24(%esp) */ - thunk->push_esp2 = 0x182474ff; /* pushl 24(%esp) */ - thunk->push_esp3 = 0x182474ff; /* pushl 24(%esp) */ - thunk->push_esp4 = 0x182474ff; /* pushl 24(%esp) */ - thunk->call = 0x182454ff; /* call 24(%esp) */ - thunk->pop_ebx = 0x5b; /* popl %ebx */ - thunk->ret = 0xc3; /* ret */ + l = p_alldiv(100, 7); + ok(l == 14, "_alldiv returned %s\n", wine_dbgstr_longlong(l)); - ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3); - ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); - ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3); - ok(ret == 0xff9ee8ddaa499389ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + l = p_alldiv(-100, 7); + ok(l == -14, "_alldiv returned %s\n", wine_dbgstr_longlong(l)); - ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3); - ok(ret == 0x61172255b66c77ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); - ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3); - ok(ret == 0, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + l = p_alldiv(0x2000000040ll, 0x100000007ll); + ok(l == 0x1f, "_alldiv returned %s\n", wine_dbgstr_longlong(l)); - thunk->mov_ecx_eax = 0xc889; - thunk->mov_ebx_edx = 0xda89; + u = p_aulldiv(100, 7); + ok(u == 14, "_aulldiv returned %s\n", wine_dbgstr_longlong(u)); - ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, 3); - ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); - ret = call_lldvrm_func(p_alldvrm, 0x0123456701234567ULL, -3); - ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + u = p_aulldiv(-100, 7); + ok(u == 0x2492492492492484ull, "_alldiv returned %s\n", wine_dbgstr_longlong(u)); - ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, 3); - ok(ret == 2, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); - ret = call_lldvrm_func(p_aulldvrm, 0x0123456701234567ULL, -3); - ok(ret == 0x123456701234567ULL, "got %x%08x\n", (DWORD)(ret >> 32), (DWORD)ret); + u = p_aulldiv(0x2000000040ull, 0x100000007ull); + ok(u == 0x1f, "_aulldiv returned %s\n", wine_dbgstr_longlong(u)); + + l = p_allrem(100, 7); + ok(l == 2, "_allrem returned %s\n", wine_dbgstr_longlong(l)); + + l = p_allrem(-100, 7); + ok(l == -2, "_allrem returned %s\n", wine_dbgstr_longlong(l)); + + l = p_allrem(0x2000000040ll, 0x100000007ll); + ok(l == 0xffffff67, "_allrem returned %s\n", wine_dbgstr_longlong(l)); + + u = p_aullrem(100, 7); + ok(u == 2, "_aullrem returned %s\n", wine_dbgstr_longlong(u)); + + u = p_aullrem(-100, 7); + ok(u == 0, "_allrem returned %s\n", wine_dbgstr_longlong(u)); + + u = p_aullrem(0x2000000040ull, 0x100000007ull); + ok(u == 0xffffff67, "_aullrem returned %s\n", wine_dbgstr_longlong(u)); + + l = p_allmul(3, 4); + ok(l == 12, "_allmul = %s\n", wine_dbgstr_longlong(l)); + + l = p_allmul(0x300000001ll, 4); + ok(l == 0xc00000004, "_allmul = %s\n", wine_dbgstr_longlong(l)); + + memcpy(code_mem, call_shift_code, sizeof(call_shift_code)); + call_shift_func = code_mem; + + l = call_shift_func(p_allshl, 0x0123456789abcdefll, 12); + ok(l == 0x3456789abcdef000ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshl, 0x0123456789abcdefll, 44); + ok(l == 0xbcdef00000000000ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshl, 0x0123456789abcdefll, 88); + ok(!l, "got %#I64x\n", l); + + l = call_shift_func(p_allshl, 0x0123456789abcdefll, 0x88); + ok(!l, "got %#I64x\n", l); + + l = call_shift_func(p_allshl, 0x0123456789abcdefll, 0x108); + ok(l == 0x23456789abcdef00ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x0123456789abcdefll, 12); + ok(l == 0x0123456789abcll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x0123456789abcdefll, 44); + ok(l == 0x01234ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x0123456789abcdefll, 88); + ok(!l, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x8123456789abcdefll, 12); + ok(l == 0xfff8123456789abcll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x8123456789abcdefll, 44); + ok(l == 0xfffffffffff81234ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x8123456789abcdefll, 88); + ok(l == -1ll, "got %#I64x\n", l); + + l = call_shift_func(p_allshr, 0x8123456789abcdefll, 0x108); + ok(l == 0xff8123456789abcdll, "got %#I64x\n", l); + + l = call_shift_func(p_aullshr, 0x8123456789abcdefll, 12); + ok(l == 0x8123456789abcll, "got %#I64x\n", l); + + l = call_shift_func(p_aullshr, 0x8123456789abcdefll, 44); + ok(l == 0x81234ll, "got %#I64x\n", l); + + l = call_shift_func(p_aullshr, 0x8123456789abcdefll, 88); + ok(!l, "got %#I64x\n", l); + + l = call_shift_func(p_aullshr, 0x8123456789abcdefll, 0x108); + ok(l == 0x8123456789abcdll, "got %#I64x\n", l); + + VirtualFree(code_mem, 0, MEM_RELEASE); +#endif /* __i386__ */ } -#endif /* __i386__ */ - START_TEST(large_int) { @@ -509,8 +589,5 @@ START_TEST(large_int) test_RtlInt64ToUnicodeString(); if (pRtlLargeIntegerToChar) test_RtlLargeIntegerToChar(); - -#ifdef __i386__ - test__alldvrm(); -#endif /* __i386__ */ + test_builtins(); } diff --git a/modules/rostests/winetests/ntdll/msvc-x64.asm b/modules/rostests/winetests/ntdll/msvc-x64.asm new file mode 100644 index 00000000000..bb20f35e78e --- /dev/null +++ b/modules/rostests/winetests/ntdll/msvc-x64.asm @@ -0,0 +1,55 @@ + + +.code + +; USHORT __readsegds(); +PUBLIC __readsegds +__readsegds PROC + mov ax, ds + ret +__readsegds ENDP + +; USHORT __readseges(); +PUBLIC __readseges +__readseges PROC + mov ax, es + ret +__readseges ENDP + +; USHORT __readsegfs(); +PUBLIC __readsegfs +__readsegfs PROC + mov ax, fs + ret +__readsegfs ENDP + +; USHORT __readseggs(); +PUBLIC __readseggs +__readseggs PROC + mov ax, gs + ret +__readseggs ENDP + +; USHORT __readsegss(); +PUBLIC __readsegss +__readsegss PROC + mov ax, ss + ret +__readsegss ENDP + +; void __cld(void); +PUBLIC __cld +__cld PROC + cld + ret +__cld ENDP + +; void Call_NtRaiseException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, BOOLEAN FirstChance, PVOID pNtRaiseException); +PUBLIC Call_NtRaiseException +Call_NtRaiseException PROC + mov r12, 0DEADCAFEh + call r9 + ret +Call_NtRaiseException ENDP + +END diff --git a/modules/rostests/winetests/ntdll/om.c b/modules/rostests/winetests/ntdll/om.c index 22592e1709c..b97992bdd64 100644 --- a/modules/rostests/winetests/ntdll/om.c +++ b/modules/rostests/winetests/ntdll/om.c @@ -19,20 +19,43 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "ntdll_test.h" -#include "winternl.h" -#include "stdio.h" -#include "winnt.h" -#include "stdlib.h" +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wingdi.h" +#include "winuser.h" +#ifndef __REACTOS__ +#include "ddk/wdm.h" +#endif +#include "wine/test.h" +#ifdef __REACTOS__ +/* These aren't in the Windows SDK but Wine needs them. */ +#define DIRECTORY_QUERY 0x0001 +#define DIRECTORY_TRAVERSE 0x0002 +#define DIRECTORY_CREATE_OBJECT 0x0004 +#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008 +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) +#define SYMBOLIC_LINK_QUERY 0x0001 +#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) +#define EVENT_QUERY_STATE 0x0001 +#define SEMAPHORE_QUERY_STATE 0x0001 + +/* These are here because Wine's headers aren't compatible */ +#define RTL_CONSTANT_STRING(s) { sizeof(s) - sizeof(s[0]), sizeof(s), (void*)s } +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3) +#endif -static HANDLE (WINAPI *pCreateWaitableTimerA)(SECURITY_ATTRIBUTES*, BOOL, LPCSTR); -static BOOLEAN (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR); static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR ); -static VOID (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING); +static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, EVENT_TYPE, BOOLEAN); static NTSTATUS (WINAPI *pNtOpenEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES); -static NTSTATUS (WINAPI *pNtPulseEvent) ( HANDLE, PULONG ); -static NTSTATUS (WINAPI *pNtQueryEvent) ( HANDLE, EVENT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); static NTSTATUS (WINAPI *pNtCreateJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtOpenJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtCreateKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG, @@ -43,8 +66,6 @@ static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ACCESS_MASK, POBJECT_A ULONG, ULONG, ULONG, PLARGE_INTEGER ); static NTSTATUS (WINAPI *pNtCreateMutant)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN ); static NTSTATUS (WINAPI *pNtOpenMutant) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); -static NTSTATUS (WINAPI *pNtQueryMutant) ( HANDLE, MUTANT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); -static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, PLONG ); static NTSTATUS (WINAPI *pNtCreateSemaphore)( PHANDLE, ACCESS_MASK,const POBJECT_ATTRIBUTES,LONG,LONG ); static NTSTATUS (WINAPI *pNtOpenSemaphore)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtCreateTimer) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, TIMER_TYPE ); @@ -65,88 +86,82 @@ static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,U static NTSTATUS (WINAPI *pNtReleaseSemaphore)(HANDLE, ULONG, PULONG); static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); -static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); -static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); static NTSTATUS (WINAPI *pNtOpenIoCompletion)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); -static NTSTATUS (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); +static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, void *, ULONG, FILE_INFORMATION_CLASS); +static NTSTATUS (WINAPI *pNtOpenProcess)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * ); +static NTSTATUS (WINAPI *pNtCreateDebugObject)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, ULONG ); +static NTSTATUS (WINAPI *pNtGetNextThread)(HANDLE process, HANDLE thread, ACCESS_MASK access, ULONG attributes, + ULONG flags, HANDLE *handle); +static NTSTATUS (WINAPI *pNtOpenProcessToken)(HANDLE,DWORD,HANDLE*); +static NTSTATUS (WINAPI *pNtOpenThread)(HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * ); +static NTSTATUS (WINAPI *pNtOpenThreadToken)(HANDLE,DWORD,BOOLEAN,HANDLE*); +static NTSTATUS (WINAPI *pNtDuplicateToken)(HANDLE,ACCESS_MASK,OBJECT_ATTRIBUTES*,BOOLEAN,TOKEN_TYPE,HANDLE*); +static NTSTATUS (WINAPI *pNtDuplicateObject)(HANDLE,HANDLE,HANDLE,HANDLE*,ACCESS_MASK,ULONG,ULONG); +static NTSTATUS (WINAPI *pNtCompareObjects)(HANDLE,HANDLE); #define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 #define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) +#define DESKTOP_ALL_ACCESS 0x01ff -#define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1)) - -static LPCSTR wine_dbgstr_us( const UNICODE_STRING *us ) +#define check_unicode_string(a, b) check_unicode_string_(__LINE__, a, b) +static void check_unicode_string_( int line, const UNICODE_STRING *string, const WCHAR *expect ) { - if (!us) return "(null)"; - return wine_dbgstr_wn(us->Buffer, us->Length / sizeof(WCHAR)); -} + size_t len = wcslen( expect ) * sizeof(WCHAR); -static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n ) -{ - if (n <= 0) return 0; - while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; } - return *str1 - *str2; + ok_(__FILE__, line)( !wcscmp( string->Buffer, expect ), "got string %s\n", debugstr_w( string->Buffer )); + ok_(__FILE__, line)( string->Length == len, "got length %u\n", string->Length ); + ok_(__FILE__, line)( string->MaximumLength == len + sizeof(WCHAR), "got max length %u\n", string->MaximumLength ); } static void test_case_sensitive (void) { - static const WCHAR buffer1[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0}; - static const WCHAR buffer2[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','T','e','s','t',0}; - static const WCHAR buffer3[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','T','E','s','t',0}; - static const WCHAR buffer4[] = {'\\','B','A','S','E','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0}; NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING str; HANDLE Event, Mutant, h; - pRtlInitUnicodeString(&str, buffer1); + pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\test"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE); - ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status); + ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08lx)\n", status); - status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, FALSE, FALSE); - ok(status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH, - "NtCreateEvent should have failed with STATUS_OBJECT_NAME_COLLISION or STATUS_OBJECT_TYPE_MISMATCH got (%08x)\n", status); + status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, NotificationEvent, FALSE); + ok(status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH /* Vista+ */, "got %#lx\n", status); - pRtlInitUnicodeString(&str, buffer2); + pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\Test"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, FALSE, FALSE); - ok(status == STATUS_SUCCESS, "Failed to create Event(%08x)\n", status); + status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, NotificationEvent, FALSE); + ok(status == STATUS_SUCCESS, "Failed to create Event(%08lx)\n", status); - pRtlInitUnicodeString(&str, buffer3); + pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\TEst"); InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL); status = pNtOpenMutant(&h, GENERIC_ALL, &attr); ok(status == STATUS_OBJECT_TYPE_MISMATCH, - "NtOpenMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08x)\n", status); + "NtOpenMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status); pNtClose(Mutant); - pRtlInitUnicodeString(&str, buffer4); + pRtlInitUnicodeString(&str, L"\\BASENamedObjects\\test"); InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL); status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE); - ok(status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH, - "NtCreateMutant should have failed with STATUS_OBJECT_NAME_COLLISION or STATUS_OBJECT_TYPE_MISMATCH got (%08x)\n", status); + ok(status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH /* Vista+ */, "got %#lx\n", status); - status = pNtCreateEvent(&h, GENERIC_ALL, &attr, FALSE, FALSE); + status = pNtCreateEvent(&h, GENERIC_ALL, &attr, NotificationEvent, FALSE); ok(status == STATUS_OBJECT_NAME_COLLISION, - "NtCreateEvent should have failed with STATUS_OBJECT_NAME_COLLISION got(%08x)\n", status); + "NtCreateEvent should have failed with STATUS_OBJECT_NAME_COLLISION got(%08lx)\n", status); attr.Attributes = 0; status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_PATH_NOT_FOUND, - "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08x)\n", status); + "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status); pNtClose(Event); } static void test_namespace_pipe(void) { - static const WCHAR buffer1[] = {'\\','?','?','\\','P','I','P','E','\\','t','e','s','t','\\','p','i','p','e',0}; - static const WCHAR buffer2[] = {'\\','?','?','\\','P','I','P','E','\\','T','E','S','T','\\','P','I','P','E',0}; - static const WCHAR buffer3[] = {'\\','?','?','\\','p','i','p','e','\\','t','e','s','t','\\','p','i','p','e',0}; - static const WCHAR buffer4[] = {'\\','?','?','\\','p','i','p','e','\\','t','e','s','t',0}; OBJECT_ATTRIBUTES attr; UNICODE_STRING str; IO_STATUS_BLOCK iosb; @@ -156,85 +171,99 @@ static void test_namespace_pipe(void) timeout.QuadPart = -10000; - pRtlInitUnicodeString(&str, buffer1); + pRtlInitUnicodeString(&str, L"\\??\\PIPE\\test\\pipe"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + status = pNtCreateNamedPipeFile((HANDLE *)0xdeadbee0, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout); - ok(status == STATUS_SUCCESS, "Failed to create NamedPipe(%08x)\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Failed to create NamedPipe(%08lx)\n", status); status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout); - ok(status == STATUS_INSTANCE_NOT_AVAILABLE, - "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08x)\n", status); + ok(status == STATUS_SUCCESS, "Failed to create NamedPipe(%08lx)\n", status); - pRtlInitUnicodeString(&str, buffer2); - InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + h = (HANDLE)0xdeadbeef; + status = pNtCreateNamedPipeFile(&h, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout); ok(status == STATUS_INSTANCE_NOT_AVAILABLE, - "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08x)\n", status); + "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08lx)\n", status); + ok( !h || broken(h == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", h ); + + pRtlInitUnicodeString(&str, L"\\??\\PIPE\\TEST\\PIPE"); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + h = (HANDLE)0xdeadbeef; + status = pNtCreateNamedPipeFile(&h, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout); + ok(status == STATUS_INSTANCE_NOT_AVAILABLE, + "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08lx)\n", status); + ok( !h || broken(h == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", h ); h = CreateFileA("\\\\.\\pipe\\test\\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); - ok(h != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%u)\n", GetLastError()); + ok(h != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%lu)\n", GetLastError()); pNtClose(h); - pRtlInitUnicodeString(&str, buffer3); + pRtlInitUnicodeString(&str, L"\\??\\pipe\\test\\pipe"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + h = (HANDLE)0xdeadbeef; status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_PIPE_NOT_AVAILABLE || status == STATUS_OBJECT_NAME_INVALID || /* vista */ status == STATUS_OBJECT_NAME_NOT_FOUND, /* win8 */ - "NtOpenFile should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08x)\n", status); + "NtOpenFile should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status); + ok( !h || broken(h == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", h ); - pRtlInitUnicodeString(&str, buffer4); + pRtlInitUnicodeString(&str, L"\\??\\pipe\\test"); InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL); + h = (HANDLE)0xdeadbeef; status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); ok(status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_NAME_INVALID, /* vista */ - "NtOpenFile should have failed with STATUS_OBJECT_NAME_NOT_FOUND got(%08x)\n", status); + "NtOpenFile should have failed with STATUS_OBJECT_NAME_NOT_FOUND got(%08lx)\n", status); + ok( !h || broken(h == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", h ); str.Length -= 4 * sizeof(WCHAR); status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); - ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08lx\n", status); pNtClose( h ); str.Length -= sizeof(WCHAR); status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); - ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenFile should have succeeded got %08lx\n", status); pNtClose( h ); pNtClose(pipe); } -#define DIRECTORY_QUERY (0x0001) -#define SYMBOLIC_LINK_QUERY 0x0001 +#define check_create_open_dir(parent, name, status) check_create_open_dir_(__LINE__, parent, name, status) +static void check_create_open_dir_( int line, HANDLE parent, const WCHAR *name, NTSTATUS expect ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + HANDLE h; -#define DIR_TEST_CREATE_OPEN(n,e) \ - do { \ - HANDLE h; \ - pRtlCreateUnicodeStringFromAsciiz(&str, n); \ - status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \ - ok( status == e, "NtCreateDirectoryObject(%s) got %08x\n", n, status ); \ - if (!status) pNtClose( h ); \ - status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); \ - ok( status == e, "NtOpenDirectoryObject(%s) got %08x\n", n, status ); \ - if (!status) pNtClose( h ); \ - pRtlFreeUnicodeString(&str); \ - } while(0) + RtlInitUnicodeString( &str, name ); + InitializeObjectAttributes( &attr, &str, 0, parent, NULL ); + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok_(__FILE__, line)( status == expect, "NtCreateDirectoryObject(%s) got %08lx\n", debugstr_w(name), status ); + if (!status) pNtClose( h ); -static BOOL is_correct_dir( HANDLE dir, const char *name ) + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok_(__FILE__, line)( status == expect, "NtOpenDirectoryObject(%s) got %08lx\n", debugstr_w(name), status ); + if (!status) pNtClose( h ); +} + +static BOOL is_correct_dir( HANDLE dir, const WCHAR *name ) { NTSTATUS status; UNICODE_STRING str; OBJECT_ATTRIBUTES attr; HANDLE h = 0; - pRtlCreateUnicodeStringFromAsciiz(&str, name); + RtlInitUnicodeString( &str, name ); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, dir, NULL); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); - pRtlFreeUnicodeString(&str); if (h) pNtClose( h ); return (status == STATUS_OBJECT_NAME_EXISTS); } @@ -242,33 +271,23 @@ static BOOL is_correct_dir( HANDLE dir, const char *name ) /* return a handle to the BaseNamedObjects dir where kernel32 objects get created */ static HANDLE get_base_dir(void) { - static const char objname[] = "om.c_get_base_dir_obj"; + static const WCHAR objname[] = L"om.c_get_base_dir_obj"; NTSTATUS status; UNICODE_STRING str; OBJECT_ATTRIBUTES attr; HANDLE dir, h; - char name[40]; + WCHAR name[40]; - h = CreateMutexA(NULL, FALSE, objname); - ok(h != 0, "CreateMutexA failed got ret=%p (%d)\n", h, GetLastError()); + h = CreateMutexW( NULL, FALSE, objname ); + ok(h != 0, "CreateMutexA failed got ret=%p (%ld)\n", h, GetLastError()); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL); - sprintf( name, "\\BaseNamedObjects\\Session\\%u", NtCurrentTeb()->Peb->SessionId ); - pRtlCreateUnicodeStringFromAsciiz(&str, name ); + swprintf( name, ARRAY_SIZE(name), L"\\BaseNamedObjects\\Session\\%u", NtCurrentTeb()->Peb->SessionId ); + RtlInitUnicodeString( &str, name ); status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr); - pRtlFreeUnicodeString(&str); - if (!status && is_correct_dir( dir, objname )) goto done; - if (!status) pNtClose( dir ); + ok(!status, "got %#lx\n", status); + ok(is_correct_dir( dir, objname ), "wrong dir\n"); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects"); - status = pNtOpenDirectoryObject(&dir, DIRECTORY_QUERY, &attr); - pRtlFreeUnicodeString(&str); - if (!status && is_correct_dir( dir, objname )) goto done; - if (!status) pNtClose( dir ); - - dir = 0; - -done: pNtClose( h ); return dir; } @@ -280,104 +299,162 @@ static void test_name_collisions(void) OBJECT_ATTRIBUTES attr; HANDLE dir, h, h1, h2; DWORD winerr; - LARGE_INTEGER size; + LARGE_INTEGER size, timeout; + IO_STATUS_BLOCK iosb; InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\"); + RtlInitUnicodeString(&str, L"\\"); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateDirectoryObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateDirectoryObject got %08lx\n", status ); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_EXISTS, "NtCreateDirectoryObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "NtCreateDirectoryObject got %08lx\n", status ); pNtClose(h); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_TYPE_MISMATCH, - "NtCreateMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08x)\n", status); - pRtlFreeUnicodeString(&str); + "NtCreateMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\??\\PIPE\\om.c-mutant"); + RtlInitUnicodeString(&str, L"\\??\\PIPE\\om.c-mutant"); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); - ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_OBJECT_PATH_NOT_FOUND, - "NtCreateMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08x)\n", status); - pRtlFreeUnicodeString(&str); + todo_wine ok(status == STATUS_OBJECT_PATH_NOT_FOUND, "got %#lx\n", status); - if (!(dir = get_base_dir())) - { - win_skip( "couldn't find the BaseNamedObjects dir\n" ); - return; - } - pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test"); + dir = get_base_dir(); + RtlInitUnicodeString(&str, L"om.c-test"); InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, dir, NULL); h = CreateMutexA(NULL, FALSE, "om.c-test"); - ok(h != 0, "CreateMutexA failed got ret=%p (%d)\n", h, GetLastError()); + ok(h != 0, "CreateMutexA failed got ret=%p (%ld)\n", h, GetLastError()); status = pNtCreateMutant(&h1, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL, - "NtCreateMutant should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08x)\n", status); + "NtCreateMutant should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status); h2 = CreateMutexA(NULL, FALSE, "om.c-test"); winerr = GetLastError(); ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS, - "CreateMutexA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%d)\n", h2, winerr); + "CreateMutexA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr); pNtClose(h); pNtClose(h1); pNtClose(h2); h = CreateEventA(NULL, FALSE, FALSE, "om.c-test"); - ok(h != 0, "CreateEventA failed got ret=%p (%d)\n", h, GetLastError()); - status = pNtCreateEvent(&h1, GENERIC_ALL, &attr, FALSE, FALSE); + ok(h != 0, "CreateEventA failed got ret=%p (%ld)\n", h, GetLastError()); + status = pNtCreateEvent(&h1, GENERIC_ALL, &attr, NotificationEvent, FALSE); ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL, - "NtCreateEvent should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08x)\n", status); + "NtCreateEvent should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status); h2 = CreateEventA(NULL, FALSE, FALSE, "om.c-test"); winerr = GetLastError(); ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS, - "CreateEventA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%d)\n", h2, winerr); + "CreateEventA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr); pNtClose(h); pNtClose(h1); pNtClose(h2); h = CreateSemaphoreA(NULL, 1, 2, "om.c-test"); - ok(h != 0, "CreateSemaphoreA failed got ret=%p (%d)\n", h, GetLastError()); + ok(h != 0, "CreateSemaphoreA failed got ret=%p (%ld)\n", h, GetLastError()); status = pNtCreateSemaphore(&h1, GENERIC_ALL, &attr, 1, 2); ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL, - "NtCreateSemaphore should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08x)\n", status); + "NtCreateSemaphore should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status); h2 = CreateSemaphoreA(NULL, 1, 2, "om.c-test"); winerr = GetLastError(); ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS, - "CreateSemaphoreA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%d)\n", h2, winerr); + "CreateSemaphoreA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr); pNtClose(h); pNtClose(h1); pNtClose(h2); - - h = pCreateWaitableTimerA(NULL, TRUE, "om.c-test"); - ok(h != 0, "CreateWaitableTimerA failed got ret=%p (%d)\n", h, GetLastError()); + + h = CreateWaitableTimerA(NULL, TRUE, "om.c-test"); + ok(h != 0, "CreateWaitableTimerA failed got ret=%p (%ld)\n", h, GetLastError()); status = pNtCreateTimer(&h1, GENERIC_ALL, &attr, NotificationTimer); ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL, - "NtCreateTimer should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08x)\n", status); - h2 = pCreateWaitableTimerA(NULL, TRUE, "om.c-test"); + "NtCreateTimer should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status); + h2 = CreateWaitableTimerA(NULL, TRUE, "om.c-test"); winerr = GetLastError(); ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS, - "CreateWaitableTimerA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%d)\n", h2, winerr); + "CreateWaitableTimerA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr); pNtClose(h); pNtClose(h1); pNtClose(h2); h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, "om.c-test"); - ok(h != 0, "CreateFileMappingA failed got ret=%p (%d)\n", h, GetLastError()); + ok(h != 0, "CreateFileMappingA failed got ret=%p (%ld)\n", h, GetLastError()); size.u.LowPart = 256; size.u.HighPart = 0; status = pNtCreateSection(&h1, SECTION_MAP_WRITE, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0); ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL, - "NtCreateSection should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08x)\n", status); + "NtCreateSection should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status); h2 = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, "om.c-test"); winerr = GetLastError(); ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS, - "CreateFileMappingA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%d)\n", h2, winerr); + "CreateFileMappingA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr); pNtClose(h); pNtClose(h1); pNtClose(h2); - pRtlFreeUnicodeString(&str); pNtClose(dir); + + RtlInitUnicodeString(&str, L"\\??\\PIPE\\named_pipe"); + attr.RootDirectory = 0; + timeout.QuadPart = -10000; + status = pNtCreateNamedPipeFile( &h, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, &timeout ); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "failed to create pipe %08lx\n", status); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = pNtCreateNamedPipeFile( &h, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN_IF, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, &timeout ); + ok(status == STATUS_SUCCESS, "failed to create pipe %08lx\n", status); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %08lx\n", status); + ok( iosb.Information == FILE_CREATED, "wrong info %Ix\n", iosb.Information ); + pNtClose( h ); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = pNtCreateNamedPipeFile( &h, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, &timeout ); + ok(status == STATUS_SUCCESS, "failed to create pipe %08lx\n", status); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %08lx\n", status); + ok( iosb.Information == FILE_CREATED, "wrong info %Ix\n", iosb.Information ); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = pNtCreateNamedPipeFile( &h1, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, &timeout ); + ok(status == STATUS_SUCCESS, "failed to create pipe %08lx\n", status); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %08lx\n", status); + ok( iosb.Information == FILE_OPENED, "wrong info %Ix\n", iosb.Information ); + pNtClose(h1); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = pNtCreateNamedPipeFile( &h1, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN_IF, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, &timeout ); + ok(status == STATUS_SUCCESS, "failed to create pipe %08lx\n", status); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %08lx\n", status); + ok( iosb.Information == FILE_OPENED, "wrong info %Ix\n", iosb.Information ); + pNtClose(h1); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = pNtCreateNamedPipeFile( &h1, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN_IF, FILE_PIPE_FULL_DUPLEX, + FALSE, FALSE, FALSE, 10, 256, 256, NULL ); + ok(status == STATUS_SUCCESS, "failed to create pipe %08lx\n", status); + ok( iosb.Status == STATUS_SUCCESS, "wrong status %08lx\n", status); + ok( iosb.Information == FILE_OPENED, "wrong info %Ix\n", iosb.Information ); + pNtClose(h1); + + h1 = CreateNamedPipeA( "\\\\.\\pipe\\named_pipe", PIPE_ACCESS_DUPLEX, + PIPE_READMODE_BYTE, 10, 256, 256, 1000, NULL ); + winerr = GetLastError(); + ok(h1 != 0 && winerr == ERROR_ALREADY_EXISTS, "CreateNamedPipeA got ret=%p (%ld)\n", h1, winerr); + pNtClose(h1); + pNtClose(h); } static void test_all_kernel_objects( UINT line, OBJECT_ATTRIBUTES *attr, @@ -388,78 +465,184 @@ static void test_all_kernel_objects( UINT line, OBJECT_ATTRIBUTES *attr, NTSTATUS status, status2; HANDLE ret, ret2; - pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" ); + RtlInitUnicodeString( &target, L"\\DosDevices" ); size.QuadPart = 4096; + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateMutant( &ret, GENERIC_ALL, attr, FALSE ); - ok( status == create_expect, "%u: NtCreateMutant failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateMutant failed %lx\n", line, status ); status2 = pNtOpenMutant( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenMutant failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenMutant failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateMutant handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenMutant handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateSemaphore( &ret, GENERIC_ALL, attr, 1, 2 ); - ok( status == create_expect, "%u: NtCreateSemaphore failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateSemaphore failed %lx\n", line, status ); status2 = pNtOpenSemaphore( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenSemaphore failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenSemaphore failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSemaphore handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); - status = pNtCreateEvent( &ret, GENERIC_ALL, attr, 1, 0 ); - ok( status == create_expect, "%u: NtCreateEvent failed %x\n", line, status ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenSemaphore handle %p\n", line, ret ); + ret = (HANDLE)0xdeadbeef; + status = pNtCreateSemaphore( &ret, GENERIC_ALL, attr, 2, 1 ); + ok( status == STATUS_INVALID_PARAMETER || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateSemaphore failed %lx\n", line, status ); + ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSemaphore handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; + status = pNtCreateEvent( &ret, GENERIC_ALL, attr, SynchronizationEvent, 0 ); + ok( status == create_expect, "%u: NtCreateEvent failed %lx\n", line, status ); status2 = pNtOpenEvent( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenEvent failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenEvent failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateEvent handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenEvent handle %p\n", line, ret ); + ret = (HANDLE)0xdeadbeef; + status = pNtCreateEvent( &ret, GENERIC_ALL, attr, 2, 0 ); + ok( status == STATUS_INVALID_PARAMETER || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateEvent failed %lx\n", line, status ); + ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateEvent handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, attr, 0 ); - ok( status == create_expect, "%u: NtCreateKeyedEvent failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateKeyedEvent failed %lx\n", line, status ); status2 = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenKeyedEvent failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenKeyedEvent failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateKeyedEvent handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenKeyedEvent handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateTimer( &ret, GENERIC_ALL, attr, NotificationTimer ); - ok( status == create_expect, "%u: NtCreateTimer failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateTimer failed %lx\n", line, status ); status2 = pNtOpenTimer( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenTimer failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenTimer failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateTimer handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenTimer handle %p\n", line, ret ); + ret = (HANDLE)0xdeadbeef; + status = pNtCreateTimer( &ret, GENERIC_ALL, attr, 2 ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER_4 || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateTimer failed %lx\n", line, status ); + ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateTimer handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateIoCompletion( &ret, GENERIC_ALL, attr, 0 ); - ok( status == create_expect, "%u: NtCreateCompletion failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateIoCompletion failed %lx\n", line, status ); status2 = pNtOpenIoCompletion( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenCompletion failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenIoCompletion failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateIoCompletion handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenIoCompletion handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateJobObject( &ret, GENERIC_ALL, attr ); - ok( status == create_expect, "%u: NtCreateJobObject failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateJobObject failed %lx\n", line, status ); status2 = pNtOpenJobObject( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenJobObject failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenJobObject failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateJobObject handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenJobObject handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, attr ); - ok( status == create_expect, "%u: NtCreateDirectoryObject failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateDirectoryObject failed %lx\n", line, status ); status2 = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenDirectoryObject failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenDirectoryObject failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateDirectoryObject handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenDirectoryObject handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, attr, &target ); - ok( status == create_expect, "%u: NtCreateSymbolicLinkObject failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateSymbolicLinkObject failed %lx\n", line, status ); status2 = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, attr ); - ok( status2 == open_expect, "%u: NtOpenSymbolicLinkObject failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenSymbolicLinkObject failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSymbolicLinkObject handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenSymbolicLinkObject handle %p\n", line, ret ); + ret = (HANDLE)0xdeadbeef; + target.MaximumLength = 0; + status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, attr, &target ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER_4 || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateSymbolicLinkObject failed %lx\n", line, status ); + ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSymbolicLinkObject handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; status = pNtCreateSection( &ret, SECTION_MAP_WRITE, attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); - ok( status == create_expect, "%u: NtCreateSection failed %x\n", line, status ); + ok( status == create_expect, "%u: NtCreateSection failed %lx\n", line, status ); status2 = pNtOpenSection( &ret2, SECTION_MAP_WRITE, attr ); - ok( status2 == open_expect, "%u: NtOpenSection failed %x\n", line, status2 ); + ok( status2 == open_expect, "%u: NtOpenSection failed %lx\n", line, status2 ); if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSection handle %p\n", line, ret ); if (!status2) pNtClose( ret2 ); - pRtlFreeUnicodeString( &target ); + else ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtOpenSection handle %p\n", line, ret ); + ret = (HANDLE)0xdeadbeef; + status = pNtCreateSection( &ret, SECTION_MAP_WRITE, attr, &size, 0x1234, SEC_COMMIT, 0 ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PAGE_PROTECTION || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateSection failed %lx\n", line, status ); + ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateSection handle %p\n", line, ret ); + + ret = ret2 = (HANDLE)0xdeadbeef; + status = pNtCreateDebugObject( &ret, DEBUG_ALL_ACCESS, attr, 0 ); + ok( status == create_expect, "%u: NtCreateDebugObject failed %lx\n", line, status ); + if (!status) pNtClose( ret ); + else ok( !ret || broken( ret == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateDebugObject handle %p\n", line, ret ); + status = pNtCreateDebugObject( &ret2, DEBUG_ALL_ACCESS, attr, 0xdead ); + ok( status == STATUS_INVALID_PARAMETER || + (status == STATUS_ACCESS_VIOLATION && create_expect == STATUS_ACCESS_VIOLATION), + "%u: NtCreateDebugObject failed %lx\n", line, status ); + ok( !ret2 || broken( ret2 == (HANDLE)0xdeadbeef ) /* vista */, + "%u: NtCreateDebugObject handle %p\n", line, ret ); } static void test_name_limits(void) { - static const WCHAR localW[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','L','o','c','a','l',0}; - static const WCHAR pipeW[] = {'\\','D','e','v','i','c','e','\\','N','a','m','e','d','P','i','p','e','\\'}; - static const WCHAR mailslotW[] = {'\\','D','e','v','i','c','e','\\','M','a','i','l','S','l','o','t','\\'}; - static const WCHAR registryW[] = {'\\','R','E','G','I','S','T','R','Y','\\','M','a','c','h','i','n','e','\\','S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\'}; + static const WCHAR pipeW[] = L"\\Device\\NamedPipe\\"; + static const WCHAR mailslotW[] = L"\\Device\\MailSlot\\"; + static const WCHAR registryW[] = L"\\REGISTRY\\Machine\\SOFTWARE\\Microsoft\\"; OBJECT_ATTRIBUTES attr, attr2, attr3; IO_STATUS_BLOCK iosb; LARGE_INTEGER size, timeout; @@ -475,106 +658,92 @@ static void test_name_limits(void) str.MaximumLength = 65534; for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i] = 'a'; size.QuadPart = 4096; - pRtlCreateUnicodeStringFromAsciiz( &target, "\\DosDevices" ); - - if (!(attr.RootDirectory = get_base_dir())) - { - win_skip( "couldn't find the BaseNamedObjects dir\n" ); - return; - } + RtlInitUnicodeString( &target, L"\\DosDevices" ); + attr.RootDirectory = get_base_dir(); str.Length = 0; status = pNtCreateMutant( &ret, GENERIC_ALL, &attr2, FALSE ); - ok( status == STATUS_SUCCESS, "%u: NtCreateMutant failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateMutant failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenMutant failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenMutant failed %lx\n", str.Length, status ); status = pNtOpenMutant( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenMutant failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenMutant failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateSemaphore( &ret, GENERIC_ALL, &attr2, 1, 2 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateSemaphore failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSemaphore failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSemaphore failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSemaphore failed %lx\n", str.Length, status ); status = pNtOpenSemaphore( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenSemaphore failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSemaphore failed %lx\n", str.Length, status ); pNtClose( ret ); - status = pNtCreateEvent( &ret, GENERIC_ALL, &attr2, 1, 0 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateEvent failed %x\n", str.Length, status ); + status = pNtCreateEvent( &ret, GENERIC_ALL, &attr2, SynchronizationEvent, 0 ); + ok( status == STATUS_SUCCESS, "%u: NtCreateEvent failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenEvent failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenEvent failed %lx\n", str.Length, status ); status = pNtOpenEvent( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenEvent failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenEvent failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, &attr2, 0 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateKeyedEvent failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateKeyedEvent failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenKeyedEvent failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenKeyedEvent failed %lx\n", str.Length, status ); status = pNtOpenKeyedEvent( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenKeyedEvent failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenKeyedEvent failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateTimer( &ret, GENERIC_ALL, &attr2, NotificationTimer ); - ok( status == STATUS_SUCCESS, "%u: NtCreateTimer failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateTimer failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenTimer failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenTimer failed %lx\n", str.Length, status ); status = pNtOpenTimer( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenTimer failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenTimer failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateIoCompletion( &ret, GENERIC_ALL, &attr2, 0 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateCompletion failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateCompletion failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenCompletion failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenCompletion failed %lx\n", str.Length, status ); status = pNtOpenIoCompletion( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenCompletion failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenCompletion failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateJobObject( &ret, GENERIC_ALL, &attr2 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateJobObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateJobObject failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenJobObject failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenJobObject failed %lx\n", str.Length, status ); status = pNtOpenJobObject( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenJobObject failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenJobObject failed %lx\n", str.Length, status ); pNtClose( ret ); status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, &attr2 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateDirectoryObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateDirectoryObject failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_DENIED), /* winxp */ - "%u: NtOpenDirectoryObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenDirectoryObject failed %lx\n", str.Length, status ); if (!status) pNtClose( ret2 ); status = pNtOpenDirectoryObject( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_SUCCESS, "%u: NtOpenDirectoryObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenDirectoryObject failed %lx\n", str.Length, status ); pNtClose( ret2 ); pNtClose( ret ); status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, &attr2, &target ); - ok( status == STATUS_SUCCESS, "%u: NtCreateSymbolicLinkObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSymbolicLinkObject failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSymbolicLinkObject failed %lx\n", str.Length, status ); status = pNtOpenSymbolicLinkObject( &ret2, GENERIC_ALL, &attr3 ); - ok( status == STATUS_SUCCESS, "%u: NtOpenSymbolicLinkObject failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenSymbolicLinkObject failed %lx\n", str.Length, status ); pNtClose( ret2 ); pNtClose( ret ); status = pNtCreateSection( &ret, SECTION_MAP_WRITE, &attr2, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); - ok( status == STATUS_SUCCESS, "%u: NtCreateSection failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateSection failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSection failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSection failed %lx\n", str.Length, status ); status = pNtOpenSection( &ret2, SECTION_MAP_WRITE, &attr3 ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE, - "%u: NtOpenSection failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "%u: NtOpenSection failed %lx\n", str.Length, status ); pNtClose( ret ); str.Length = 67; @@ -601,86 +770,166 @@ static void test_name_limits(void) attr2.ObjectName = attr3.ObjectName = NULL; test_all_kernel_objects( __LINE__, &attr2, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_INVALID ); test_all_kernel_objects( __LINE__, &attr3, STATUS_SUCCESS, STATUS_OBJECT_PATH_SYNTAX_BAD ); + attr2.ObjectName = attr3.ObjectName = (void *)0xdeadbeef; + test_all_kernel_objects( __LINE__, &attr2, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION ); + test_all_kernel_objects( __LINE__, &attr3, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION ); + attr2.ObjectName = attr3.ObjectName = &str2; + str2.Buffer = (WCHAR *)((char *)pipeW + 1); /* misaligned buffer */ + str2.Length = 3; + test_all_kernel_objects( __LINE__, &attr2, STATUS_DATATYPE_MISALIGNMENT, STATUS_DATATYPE_MISALIGNMENT ); + test_all_kernel_objects( __LINE__, &attr3, STATUS_DATATYPE_MISALIGNMENT, STATUS_DATATYPE_MISALIGNMENT ); + str2.Buffer = (WCHAR *)0xdeadbee0; + str2.Length = 2; + test_all_kernel_objects( __LINE__, &attr2, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION ); + test_all_kernel_objects( __LINE__, &attr3, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION ); attr3.ObjectName = &str2; - pRtlInitUnicodeString( &str2, localW ); + pRtlInitUnicodeString( &str2, L"\\BaseNamedObjects\\Local" ); status = pNtOpenSymbolicLinkObject( &ret, SYMBOLIC_LINK_QUERY, &attr3 ); - ok( status == STATUS_SUCCESS, "can't open BaseNamedObjects\\Local %x\n", status ); + ok( status == STATUS_SUCCESS, "can't open BaseNamedObjects\\Local %lx\n", status ); attr3.ObjectName = &str; attr3.RootDirectory = ret; test_all_kernel_objects( __LINE__, &attr3, STATUS_OBJECT_TYPE_MISMATCH, STATUS_OBJECT_TYPE_MISMATCH ); pNtClose( attr3.RootDirectory ); status = pNtCreateMutant( &ret, GENERIC_ALL, NULL, FALSE ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateMutant failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateMutant failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateMutant( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, FALSE ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateMutant failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenMutant( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenMutant failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenMutant failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenMutant( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenMutant failed %lx\n", status ); + status = pNtCreateSemaphore( &ret, GENERIC_ALL, NULL, 1, 2 ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateSemaphore failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateSemaphore failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateSemaphore( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, 1, 2 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateSemaphore failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenSemaphore( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSemaphore failed %x\n", status ); - status = pNtCreateEvent( &ret, GENERIC_ALL, NULL, 1, 0 ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateEvent failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSemaphore failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenSemaphore( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenSemaphore failed %lx\n", status ); + + status = pNtCreateEvent( &ret, GENERIC_ALL, NULL, SynchronizationEvent, 0 ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateEvent failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateEvent( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, SynchronizationEvent, 0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateEvent failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenEvent( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenEvent failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenEvent failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenEvent( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenEvent failed %lx\n", status ); + status = pNtCreateKeyedEvent( &ret, GENERIC_ALL, NULL, 0 ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateKeyedEvent failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateKeyedEvent failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateKeyedEvent( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, 0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKeyedEvent failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenKeyedEvent( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenKeyedEvent failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenKeyedEvent failed %lx\n", status ); + ok( !ret, "handle set %p\n", ret ); + status = pNtOpenKeyedEvent( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKeyedEvent failed %lx\n", status ); + status = pNtCreateTimer( &ret, GENERIC_ALL, NULL, NotificationTimer ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateTimer failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateTimer failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateTimer( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, NotificationTimer ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateTimer failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenTimer( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenTimer failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenTimer failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenTimer( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenTimer failed %lx\n", status ); + status = pNtCreateIoCompletion( &ret, GENERIC_ALL, NULL, 0 ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateCompletion failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateCompletion failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateIoCompletion( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, 0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateCompletion failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenIoCompletion( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenCompletion failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenCompletion failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenIoCompletion( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenCompletion failed %lx\n", status ); + status = pNtCreateJobObject( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateJobObject failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateJobObject failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateJobObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateJobObject failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenJobObject( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenJobObject failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenJobObject failed %lx\n", status ); + ok( !ret || broken(ret == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", ret ); + status = pNtOpenJobObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenJobObject failed %lx\n", status ); + status = pNtCreateDirectoryObject( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateDirectoryObject failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateDirectoryObject failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateDirectoryObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateDirectoryObject failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenDirectoryObject( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenDirectoryObject failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenDirectoryObject failed %lx\n", status ); + ok( !ret, "handle set %p\n", ret ); + status = pNtOpenDirectoryObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenDirectoryObject failed %lx\n", status ); + status = pNtCreateSymbolicLinkObject( &ret, GENERIC_ALL, NULL, &target ); - ok( status == STATUS_ACCESS_VIOLATION || broken( status == STATUS_SUCCESS), /* winxp */ - "NULL: NtCreateSymbolicLinkObject failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateSymbolicLinkObject failed %lx\n", status ); if (!status) pNtClose( ret ); + status = pNtCreateSymbolicLinkObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL, &target ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateSymbolicLinkObject failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenSymbolicLinkObject( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSymbolicLinkObject failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSymbolicLinkObject failed %lx\n", status ); + ok( !ret, "handle set %p\n", ret ); + status = pNtOpenSymbolicLinkObject( (HANDLE *)0xdeadbee0, GENERIC_ALL, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenSymbolicLinkObject failed %lx\n", status ); + status = pNtCreateSection( &ret, SECTION_MAP_WRITE, NULL, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); - ok( status == STATUS_SUCCESS, "NULL: NtCreateSection failed %x\n", status ); + ok( status == STATUS_SUCCESS, "NULL: NtCreateSection failed %lx\n", status ); pNtClose( ret ); + status = pNtCreateSection( (HANDLE *)0xdeadbee0, SECTION_MAP_WRITE, NULL, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateSection failed %lx\n", status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenSection( &ret, SECTION_MAP_WRITE, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSection failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtOpenSection failed %lx\n", status ); + ok( !ret, "handle set %p\n", ret ); + status = pNtOpenSection( (HANDLE *)0xdeadbee0, SECTION_MAP_WRITE, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenSection failed %lx\n", status ); attr2.ObjectName = attr3.ObjectName = &str; /* named pipes */ - memcpy( str.Buffer, pipeW, sizeof(pipeW) ); - for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(pipeW)/sizeof(WCHAR)] = 'a'; + wcscpy( str.Buffer, pipeW ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + wcslen( pipeW )] = 'a'; str.Length = 0; attr.RootDirectory = 0; attr.Attributes = OBJ_CASE_INSENSITIVE; timeout.QuadPart = -10000; status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); str.Length = 67; status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); str.Length = 128; for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) { @@ -688,124 +937,127 @@ static void test_name_limits(void) FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); if (attr.Length == sizeof(attr)) { - ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); pNtClose( ret ); } else ok( status == STATUS_INVALID_PARAMETER, - "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); } attr.Length = sizeof(attr); str.Length = 65532; status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); pNtClose( ret ); str.Length = 65534; status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateNamedPipeFile failed %lx\n", str.Length, status ); attr3.RootDirectory = 0; attr2.ObjectName = attr3.ObjectName = NULL; status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr2, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateNamedPipeFile failed %lx\n", status ); status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, &attr3, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateNamedPipeFile failed %lx\n", status ); status = pNtCreateNamedPipeFile( &ret, GENERIC_ALL, NULL, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateNamedPipeFile failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateNamedPipeFile failed %lx\n", status ); attr2.ObjectName = attr3.ObjectName = &str; /* mailslots */ - memcpy( str.Buffer, mailslotW, sizeof(mailslotW) ); - for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(mailslotW)/sizeof(WCHAR)] = 'a'; + wcscpy( str.Buffer, mailslotW ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + wcslen( mailslotW )] = 'a'; str.Length = 0; status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); str.Length = 67; status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); str.Length = 128; for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) { status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); if (attr.Length == sizeof(attr)) { - ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); pNtClose( ret ); } else ok( status == STATUS_INVALID_PARAMETER, - "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); } attr.Length = sizeof(attr); str.Length = 65532; status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); pNtClose( ret ); str.Length = 65534; status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "%u: NtCreateMailslotFile failed %lx\n", str.Length, status ); attr3.RootDirectory = 0; attr2.ObjectName = attr3.ObjectName = NULL; status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr2, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateMailslotFile failed %x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NULL: NtCreateMailslotFile failed %lx\n", status ); status = pNtCreateMailslotFile( &ret, GENERIC_ALL, &attr3, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateMailslotFile failed %x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NULL: NtCreateMailslotFile failed %lx\n", status ); status = pNtCreateMailslotFile( &ret, GENERIC_ALL, NULL, &iosb, 0, 0, 0, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateMailslotFile failed %x\n", status ); + ok( status == STATUS_INVALID_PARAMETER, "NULL: NtCreateMailslotFile failed %lx\n", status ); attr2.ObjectName = attr3.ObjectName = &str; /* registry keys */ - memcpy( str.Buffer, registryW, sizeof(registryW) ); - for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + sizeof(registryW)/sizeof(WCHAR)] = 'a'; + wcscpy( str.Buffer, registryW ); + for (i = 0; i < 65536 / sizeof(WCHAR); i++) str.Buffer[i + wcslen(registryW)] = 'a'; str.Length = 0; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); - todo_wine - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL ); - ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_HANDLE, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 ); - ok( status == STATUS_INVALID_HANDLE, "%u: NtOpenKey failed %x\n", str.Length, status ); - str.Length = sizeof(registryW) + 250 * sizeof(WCHAR) + 1; + ok( status == STATUS_INVALID_HANDLE, "%u: NtOpenKey failed %lx\n", str.Length, status ); + str.Length = (wcslen( registryW ) + 250) * sizeof(WCHAR) + 1; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_INVALID_PARAMETER || broken( status == STATUS_SUCCESS ), /* wow64 */ - "%u: NtCreateKey failed %x\n", str.Length, status ); + "%u: NtCreateKey failed %lx\n", str.Length, status ); if (!status) { pNtDeleteKey( ret ); pNtClose( ret ); } - str.Length = sizeof(registryW) + 256 * sizeof(WCHAR); + str.Length = (wcslen( registryW ) + 256) * sizeof(WCHAR); status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "%u: NtCreateKey failed %x\n", str.Length, status ); + "%u: NtCreateKey failed %lx\n", str.Length, status ); + status = pNtCreateKey( (HANDLE *)0xdeadbee0, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); + ok( status == STATUS_ACCESS_VIOLATION, "%u: NtCreateKey failed %lx\n", str.Length, status ); if (!status) { status = pNtOpenKey( &ret2, KEY_READ, &attr ); - ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %lx\n", str.Length, status ); pNtClose( ret2 ); + status = pNtOpenKey( (HANDLE *)0xdeadbee0, KEY_READ, &attr ); + ok( status == STATUS_ACCESS_VIOLATION, "%u: NtOpenKey failed %lx\n", str.Length, status ); attr3.RootDirectory = ret; str.Length = 0; status = pNtOpenKey( &ret2, KEY_READ, &attr3 ); - ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %lx\n", str.Length, status ); pNtClose( ret2 ); pNtDeleteKey( ret ); pNtClose( ret ); - str.Length = sizeof(registryW) + 256 * sizeof(WCHAR); + str.Length = (wcslen( registryW ) + 256) * sizeof(WCHAR); for (attr.Length = 0; attr.Length <= 2 * sizeof(attr); attr.Length++) { if (attr.Length == sizeof(attr)) { status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); - ok( status == STATUS_SUCCESS, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtOpenKey( &ret2, KEY_READ, &attr ); - ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_SUCCESS, "%u: NtOpenKey failed %lx\n", str.Length, status ); pNtClose( ret2 ); pNtDeleteKey( ret ); pNtClose( ret ); @@ -813,19 +1065,19 @@ static void test_name_limits(void) else { status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtOpenKey( &ret2, KEY_READ, &attr ); - ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %lx\n", str.Length, status ); } } attr.Length = sizeof(attr); } - str.Length = sizeof(registryW) + 256 * sizeof(WCHAR) + 1; + str.Length = (wcslen( registryW ) + 256) * sizeof(WCHAR) + 1; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_INVALID_PARAMETER || broken( status == STATUS_SUCCESS ), /* win7 */ - "%u: NtCreateKey failed %x\n", str.Length, status ); + "%u: NtCreateKey failed %lx\n", str.Length, status ); if (!status) { pNtDeleteKey( ret ); @@ -835,66 +1087,63 @@ static void test_name_limits(void) ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_INVALID_PARAMETER || broken( status == STATUS_OBJECT_NAME_NOT_FOUND ), /* wow64 */ - "%u: NtOpenKey failed %x\n", str.Length, status ); + "%u: NtOpenKey failed %lx\n", str.Length, status ); str.Length++; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); - todo_wine - ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %lx\n", str.Length, status ); str.Length = 2000; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); - ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtCreateKey failed %lx\n", str.Length, status ); status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); - todo_wine - ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %x\n", str.Length, status ); + ok( status == STATUS_INVALID_PARAMETER, "%u: NtOpenKey failed %lx\n", str.Length, status ); /* some Windows versions change the error past 2050 chars, others past 4066 chars, some don't */ str.Length = 5000; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); ok( status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INVALID_PARAMETER, - "%u: NtCreateKey failed %x\n", str.Length, status ); + "%u: NtCreateKey failed %lx\n", str.Length, status ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); - todo_wine ok( status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INVALID_PARAMETER, - "%u: NtOpenKey failed %x\n", str.Length, status ); + "%u: NtOpenKey failed %lx\n", str.Length, status ); + ok( !ret, "handle set %p\n", ret ); str.Length = 65534; + ret = (HANDLE)0xdeadbeef; status = pNtCreateKey( &ret, GENERIC_ALL, &attr, 0, NULL, 0, NULL ); ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL, - "%u: NtCreateKey failed %x\n", str.Length, status ); + "%u: NtCreateKey failed %lx\n", str.Length, status ); + ok( !ret, "handle set %p\n", ret ); + ret = (HANDLE)0xdeadbeef; status = pNtOpenKey( &ret, GENERIC_ALL, &attr ); - todo_wine ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL, - "%u: NtOpenKey failed %x\n", str.Length, status ); + "%u: NtOpenKey failed %lx\n", str.Length, status ); + ok( !ret, "handle set %p\n", ret ); attr3.RootDirectory = 0; attr2.ObjectName = attr3.ObjectName = NULL; status = pNtCreateKey( &ret, GENERIC_ALL, &attr2, 0, NULL, 0, NULL ); - todo_wine - ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "NULL: NtCreateKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %lx\n", status ); status = pNtCreateKey( &ret, GENERIC_ALL, &attr3, 0, NULL, 0, NULL ); - todo_wine - ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %lx\n", status ); status = pNtCreateKey( &ret, GENERIC_ALL, NULL, 0, NULL, 0, NULL ); - ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtCreateKey failed %lx\n", status ); status = pNtOpenKey( &ret, GENERIC_ALL, &attr2 ); - ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE, - "NULL: NtOpenKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %lx\n", status ); status = pNtOpenKey( &ret, GENERIC_ALL, &attr3 ); - ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %lx\n", status ); status = pNtOpenKey( &ret, GENERIC_ALL, NULL ); - ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %x\n", status ); + ok( status == STATUS_ACCESS_VIOLATION, "NULL: NtOpenKey failed %lx\n", status ); attr2.ObjectName = attr3.ObjectName = &str; - pRtlFreeUnicodeString( &str ); - pRtlFreeUnicodeString( &target ); + HeapFree( GetProcessHeap(), 0, str.Buffer ); } static void test_directory(void) @@ -903,53 +1152,50 @@ static void test_directory(void) UNICODE_STRING str; OBJECT_ATTRIBUTES attr; HANDLE dir, dir1, h, h2; - BOOL is_nt4; + WCHAR buffer[256]; + ULONG len, full_len; /* No name and/or no attributes */ status = pNtCreateDirectoryObject(NULL, DIRECTORY_QUERY, &attr); - ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "NtCreateDirectoryObject should have failed with STATUS_ACCESS_VIOLATION got(%08x)\n", status); + ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER /* wow64 */, "got %#lx\n", status); status = pNtOpenDirectoryObject(NULL, DIRECTORY_QUERY, &attr); - ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "NtOpenDirectoryObject should have failed with STATUS_ACCESS_VIOLATION got(%08x)\n", status); + ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER /* wow64 */, "got %#lx\n", status); status = pNtCreateDirectoryObject(&h, DIRECTORY_QUERY, NULL); - ok(status == STATUS_SUCCESS, "Failed to create Directory without attributes(%08x)\n", status); + ok(status == STATUS_SUCCESS, "Failed to create Directory without attributes(%08lx)\n", status); pNtClose(h); status = pNtOpenDirectoryObject(&h, DIRECTORY_QUERY, NULL); ok(status == STATUS_INVALID_PARAMETER, - "NtOpenDirectoryObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status); + "NtOpenDirectoryObject should have failed with STATUS_INVALID_PARAMETER got(%08lx)\n", status); InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08lx\n", status ); /* Bad name */ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, ""); + RtlInitUnicodeString(&str, L""); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); pNtClose(h); status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08lx\n", status ); pNtClose(dir); - DIR_TEST_CREATE_OPEN( "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD ); - DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID ); - DIR_TEST_CREATE_OPEN( "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID ); - DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID ); - DIR_TEST_CREATE_OPEN( "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); + check_create_open_dir( NULL, L"BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD ); + check_create_open_dir( NULL, L"\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID ); + check_create_open_dir( NULL, L"\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID ); + check_create_open_dir( NULL, L"\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID ); + check_create_open_dir( NULL, L"\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test"); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\om.c-test"); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); status = pNtOpenDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to open directory %08lx\n", status ); pNtClose(h); pNtClose(dir1); @@ -957,201 +1203,172 @@ static void test_directory(void) /* Use of root directory */ /* Can't use symlinks as a directory */ - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local"); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\Local"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); status = pNtOpenSymbolicLinkObject(&dir, SYMBOLIC_LINK_QUERY, &attr); - is_nt4 = (status == STATUS_OBJECT_NAME_NOT_FOUND); /* nt4 doesn't have Local\\ symlink */ - if (!is_nt4) - { - WCHAR buffer[256]; - ULONG len, full_len; - ok(status == STATUS_SUCCESS, "Failed to open SymbolicLink(%08x)\n", status); - pRtlFreeUnicodeString(&str); - InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); - status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateDirectoryObject got %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok(status == STATUS_SUCCESS, "Failed to open SymbolicLink(%08lx)\n", status); + InitializeObjectAttributes(&attr, &str, 0, dir, NULL); + RtlInitUnicodeString(&str, L"one more level"); + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateDirectoryObject got %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\Local\\om.c-test" ); - InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); - status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString( &str ); - pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test" ); - InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); - status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "Failed to open directory %08x\n", status ); - if (!status) pNtClose(h); - pRtlFreeUnicodeString( &str ); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\Local\\om.c-test" ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); + RtlInitUnicodeString( &str, L"om.c-test" ); + InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); + status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "Failed to open directory %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-event" ); - InitializeObjectAttributes( &attr, &str, 0, dir1, NULL ); - status = pNtCreateEvent( &h, GENERIC_ALL, &attr, 1, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateEvent failed %x\n", status ); - status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenEvent failed %x\n", status ); - pNtClose( h2 ); - pRtlFreeUnicodeString( &str ); - pRtlCreateUnicodeStringFromAsciiz( &str, "om.c-test\\om.c-event" ); - InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); - status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenEvent failed %x\n", status ); - pRtlFreeUnicodeString( &str ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\BasedNamedObjects\\Local\\om.c-test\\om.c-event" ); - InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); - status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenEvent failed %x\n", status ); - pRtlFreeUnicodeString( &str ); - pNtClose( h ); - pNtClose( dir1 ); + RtlInitUnicodeString( &str, L"om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, dir1, NULL ); + status = pNtCreateEvent( &h, GENERIC_ALL, &attr, SynchronizationEvent, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateEvent failed %lx\n", status ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenEvent failed %lx\n", status ); + pNtClose( h2 ); + RtlInitUnicodeString( &str, L"om.c-test\\om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, dir, NULL ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenEvent failed %lx\n", status ); + RtlInitUnicodeString( &str, L"\\BasedNamedObjects\\Local\\om.c-test\\om.c-event" ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + status = pNtOpenEvent( &h2, GENERIC_ALL, &attr ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenEvent failed %lx\n", status ); + pNtClose( h ); + pNtClose( dir1 ); - str.Buffer = buffer; - str.MaximumLength = sizeof(buffer); - len = 0xdeadbeef; - memset( buffer, 0xaa, sizeof(buffer) ); - status = pNtQuerySymbolicLinkObject( dir, &str, &len ); - ok( status == STATUS_SUCCESS, "NtQuerySymbolicLinkObject failed %08x\n", status ); - if (status != STATUS_SUCCESS) - goto error; - full_len = str.Length + sizeof(WCHAR); - ok( len == full_len, "bad length %u/%u\n", len, full_len ); - if (len == full_len) - ok( buffer[len / sizeof(WCHAR) - 1] == 0, "no terminating null\n" ); + str.Buffer = buffer; + str.MaximumLength = sizeof(buffer); + len = 0xdeadbeef; + memset( buffer, 0xaa, sizeof(buffer) ); + status = pNtQuerySymbolicLinkObject( dir, &str, &len ); + ok( status == STATUS_SUCCESS, "NtQuerySymbolicLinkObject failed %08lx\n", status ); + full_len = str.Length + sizeof(WCHAR); + ok( len == full_len, "bad length %lu/%lu\n", len, full_len ); + ok( buffer[len / sizeof(WCHAR) - 1] == 0, "no terminating null\n" ); - str.MaximumLength = str.Length; - len = 0xdeadbeef; - status = pNtQuerySymbolicLinkObject( dir, &str, &len ); - ok( status == STATUS_BUFFER_TOO_SMALL, "NtQuerySymbolicLinkObject failed %08x\n", status ); - ok( len == full_len, "bad length %u/%u\n", len, full_len ); + str.MaximumLength = str.Length; + str.Length = 0x4444; + len = 0xdeadbeef; + status = pNtQuerySymbolicLinkObject( dir, &str, &len ); + ok( status == STATUS_BUFFER_TOO_SMALL, "NtQuerySymbolicLinkObject failed %08lx\n", status ); + ok( len == full_len, "bad length %lu/%lu\n", len, full_len ); + ok( str.Length == 0x4444, "len set to %x\n", str.Length ); - str.MaximumLength = 0; - len = 0xdeadbeef; - status = pNtQuerySymbolicLinkObject( dir, &str, &len ); - ok( status == STATUS_BUFFER_TOO_SMALL, "NtQuerySymbolicLinkObject failed %08x\n", status ); - ok( len == full_len, "bad length %u/%u\n", len, full_len ); + str.MaximumLength = 0; + str.Length = 0x4444; + len = 0xdeadbeef; + status = pNtQuerySymbolicLinkObject( dir, &str, &len ); + ok( status == STATUS_BUFFER_TOO_SMALL, "NtQuerySymbolicLinkObject failed %08lx\n", status ); + ok( len == full_len, "bad length %lu/%lu\n", len, full_len ); + ok( str.Length == 0x4444, "len set to %x\n", str.Length ); - str.MaximumLength = str.Length + sizeof(WCHAR); - len = 0xdeadbeef; - status = pNtQuerySymbolicLinkObject( dir, &str, &len ); - ok( status == STATUS_SUCCESS, "NtQuerySymbolicLinkObject failed %08x\n", status ); - ok( len == full_len, "bad length %u/%u\n", len, full_len ); + str.MaximumLength = full_len; + str.Length = 0x4444; + len = 0xdeadbeef; + status = pNtQuerySymbolicLinkObject( dir, &str, &len ); + ok( status == STATUS_SUCCESS, "NtQuerySymbolicLinkObject failed %08lx\n", status ); + ok( len == full_len, "bad length %lu/%lu\n", len, full_len ); + ok( str.Length == full_len - sizeof(WCHAR), "len set to %x\n", str.Length ); -error: - pNtClose(dir); - } + pNtClose(dir); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects"); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to open directory %08lx\n", status ); InitializeObjectAttributes(&attr, NULL, 0, dir, NULL); status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenDirectoryObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenDirectoryObject got %08lx\n", status ); + + check_create_open_dir( dir, L"", STATUS_SUCCESS ); + check_create_open_dir( dir, L"\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); + check_create_open_dir( dir, L"\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD ); + check_create_open_dir( dir, L"\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); + check_create_open_dir( dir, L"om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - DIR_TEST_CREATE_OPEN( "", STATUS_SUCCESS ); - DIR_TEST_CREATE_OPEN( "\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); - DIR_TEST_CREATE_OPEN( "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD ); - DIR_TEST_CREATE_OPEN( "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD ); - DIR_TEST_CREATE_OPEN( "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND ); - - pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test"); + RtlInitUnicodeString(&str, L"om.c-test"); status = pNtCreateDirectoryObject( &dir1, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to open directory %08lx\n", status ); pNtClose(h); pNtClose(dir1); pNtClose(dir); /* Nested directories */ - pRtlCreateUnicodeStringFromAsciiz(&str, "\\"); + RtlInitUnicodeString(&str, L"\\"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); + ok( status == STATUS_SUCCESS, "Failed to open directory %08lx\n", status ); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); status = pNtOpenDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenDirectoryObject got %08lx\n", status ); pNtClose(dir); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test"); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\om.c-test"); status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test\\one more level"); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\om.c-test\\one more level"); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); pNtClose(h); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); + RtlInitUnicodeString(&str, L"one more level"); status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); pNtClose(h); pNtClose(dir); - if (!is_nt4) - { - InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Global\\om.c-test"); - status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local\\om.c-test\\one more level"); - status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); - pNtClose(h); - InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "one more level"); - status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to create directory %08x\n", status ); - pRtlFreeUnicodeString(&str); - pNtClose(h); - pNtClose(dir); - } + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\Global\\om.c-test"); + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\Local\\om.c-test\\one more level"); + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); + pNtClose(h); + InitializeObjectAttributes(&attr, &str, 0, dir, NULL); + RtlInitUnicodeString(&str, L"one more level"); + status = pNtCreateDirectoryObject( &h, DIRECTORY_QUERY, &attr ); + ok( status == STATUS_SUCCESS, "Failed to create directory %08lx\n", status ); + pNtClose(h); + pNtClose(dir); /* Create other objects using RootDirectory */ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects"); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects"); status = pNtOpenDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); - ok( status == STATUS_SUCCESS, "Failed to open directory %08x\n", status ); - pRtlFreeUnicodeString(&str); + ok( status == STATUS_SUCCESS, "Failed to open directory %08lx\n", status ); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); /* Test invalid paths */ - pRtlCreateUnicodeStringFromAsciiz(&str, "\\om.c-mutant"); + RtlInitUnicodeString(&str, L"\\om.c-mutant"); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, - "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08x)\n", status); - pRtlFreeUnicodeString(&str); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\om.c-mutant\\"); + "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status); + RtlInitUnicodeString(&str, L"\\om.c-mutant\\"); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, - "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08x)\n", status); - pRtlFreeUnicodeString(&str); + "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status); - pRtlCreateUnicodeStringFromAsciiz(&str, "om.c\\-mutant"); + RtlInitUnicodeString(&str, L"om.c\\-mutant"); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); ok(status == STATUS_OBJECT_PATH_NOT_FOUND, - "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08x)\n", status); - pRtlFreeUnicodeString(&str); + "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status); - pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-mutant"); + RtlInitUnicodeString(&str, L"om.c-mutant"); status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE); - ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status); - pRtlFreeUnicodeString(&str); + ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08lx)\n", status); pNtClose(h); pNtClose(dir); @@ -1162,221 +1379,398 @@ static void test_symboliclink(void) NTSTATUS status; UNICODE_STRING str, target; OBJECT_ATTRIBUTES attr; - HANDLE dir, link, h; + HANDLE dir, link, h, h2; IO_STATUS_BLOCK iosb; /* No name and/or no attributes */ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices"); + RtlInitUnicodeString(&target, L"\\DosDevices"); status = pNtCreateSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "NtCreateSymbolicLinkObject got %08x\n", status ); + ok(status == STATUS_ACCESS_VIOLATION, "got %#lx\n", status); status = pNtOpenSymbolicLinkObject( NULL, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "NtOpenSymbolicLinkObject got %08x\n", status ); + ok(status == STATUS_ACCESS_VIOLATION, "got %#lx\n", status); status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, NULL); ok(status == STATUS_ACCESS_VIOLATION, - "NtCreateSymbolicLinkObject should have failed with STATUS_ACCESS_VIOLATION got(%08x)\n", status); + "NtCreateSymbolicLinkObject should have failed with STATUS_ACCESS_VIOLATION got(%08lx)\n", status); status = pNtOpenSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL); ok(status == STATUS_INVALID_PARAMETER, - "NtOpenSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status); + "NtOpenSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08lx)\n", status); /* No attributes */ status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, &target); ok(status == STATUS_SUCCESS || status == STATUS_ACCESS_VIOLATION, /* nt4 */ - "NtCreateSymbolicLinkObject failed(%08x)\n", status); - pRtlFreeUnicodeString(&target); - if (!status) pNtClose(h); + "NtCreateSymbolicLinkObject failed(%08lx)\n", status); InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); + memset(&target, 0, sizeof(target)); status = pNtCreateSymbolicLinkObject(&link, SYMBOLIC_LINK_QUERY, &attr, &target); - ok(status == STATUS_INVALID_PARAMETER || - broken(status == STATUS_SUCCESS), /* nt4 */ - "NtCreateSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08x)\n", status); - if (!status) pNtClose(h); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); status = pNtOpenSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, &attr); ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, - "NtOpenSymbolicLinkObject should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08x)\n", status); + "NtOpenSymbolicLinkObject should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status); /* Bad name */ - pRtlCreateUnicodeStringFromAsciiz(&target, "anywhere"); + RtlInitUnicodeString(&target, L"anywhere"); InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, ""); + RtlInitUnicodeString(&str, L""); status = pNtCreateSymbolicLinkObject(&link, SYMBOLIC_LINK_QUERY, &attr, &target); - ok(status == STATUS_SUCCESS, "Failed to create SymbolicLink(%08x)\n", status); + ok(status == STATUS_SUCCESS, "Failed to create SymbolicLink(%08lx)\n", status); status = pNtOpenSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, &attr); ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, - "NtOpenSymbolicLinkObject should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08x)\n", status); + "NtOpenSymbolicLinkObject should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status); pNtClose(link); - pRtlFreeUnicodeString(&str); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\"); + RtlInitUnicodeString(&str, L"\\"); + attr.Attributes = OBJ_OPENIF; status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, &attr, &target); - todo_wine ok(status == STATUS_OBJECT_TYPE_MISMATCH, - "NtCreateSymbolicLinkObject should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08x)\n", status); - pRtlFreeUnicodeString(&str); - pRtlFreeUnicodeString(&target); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, + "NtCreateSymbolicLinkObject should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status); + attr.Attributes = 0; + status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, &attr, &target); + todo_wine + ok(status == STATUS_OBJECT_TYPE_MISMATCH, + "NtCreateSymbolicLinkObject should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status); - pRtlCreateUnicodeStringFromAsciiz( &target, "->Somewhere"); + RtlInitUnicodeString( &target, L"->Somewhere"); - pRtlCreateUnicodeStringFromAsciiz( &str, "BaseNamedObjects" ); + RtlInitUnicodeString( &str, L"BaseNamedObjects" ); status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateSymbolicLinkObject got %08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateSymbolicLinkObject got %08lx\n", status ); status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenSymbolicLinkObject got %08x\n", status ); - pRtlFreeUnicodeString( &str ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenSymbolicLinkObject got %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\" ); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\" ); status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08lx\n", status ); status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); - pRtlFreeUnicodeString( &str ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\\\BaseNamedObjects" ); + RtlInitUnicodeString( &str, L"\\\\BaseNamedObjects" ); status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08lx\n", status ); status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); - pRtlFreeUnicodeString( &str ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\\\om.c-test" ); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\\\om.c-test" ); status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08x\n", status ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtCreateSymbolicLinkObject got %08lx\n", status ); status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08x\n", status ); - pRtlFreeUnicodeString( &str ); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenSymbolicLinkObject got %08lx\n", status ); - pRtlCreateUnicodeStringFromAsciiz( &str, "\\BaseNamedObjects\\om.c-test\\" ); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\om.c-test\\" ); status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); - ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND, - "NtCreateSymbolicLinkObject got %08x\n", status ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "got %#lx\n", status ); status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); - ok( status == STATUS_OBJECT_NAME_INVALID || status == STATUS_OBJECT_PATH_NOT_FOUND, - "NtOpenSymbolicLinkObject got %08x\n", status ); - pRtlFreeUnicodeString( &str ); - pRtlFreeUnicodeString(&target); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "got %#lx\n", status ); /* Compound test */ - if (!(dir = get_base_dir())) - { - win_skip( "couldn't find the BaseNamedObjects dir\n" ); - return; - } - + dir = get_base_dir(); InitializeObjectAttributes(&attr, &str, 0, dir, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "test-link"); - pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices"); + RtlInitUnicodeString(&str, L"test-link"); + RtlInitUnicodeString(&target, L"\\DosDevices"); status = pNtCreateSymbolicLinkObject(&link, SYMBOLIC_LINK_QUERY, &attr, &target); - ok(status == STATUS_SUCCESS, "Failed to create SymbolicLink(%08x)\n", status); - pRtlFreeUnicodeString(&str); - pRtlFreeUnicodeString(&target); + ok(status == STATUS_SUCCESS, "Failed to create SymbolicLink(%08lx)\n", status); - pRtlCreateUnicodeStringFromAsciiz(&str, "test-link\\NUL"); + RtlInitUnicodeString(&str, L"test-link\\NUL"); status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, 0); - ok(status == STATUS_SUCCESS, "Failed to open NUL device(%08x)\n", status); + ok(status == STATUS_SUCCESS, "Failed to open NUL device(%08lx)\n", status); status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE); - ok(status == STATUS_SUCCESS, "Failed to open NUL device(%08x)\n", status); - pRtlFreeUnicodeString(&str); + ok(status == STATUS_SUCCESS, "Failed to open NUL device(%08lx)\n", status); pNtClose(h); pNtClose(link); pNtClose(dir); + + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + RtlInitUnicodeString(&str, L"\\BaseNamedObjects\\om.c-test"); + status = pNtCreateDirectoryObject(&dir, DIRECTORY_QUERY, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + RtlInitUnicodeString(&str, L"\\DosDevices\\test_link"); + RtlInitUnicodeString(&target, L"\\BaseNamedObjects"); + status = pNtCreateSymbolicLinkObject(&link, SYMBOLIC_LINK_QUERY, &attr, &target); + ok(status == STATUS_SUCCESS && !!link, "Got unexpected status %#lx.\n", status); + + status = NtCreateFile(&h, GENERIC_READ | SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + pNtClose(h); + + attr.Attributes = OBJ_OPENIF; + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok(status == STATUS_SUCCESS || broken( status == STATUS_OBJECT_NAME_EXISTS ), /* <= win10 1507 */ + "Got unexpected status %#lx.\n", status); + pNtClose(h); + attr.Attributes = 0; + status = pNtCreateSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr, &target ); + ok(status == STATUS_OBJECT_NAME_COLLISION, "Got unexpected status %#lx.\n", status); + pNtClose(h); + + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\om.c-test\\" ); + status = NtCreateFile(&h, GENERIC_READ | SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status); + + InitializeObjectAttributes(&attr, &str, 0, link, NULL); + RtlInitUnicodeString( &str, L"om.c-test\\test_object" ); + status = pNtCreateMutant( &h, GENERIC_ALL, &attr, FALSE ); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + RtlInitUnicodeString( &str, L"\\DosDevices\\test_link\\om.c-test\\test_object" ); + status = pNtCreateMutant( &h, GENERIC_ALL, &attr, FALSE ); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtOpenMutant( &h2, GENERIC_ALL, &attr ); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + pNtClose(h2); + RtlInitUnicodeString( &str, L"\\BaseNamedObjects\\om.c-test\\test_object" ); + status = pNtCreateMutant( &h2, GENERIC_ALL, &attr, FALSE ); + ok(status == STATUS_OBJECT_NAME_COLLISION, "Got unexpected status %#lx.\n", status); + + InitializeObjectAttributes(&attr, &str, 0, link, NULL); + RtlInitUnicodeString( &str, L"om.c-test\\test_object" ); + status = pNtOpenMutant( &h2, GENERIC_ALL, &attr ); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + pNtClose(h); + + status = pNtOpenMutant( &h, GENERIC_ALL, &attr ); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + InitializeObjectAttributes(&attr, &str, 0, dir, NULL); + RtlInitUnicodeString( &str, L"test_object" ); + status = pNtCreateMutant( &h, GENERIC_ALL, &attr, FALSE ); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtOpenMutant( &h2, GENERIC_ALL, &attr ); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + pNtClose(h); + pNtClose(h2); + + pNtClose(link); + pNtClose(dir); +} + +#define test_file_info(a) _test_file_info(__LINE__,a) +static void _test_file_info(unsigned line, HANDLE handle) +{ + IO_STATUS_BLOCK io; + char buf[256]; + NTSTATUS status; + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), 0xdeadbeef); + ok_(__FILE__,line)(status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED /* Vista+ */, + "expected STATUS_NOT_IMPLEMENTED, got %lx\n", status); + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), FileAccessInformation); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "FileAccessInformation returned %lx\n", status); + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), + FileIoCompletionNotificationInformation); + ok_(__FILE__,line)(status == STATUS_SUCCESS, + "FileIoCompletionNotificationInformation returned %lx\n", status); +} + +#define test_no_file_info(a) _test_no_file_info(__LINE__,a) +static void _test_no_file_info(unsigned line, HANDLE handle) +{ + IO_STATUS_BLOCK io; + char buf[256]; + NTSTATUS status; + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), 0xdeadbeef); + ok_(__FILE__,line)(status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED /* Vista+ */, + "expected STATUS_NOT_IMPLEMENTED, got %lx\n", status); + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), FileAccessInformation); + ok_(__FILE__,line)(status == STATUS_OBJECT_TYPE_MISMATCH, + "FileAccessInformation returned %lx\n", status); + + status = pNtQueryInformationFile(handle, &io, buf, sizeof(buf), + FileIoCompletionNotificationInformation); + ok_(__FILE__,line)(status == STATUS_OBJECT_TYPE_MISMATCH, + "FileIoCompletionNotificationInformation returned %lx\n", status); +} + +static OBJECT_TYPE_INFORMATION all_types[256]; + +static void add_object_type( OBJECT_TYPE_INFORMATION *info ) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(all_types); i++) + { + if (!all_types[i].TypeName.Buffer) break; + if (!RtlCompareUnicodeString( &all_types[i].TypeName, &info->TypeName, FALSE )) break; + } + ok( i < ARRAY_SIZE(all_types), "too many types\n" ); + + if (all_types[i].TypeName.Buffer) /* existing type */ + { + ok( !memcmp( &all_types[i].GenericMapping, &info->GenericMapping, sizeof(GENERIC_MAPPING) ), + "%u: mismatched mappings %08lx,%08lx,%08lx,%08lx / %08lx,%08lx,%08lx,%08lx\n", i, + all_types[i].GenericMapping.GenericRead, all_types[i].GenericMapping.GenericWrite, + all_types[i].GenericMapping.GenericExecute, all_types[i].GenericMapping.GenericAll, + info->GenericMapping.GenericRead, info->GenericMapping.GenericWrite, + info->GenericMapping.GenericExecute, info->GenericMapping.GenericAll ); + ok( all_types[i].ValidAccessMask == info->ValidAccessMask, + "%u: mismatched access mask %08lx / %08lx\n", i, + all_types[i].ValidAccessMask, info->ValidAccessMask ); + } + else /* add it */ + { + all_types[i] = *info; + RtlDuplicateUnicodeString( 1, &info->TypeName, &all_types[i].TypeName ); + } + ok( info->TotalNumberOfObjects <= info->HighWaterNumberOfObjects, "%s: wrong object counts %lu/%lu\n", + debugstr_w( all_types[i].TypeName.Buffer ), + info->TotalNumberOfObjects, info->HighWaterNumberOfObjects ); + ok( info->TotalNumberOfHandles <= info->HighWaterNumberOfHandles, "%s: wrong handle counts %lu/%lu\n", + debugstr_w( all_types[i].TypeName.Buffer ), + info->TotalNumberOfHandles, info->HighWaterNumberOfHandles ); +} + +static BOOL compare_unicode_string( const UNICODE_STRING *string, const WCHAR *expect ) +{ + return string->Length == wcslen( expect ) * sizeof(WCHAR) + && !wcsnicmp( string->Buffer, expect, string->Length / sizeof(WCHAR) ); +} + +#define test_object_type(a,b) _test_object_type(__LINE__,a,b) +static void _test_object_type( unsigned line, HANDLE handle, const WCHAR *expected_name ) +{ + char buffer[1024]; + OBJECT_TYPE_INFORMATION *type = (OBJECT_TYPE_INFORMATION *)buffer; + UNICODE_STRING expect; + ULONG len = 0; + NTSTATUS status; + + RtlInitUnicodeString( &expect, expected_name ); + + memset( buffer, 0, sizeof(buffer) ); + status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); + ok_(__FILE__,line)( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok_(__FILE__,line)( len > sizeof(UNICODE_STRING), "unexpected len %lu\n", len ); + ok_(__FILE__,line)( len >= sizeof(*type) + type->TypeName.Length, "unexpected len %lu\n", len ); + ok_(__FILE__,line)(compare_unicode_string( &type->TypeName, expected_name ), "wrong name %s\n", + debugstr_w( type->TypeName.Buffer )); + add_object_type( type ); +} + +#define test_object_name(a,b) _test_object_name(__LINE__,a,b) +static void _test_object_name( unsigned line, HANDLE handle, const WCHAR *expected_name ) +{ + char buffer[1024]; + UNICODE_STRING *str = (UNICODE_STRING *)buffer, expect; + ULONG len = 0; + NTSTATUS status; + + RtlInitUnicodeString( &expect, expected_name ); + + memset( buffer, 0, sizeof(buffer) ); + status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok_(__FILE__,line)( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok_(__FILE__,line)( len >= sizeof(OBJECT_NAME_INFORMATION) + str->Length, "unexpected len %lu\n", len ); + ok_(__FILE__,line)( compare_unicode_string( str, expected_name ), "got %s, expected %s\n", + debugstr_w(str->Buffer), debugstr_w(expected_name) ); } static void test_query_object(void) { - static const WCHAR name[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s', - '\\','t','e','s','t','_','e','v','e','n','t'}; - static const WCHAR type_event[] = {'E','v','e','n','t'}; - static const WCHAR type_file[] = {'F','i','l','e'}; - static const WCHAR type_iocompletion[] = {'I','o','C','o','m','p','l','e','t','i','o','n'}; - static const WCHAR type_directory[] = {'D','i','r','e','c','t','o','r','y'}; - static const WCHAR type_section[] = {'S','e','c','t','i','o','n'}; - HANDLE handle; + static const WCHAR name[] = L"\\BaseNamedObjects\\test_event"; + HANDLE handle, client; char buffer[1024]; NTSTATUS status; ULONG len, expected_len; + OBJECT_BASIC_INFORMATION info; OBJECT_ATTRIBUTES attr; - UNICODE_STRING path, session, *str; + UNICODE_STRING path, target, *str; char dir[MAX_PATH], tmp_path[MAX_PATH], file1[MAX_PATH + 16]; + WCHAR expect[100]; LARGE_INTEGER size; - sprintf( tmp_path, "\\Sessions\\%u", NtCurrentTeb()->Peb->SessionId ); - pRtlCreateUnicodeStringFromAsciiz( &session, tmp_path ); InitializeObjectAttributes( &attr, &path, 0, 0, 0 ); handle = CreateEventA( NULL, FALSE, FALSE, "test_event" ); + status = pNtQueryObject( handle, ObjectBasicInformation, NULL, 0, NULL ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + + status = pNtQueryObject( handle, ObjectBasicInformation, &info, 0, NULL ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + + status = pNtQueryObject( handle, ObjectBasicInformation, NULL, 0, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + + len = 0; + status = pNtQueryObject( handle, ObjectBasicInformation, &info, sizeof(OBJECT_BASIC_INFORMATION), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(OBJECT_BASIC_INFORMATION), "unexpected len %lu\n", len ); + len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, 0, &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(UNICODE_STRING) + sizeof(name), "unexpected len %lu\n", len ); len = 0; status = pNtQueryObject( handle, ObjectTypeInformation, buffer, 0, &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(type_event) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(L"Event"), "unexpected len %lu\n", len ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(UNICODE_STRING), &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(UNICODE_STRING) + sizeof(name), "unexpected len %lu\n", len ); len = 0; status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(OBJECT_TYPE_INFORMATION), &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(type_event) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(L"Event"), "unexpected len %lu\n", len ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok( len > sizeof(UNICODE_STRING), "unexpected len %lu\n", len ); str = (UNICODE_STRING *)buffer; - ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len, "unexpected len %u\n", len ); - ok( str->Length >= sizeof(name), "unexpected len %u\n", str->Length ); + ok( sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR) == len, "unexpected len %lu\n", len ); + ok( str->Length >= sizeof(name) - sizeof(WCHAR), "unexpected len %u\n", str->Length ); ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_event") * sizeof(WCHAR), "name too short %s\n", wine_dbgstr_w(str->Buffer) ); /* check for \\Sessions prefix in the name */ - ok( (str->Length > session.Length && - !memcmp( str->Buffer, session.Buffer, session.Length ) && - !memcmp( str->Buffer + session.Length / sizeof(WCHAR), name, sizeof(name) )) || - broken( !memcmp( str->Buffer, name, sizeof(name) )), /* winxp */ + swprintf( expect, ARRAY_SIZE(expect), L"\\Sessions\\%u%s", NtCurrentTeb()->Peb->SessionId, name ); + ok( (str->Length == wcslen( expect ) * sizeof(WCHAR) && !wcscmp( str->Buffer, expect )), "wrong name %s\n", wine_dbgstr_w(str->Buffer) ); - trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + trace( "got %s len %lu\n", wine_dbgstr_w(str->Buffer), len ); len -= sizeof(WCHAR); status = pNtQueryObject( handle, ObjectNameInformation, buffer, len, &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(UNICODE_STRING) + sizeof(name) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(UNICODE_STRING) + sizeof(name), "unexpected len %lu\n", len ); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR), "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_event, sizeof(type_event) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); + test_object_type( handle, L"Event" ); len -= sizeof(WCHAR); status = pNtQueryObject( handle, ObjectTypeInformation, buffer, len, &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(type_event) + sizeof(WCHAR), "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( len >= sizeof(OBJECT_TYPE_INFORMATION) + sizeof(L"Event"), "unexpected len %lu\n", len ); + test_no_file_info( handle ); pNtClose( handle ); handle = CreateEventA( NULL, FALSE, FALSE, NULL ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len == sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok( len == sizeof(UNICODE_STRING), "unexpected len %lu\n", len ); str = (UNICODE_STRING *)buffer; - ok( str->Length == 0, "unexpected len %u\n", len ); + ok( str->Length == 0, "unexpected len %lu\n", len ); ok( str->Buffer == NULL, "unexpected ptr %p\n", str->Buffer ); + test_no_file_info( handle ); pNtClose( handle ); GetWindowsDirectoryA( dir, MAX_PATH ); @@ -1384,262 +1778,238 @@ static void test_query_object(void) FILE_FLAG_BACKUP_SEMANTICS, 0 ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); + ok( len > sizeof(UNICODE_STRING), "unexpected len %lu\n", len ); str = (UNICODE_STRING *)buffer; expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); - ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ - "unexpected len %u\n", len ); - trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + ok( len == expected_len, "unexpected len %lu\n", len ); + trace( "got %s len %lu\n", wine_dbgstr_w(str->Buffer), len ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, 0, &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH || broken(status == STATUS_INSUFFICIENT_RESOURCES), - "NtQueryObject failed %x\n", status ); - ok( len == expected_len || broken(!len || len == sizeof(UNICODE_STRING)), - "unexpected len %u\n", len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx\n", status ); + ok( len == expected_len, "unexpected len %lu\n", len ); len = 0; status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(UNICODE_STRING), &len ); - ok( status == STATUS_BUFFER_OVERFLOW || broken(status == STATUS_INSUFFICIENT_RESOURCES - || status == STATUS_INFO_LENGTH_MISMATCH), - "NtQueryObject failed %x\n", status ); - ok( len == expected_len || broken(!len), - "unexpected len %u\n", len ); + ok( status == STATUS_BUFFER_OVERFLOW, "got %#lx\n", status); + ok( len == expected_len, "unexpected len %lu\n", len ); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR); - ok( len >= expected_len, "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_file, sizeof(type_file) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); + test_object_type( handle, L"File" ); pNtClose( handle ); GetTempPathA(MAX_PATH, tmp_path); GetTempFileNameA(tmp_path, "foo", 0, file1); handle = CreateFileA(file1, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR); - ok( len >= expected_len, "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_file, sizeof(type_file) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); - DeleteFileA( file1 ); + test_object_type(handle, L"File"); + test_file_info( handle ); pNtClose( handle ); + DeleteFileA( file1 ); status = pNtCreateIoCompletion( &handle, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateIoCompletion failed %x\n", status); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR); - ok( len >= expected_len, "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_iocompletion, sizeof(type_iocompletion) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); + ok( status == STATUS_SUCCESS, "NtCreateIoCompletion failed %lx\n", status); + + test_object_type( handle, L"IoCompletion" ); + test_no_file_info( handle ); + pNtClose( handle ); + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_debug" ); + status = pNtCreateDebugObject( &handle, DEBUG_ALL_ACCESS, &attr, 0 ); + ok(!status, "NtCreateDebugObject failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_debug" ); + test_object_type( handle, L"DebugObject" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_mutant" ); + status = pNtCreateMutant( &handle, MUTANT_ALL_ACCESS, &attr, 0 ); + ok(!status, "NtCreateMutant failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_mutant" ); + test_object_type( handle, L"Mutant" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_sem" ); + status = pNtCreateSemaphore( &handle, SEMAPHORE_ALL_ACCESS, &attr, 1, 2 ); + ok(!status, "NtCreateSemaphore failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_sem" ); + test_object_type( handle, L"Semaphore" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_keyed" ); + status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); + ok(!status, "NtCreateKeyedEvent failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_keyed" ); + test_object_type( handle, L"KeyedEvent" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_compl" ); + status = pNtCreateIoCompletion( &handle, IO_COMPLETION_ALL_ACCESS, &attr, 0 ); + ok(!status, "NtCreateIoCompletion failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_compl" ); + test_object_type( handle, L"IoCompletion" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_job" ); + status = pNtCreateJobObject( &handle, JOB_OBJECT_ALL_ACCESS, &attr ); + ok(!status, "NtCreateJobObject failed: %lx\n", status); + test_object_name( handle, L"\\BaseNamedObjects\\test_job" ); + test_object_type( handle, L"Job" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\BaseNamedObjects\\test_timer" ); + status = pNtCreateTimer( &handle, TIMER_ALL_ACCESS, &attr, NotificationTimer ); + ok(!status, "NtCreateTimer failed: %lx\n", status); + test_object_type( handle, L"Timer" ); + test_no_file_info( handle ); + pNtClose(handle); + + RtlInitUnicodeString( &path, L"\\DosDevices\\test_link" ); + RtlInitUnicodeString( &target, L"\\DosDevices" ); + status = pNtCreateSymbolicLinkObject( &handle, SYMBOLIC_LINK_ALL_ACCESS, &attr, &target ); + ok(!status, "NtCreateSymbolicLinkObject failed: %lx\n", status); + test_object_type( handle, L"SymbolicLink" ); + test_no_file_info( handle ); + pNtClose(handle); + + handle = GetProcessWindowStation(); + swprintf( expect, ARRAY_SIZE(expect), L"\\Sessions\\%u\\Windows\\WindowStations\\WinSta0", NtCurrentTeb()->Peb->SessionId ); + test_object_name( handle, expect ); + test_object_type( handle, L"WindowStation" ); + test_no_file_info( handle ); + + handle = GetThreadDesktop( GetCurrentThreadId() ); + test_object_name( handle, L"\\Default" ); + test_object_type( handle, L"Desktop" ); + test_no_file_info( handle ); + status = pNtCreateDirectoryObject( &handle, DIRECTORY_QUERY, NULL ); - ok(status == STATUS_SUCCESS, "Failed to create Directory %08x\n", status); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR); - ok( len >= expected_len, "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_directory, sizeof(type_directory) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); + ok(status == STATUS_SUCCESS, "Failed to create Directory %08lx\n", status); + + test_object_type( handle, L"Directory" ); + test_no_file_info( handle ); + pNtClose( handle ); size.u.LowPart = 256; size.u.HighPart = 0; status = pNtCreateSection( &handle, SECTION_MAP_WRITE, NULL, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); - ok( status == STATUS_SUCCESS , "NtCreateSection returned %x\n", status ); - len = 0; - memset( buffer, 0, sizeof(buffer) ); - status = pNtQueryObject( handle, ObjectTypeInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( len > sizeof(OBJECT_TYPE_INFORMATION), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(OBJECT_TYPE_INFORMATION) + str->Length + sizeof(WCHAR); - ok( len >= expected_len, "unexpected len %u\n", len ); - ok( str->Buffer && !memcmp( str->Buffer, type_section, sizeof(type_section) ), - "wrong/bad type name %s (%p)\n", wine_dbgstr_w(str->Buffer), str->Buffer ); + ok( status == STATUS_SUCCESS , "NtCreateSection returned %lx\n", status ); + + test_object_type( handle, L"Section" ); + test_no_file_info( handle ); + pNtClose( handle ); handle = CreateMailslotA( "\\\\.\\mailslot\\test_mailslot", 100, 1000, NULL ); - ok( handle != INVALID_HANDLE_VALUE, "CreateMailslot failed err %u\n", GetLastError() ); + ok( handle != INVALID_HANDLE_VALUE, "CreateMailslot failed err %lu\n", GetLastError() ); + + test_object_name( handle, L"\\Device\\Mailslot\\test_mailslot" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); + + client = CreateFileA( "\\\\.\\mailslot\\test_mailslot", 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + len = 0; - status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); + status = pNtQueryObject( client, ObjectNameInformation, buffer, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject failed %lx\n", status ); str = (UNICODE_STRING *)buffer; - ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); - ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ - "unexpected len %u\n", len ); - ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_mailslot") * sizeof(WCHAR), - "name too short %s\n", wine_dbgstr_w(str->Buffer) ); - trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + ok( len == sizeof(UNICODE_STRING) + str->MaximumLength, "unexpected len %lu\n", len ); + todo_wine + ok( compare_unicode_string( str, L"\\Device\\Mailslot" ) || + compare_unicode_string( str, L"\\Device\\Mailslot\\test_mailslot" ) /* win8+ */, + "wrong name %s\n", debugstr_w( str->Buffer )); + + test_object_type( client, L"File" ); + test_file_info( client ); + + pNtClose( client ); + pNtClose( handle ); + + handle = CreateFileA( "\\\\.\\mailslot", 0, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + + test_object_name( handle, L"\\Device\\Mailslot" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); + pNtClose( handle ); handle = CreateNamedPipeA( "\\\\.\\pipe\\test_pipe", PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, 1, 1000, 1000, 1000, NULL ); - ok( handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed err %u\n", GetLastError() ); - len = 0; - status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); - str = (UNICODE_STRING *)buffer; - ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); - ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ - "unexpected len %u\n", len ); - ok( len > sizeof(UNICODE_STRING) + sizeof("\\test_pipe") * sizeof(WCHAR), - "name too short %s\n", wine_dbgstr_w(str->Buffer) ); - trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); + ok( handle != INVALID_HANDLE_VALUE, "CreateNamedPipe failed err %lu\n", GetLastError() ); + + test_object_name( handle, L"\\Device\\NamedPipe\\test_pipe" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); + + client = CreateFileA( "\\\\.\\pipe\\test_pipe", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + + test_object_type( client, L"File" ); + test_file_info( client ); + + pNtClose( client ); pNtClose( handle ); - pRtlCreateUnicodeStringFromAsciiz( &path, "\\REGISTRY\\Machine\\Software\\Classes" ); - status = pNtCreateKey( &handle, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed status %x\n", status ); - pRtlFreeUnicodeString( &path ); - if (status == STATUS_SUCCESS) - { - len = 0; - status = pNtQueryObject( handle, ObjectNameInformation, buffer, sizeof(buffer), &len ); - ok( status == STATUS_SUCCESS , "NtQueryObject returned %x\n", status ); - str = (UNICODE_STRING *)buffer; - todo_wine - ok( len > sizeof(UNICODE_STRING), "unexpected len %u\n", len ); - str = (UNICODE_STRING *)buffer; - expected_len = sizeof(UNICODE_STRING) + str->Length + sizeof(WCHAR); - todo_wine - ok( len == expected_len || broken(len == expected_len - sizeof(WCHAR)), /* NT4 */ - "unexpected len %u\n", len ); - todo_wine - ok( len > sizeof(UNICODE_STRING) + sizeof("\\Classes") * sizeof(WCHAR), - "name too short %s\n", wine_dbgstr_w(str->Buffer) ); - trace( "got %s len %u\n", wine_dbgstr_w(str->Buffer), len ); - pNtClose( handle ); - } - pRtlFreeUnicodeString( &session ); -} + handle = CreateFileA( "\\\\.\\pipe", 0, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); -static BOOL winver_equal_or_newer(WORD major, WORD minor) -{ - OSVERSIONINFOEXW info = {sizeof(info)}; - ULONGLONG mask = 0; + test_object_name( handle, L"\\Device\\NamedPipe" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); - info.dwMajorVersion = major; - info.dwMinorVersion = minor; + pNtClose( handle ); - VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(mask, VER_MINORVERSION, VER_GREATER_EQUAL); + handle = CreateFileA( "\\\\.\\pipe\\", 0, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%lu)\n", GetLastError() ); - return VerifyVersionInfoW(&info, VER_MAJORVERSION | VER_MINORVERSION, mask); -} + test_object_name( handle, L"\\Device\\NamedPipe\\" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); -static void test_query_object_types(void) -{ - static const WCHAR typeW[] = {'T','y','p','e'}; - static const WCHAR eventW[] = {'E','v','e','n','t'}; - SYSTEM_HANDLE_INFORMATION_EX *shi; - OBJECT_TYPES_INFORMATION *buffer; - OBJECT_TYPE_INFORMATION *type; - NTSTATUS status; - HANDLE handle; - BOOL found; - ULONG len, i, event_type_index = 0; + pNtClose( handle ); - buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(OBJECT_TYPES_INFORMATION) ); - ok( buffer != NULL, "Failed to allocate memory\n" ); + RtlInitUnicodeString( &path, L"\\REGISTRY\\Machine" ); + status = pNtCreateKey( &handle, KEY_READ, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed status %lx\n", status ); - status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, sizeof(OBJECT_TYPES_INFORMATION), &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %x\n", status ); - ok( len, "len is zero\n"); + test_object_name( handle, L"\\REGISTRY\\MACHINE" ); + test_object_type( handle, L"Key" ); - buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, len ); - ok( buffer != NULL, "Failed to allocate memory\n" ); + pNtClose( handle ); - memset( buffer, 0, len ); - status = pNtQueryObject( NULL, ObjectTypesInformation, buffer, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryObject failed %x\n", status ); - ok( buffer->NumberOfTypes, "NumberOfTypes is zero\n" ); + test_object_name( GetCurrentProcess(), L"" ); + test_object_type( GetCurrentProcess(), L"Process" ); + test_no_file_info( GetCurrentProcess() ); - type = (OBJECT_TYPE_INFORMATION *)(buffer + 1); - for (i = 0; i < buffer->NumberOfTypes; i++) - { - USHORT length = type->TypeName.MaximumLength; - trace( "Type %u: %s\n", i, wine_dbgstr_us(&type->TypeName) ); + test_object_name( GetCurrentThread(), L"" ); + test_object_type( GetCurrentThread(), L"Thread" ); + test_no_file_info( GetCurrentThread() ); - if (i == 0) - { - ok( type->TypeName.Length == sizeof(typeW) && !strncmpW(typeW, type->TypeName.Buffer, 4), - "Expected 'Type' as first type, got %s\n", wine_dbgstr_us(&type->TypeName) ); - } - if (type->TypeName.Length == sizeof(eventW) && !strncmpW(eventW, type->TypeName.Buffer, 5)) - { - if (winver_equal_or_newer( 6, 2 )) - event_type_index = type->TypeIndex; - else - event_type_index = winver_equal_or_newer( 6, 1 ) ? i + 2 : i + 1; - } + status = pNtOpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &handle); + ok(!status, "OpenProcessToken failed: %lx\n", status); - type = (OBJECT_TYPE_INFORMATION *)ROUND_UP( (DWORD_PTR)(type + 1) + length, sizeof(DWORD_PTR) ); - } + test_object_name( handle, L"" ); + test_object_type( handle, L"Token" ); + test_no_file_info( handle ); - HeapFree( GetProcessHeap(), 0, buffer ); + pNtClose(handle); - ok( event_type_index, "Could not find object type for events\n" ); - - handle = CreateEventA( NULL, FALSE, FALSE, NULL ); - ok( handle != NULL, "Failed to create event\n" ); - - shi = HeapAlloc( GetProcessHeap(), 0, sizeof(*shi) ); - ok( shi != NULL, "Failed to allocate memory\n" ); - - status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, sizeof(*shi), &len ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); - - shi = HeapReAlloc( GetProcessHeap(), 0, shi, len ); - ok( shi != NULL, "Failed to allocate memory\n" ); - - status = pNtQuerySystemInformation( SystemExtendedHandleInformation, shi, len, &len ); - ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status ); - - found = FALSE; - for (i = 0; i < shi->NumberOfHandles; i++) - { - if (shi->Handles[i].UniqueProcessId != GetCurrentProcessId()) - continue; - if ((HANDLE)(ULONG_PTR)shi->Handles[i].HandleValue != handle) - continue; - - ok( shi->Handles[i].ObjectTypeIndex == event_type_index, "Event type does not match: %u vs %u\n", - shi->Handles[i].ObjectTypeIndex, event_type_index ); - - found = TRUE; - break; - } - ok( found, "Expected to find event handle %p (pid %x) in handle list\n", handle, GetCurrentProcessId() ); - - HeapFree( GetProcessHeap(), 0, shi ); - CloseHandle( handle ); + handle = CreateFileA( "nul", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + test_object_name( handle, L"\\Device\\Null" ); + test_object_type( handle, L"File" ); + test_file_info( handle ); + pNtClose( handle ); } static void test_type_mismatch(void) @@ -1655,239 +2025,15 @@ static void test_type_mismatch(void) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; - res = pNtCreateEvent( &h, 0, &attr, 0, 0 ); - ok(!res, "can't create event: %x\n", res); + res = pNtCreateEvent( &h, 0, &attr, NotificationEvent, 0 ); + ok(!res, "can't create event: %lx\n", res); res = pNtReleaseSemaphore( h, 30, NULL ); - ok(res == STATUS_OBJECT_TYPE_MISMATCH, "expected 0xc0000024, got %x\n", res); + ok(res == STATUS_OBJECT_TYPE_MISMATCH, "expected 0xc0000024, got %lx\n", res); pNtClose( h ); } -static void test_event(void) -{ - HANDLE Event; - HANDLE Event2; - NTSTATUS status; - UNICODE_STRING str; - OBJECT_ATTRIBUTES attr; - EVENT_BASIC_INFORMATION info; - static const WCHAR eventName[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t','E','v','e','n','t',0}; - - pRtlInitUnicodeString(&str, eventName); - InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - - status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, 1, 0); - ok( status == STATUS_SUCCESS, "NtCreateEvent failed %08x\n", status ); - - status = pNtPulseEvent(Event, NULL); - ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); - - status = pNtQueryEvent(Event, EventBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); - ok( info.EventType == 1 && info.EventState == 0, - "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); - - status = pNtOpenEvent(&Event2, GENERIC_ALL, &attr); - ok( status == STATUS_SUCCESS, "NtOpenEvent failed %08x\n", status ); - - pNtClose(Event); - - status = pNtQueryEvent(Event2, EventBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); - ok( info.EventType == 1 && info.EventState == 0, - "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); - - pNtClose(Event2); -} - -static const WCHAR keyed_nameW[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s', - '\\','W','i','n','e','T','e','s','t','E','v','e','n','t',0}; - -static DWORD WINAPI keyed_event_thread( void *arg ) -{ - HANDLE handle; - NTSTATUS status; - LARGE_INTEGER timeout; - OBJECT_ATTRIBUTES attr; - UNICODE_STRING str; - ULONG_PTR i; - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.ObjectName = &str; - attr.Attributes = 0; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - RtlInitUnicodeString( &str, keyed_nameW ); - - status = pNtOpenKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr ); - ok( !status, "NtOpenKeyedEvent failed %x\n", status ); - - for (i = 0; i < 20; i++) - { - if (i & 1) - status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); - else - status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); - ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); - Sleep( 20 - i ); - } - - status = pNtReleaseKeyedEvent( handle, (void *)0x1234, 0, NULL ); - ok( status == STATUS_SUCCESS, "NtReleaseKeyedEvent %x\n", status ); - - timeout.QuadPart = -10000; - status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - - NtClose( handle ); - return 0; -} - -static void test_keyed_events(void) -{ - OBJECT_ATTRIBUTES attr; - UNICODE_STRING str; - HANDLE handle, event, thread; - NTSTATUS status; - LARGE_INTEGER timeout; - ULONG_PTR i; - - if (!pNtCreateKeyedEvent) - { - win_skip( "Keyed events not supported\n" ); - return; - } - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.ObjectName = &str; - attr.Attributes = 0; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - RtlInitUnicodeString( &str, keyed_nameW ); - - status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS | SYNCHRONIZE, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - - status = WaitForSingleObject( handle, 1000 ); - ok( status == 0, "WaitForSingleObject %x\n", status ); - - timeout.QuadPart = -100000; - status = pNtWaitForKeyedEvent( handle, (void *)255, 0, &timeout ); - ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)255, 0, &timeout ); - ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); - - status = pNtWaitForKeyedEvent( handle, (void *)254, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)254, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - - status = pNtWaitForKeyedEvent( handle, NULL, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, NULL, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - - status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); - ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); - ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); - - status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); - ok( status == STATUS_INVALID_HANDLE, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); - ok( status == STATUS_INVALID_HANDLE, "NtReleaseKeyedEvent %x\n", status ); - - thread = CreateThread( NULL, 0, keyed_event_thread, 0, 0, NULL ); - for (i = 0; i < 20; i++) - { - if (i & 1) - status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); - else - status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); - ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); - Sleep( i ); - } - status = pNtWaitForKeyedEvent( handle, (void *)0x1234, 0, &timeout ); - ok( status == STATUS_SUCCESS, "NtWaitForKeyedEvent %x\n", status ); - status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - - ok( WaitForSingleObject( thread, 30000 ) == 0, "wait failed\n" ); - - NtClose( handle ); - - /* test access rights */ - - status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAIT, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); - NtClose( handle ); - - status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAKE, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - NtClose( handle ); - - status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - status = WaitForSingleObject( handle, 1000 ); - ok( status == WAIT_FAILED && GetLastError() == ERROR_ACCESS_DENIED, - "WaitForSingleObject %x err %u\n", status, GetLastError() ); - status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - NtClose( handle ); - - /* GENERIC_READ gives wait access */ - status = pNtCreateKeyedEvent( &handle, GENERIC_READ, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); - NtClose( handle ); - - /* GENERIC_WRITE gives wake access */ - status = pNtCreateKeyedEvent( &handle, GENERIC_WRITE, &attr, 0 ); - ok( !status, "NtCreateKeyedEvent failed %x\n", status ); - status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); - ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); - - /* it's not an event */ - status = pNtPulseEvent( handle, NULL ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtPulseEvent %x\n", status ); - - status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); - ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH, - "CreateEvent %x\n", status ); - - NtClose( handle ); - - status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); - ok( status == 0, "CreateEvent %x\n", status ); - status = pNtWaitForKeyedEvent( event, (void *)8, 0, &timeout ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtWaitForKeyedEvent %x\n", status ); - status = pNtReleaseKeyedEvent( event, (void *)8, 0, &timeout ); - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtReleaseKeyedEvent %x\n", status ); - NtClose( event ); -} - static void test_null_device(void) { OBJECT_ATTRIBUTES attr; @@ -1904,60 +2050,43 @@ static void test_null_device(void) memset(&ov, 0, sizeof(ov)); ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); - pRtlCreateUnicodeStringFromAsciiz(&str, "\\Device\\Null"); + RtlInitUnicodeString(&str, L"\\Device\\Null"); InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL); status = pNtOpenSymbolicLinkObject(&null, SYMBOLIC_LINK_QUERY, &attr); ok(status == STATUS_OBJECT_TYPE_MISMATCH, - "expected STATUS_OBJECT_TYPE_MISMATCH, got %08x\n", status); + "expected STATUS_OBJECT_TYPE_MISMATCH, got %08lx\n", status); status = pNtOpenFile(&null, GENERIC_READ | GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 0); ok(status == STATUS_SUCCESS, - "expected STATUS_SUCCESS, got %08x\n", status); + "expected STATUS_SUCCESS, got %08lx\n", status); + + test_object_type(null, L"File"); SetLastError(0xdeadbeef); ret = WriteFile(null, buf, sizeof(buf), &num_bytes, NULL); ok(!ret, "WriteFile unexpectedly succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, - "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + "expected ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); SetLastError(0xdeadbeef); ret = ReadFile(null, buf, sizeof(buf), &num_bytes, NULL); ok(!ret, "ReadFile unexpectedly succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, - "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + "expected ERROR_INVALID_PARAMETER, got %lu\n", GetLastError()); num_bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = WriteFile(null, buf, sizeof(buf), &num_bytes, &ov); - if (ret || GetLastError() != ERROR_IO_PENDING) - { - ok(ret, "WriteFile failed with error %u\n", GetLastError()); - } - else - { - num_bytes = 0xdeadbeef; - ret = GetOverlappedResult(null, &ov, &num_bytes, TRUE); - ok(ret, "GetOverlappedResult failed with error %u\n", GetLastError()); - } - ok(num_bytes == sizeof(buf), "expected num_bytes = %u, got %u\n", + ok(ret, "got error %lu\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected num_bytes = %lu, got %lu\n", (DWORD)sizeof(buf), num_bytes); num_bytes = 0xdeadbeef; SetLastError(0xdeadbeef); ret = ReadFile(null, buf, sizeof(buf), &num_bytes, &ov); - if (ret || GetLastError() != ERROR_IO_PENDING) - { - ok(!ret, "ReadFile unexpectedly succeeded\n"); - } - else - { - num_bytes = 0xdeadbeef; - ret = GetOverlappedResult(null, &ov, &num_bytes, TRUE); - ok(!ret, "GetOverlappedResult unexpectedly succeeded\n"); - } - ok(GetLastError() == ERROR_HANDLE_EOF, - "expected ERROR_HANDLE_EOF, got %u\n", GetLastError()); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_HANDLE_EOF, "got error %lu\n", GetLastError()); pNtClose(null); @@ -1966,149 +2095,1787 @@ static void test_null_device(void) OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ok(null == INVALID_HANDLE_VALUE, "CreateFileA unexpectedly succeeded\n"); ok(GetLastError() == ERROR_FILE_NOT_FOUND, - "expected ERROR_FILE_NOT_FOUND, got %u\n", GetLastError()); + "expected ERROR_FILE_NOT_FOUND, got %lu\n", GetLastError()); null = CreateFileA("\\\\.\\Device\\Null", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ok(null == INVALID_HANDLE_VALUE, "CreateFileA unexpectedly succeeded\n"); ok(GetLastError() == ERROR_PATH_NOT_FOUND, - "expected ERROR_PATH_NOT_FOUND, got %u\n", GetLastError()); + "expected ERROR_PATH_NOT_FOUND, got %lu\n", GetLastError()); - pRtlFreeUnicodeString(&str); CloseHandle(ov.hEvent); } -static DWORD WINAPI mutant_thread( void *arg ) +static void test_process(void) { - MUTANT_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + CLIENT_ID cid; NTSTATUS status; - HANDLE mutant; - DWORD ret; + HANDLE process; - mutant = arg; - ret = WaitForSingleObject( mutant, 1000 ); - ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); + if (!pNtOpenProcess) + { + win_skip( "NtOpenProcess not supported, skipping test\n" ); + return; + } - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); - /* abandon mutant */ + InitializeObjectAttributes( &attr, NULL, 0, 0, NULL ); + cid.UniqueProcess = 0; + cid.UniqueThread = 0; + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, NULL, &cid ); + todo_wine ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcess returned %lx\n", status ); + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, NULL ); + todo_wine ok( status == STATUS_INVALID_PARAMETER_MIX, "NtOpenProcess returned %lx\n", status ); + + cid.UniqueProcess = 0; + cid.UniqueThread = 0; + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_INVALID_CID, "NtOpenProcess returned %lx\n", status ); + + cid.UniqueProcess = ULongToHandle( 0xdeadbeef ); + cid.UniqueThread = ULongToHandle( 0xdeadbeef ); + process = (HANDLE)0xdeadbeef; + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_INVALID_CID, "NtOpenProcess returned %lx\n", status ); + ok( !process || broken(process == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", process ); + + cid.UniqueProcess = ULongToHandle( GetCurrentThreadId() ); + cid.UniqueThread = 0; + process = (HANDLE)0xdeadbeef; + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_INVALID_CID, "NtOpenProcess returned %lx\n", status ); + ok( !process || broken(process == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", process ); + + cid.UniqueProcess = ULongToHandle( GetCurrentProcessId() ); + cid.UniqueThread = 0; + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( !status, "NtOpenProcess returned %lx\n", status ); + pNtClose( process ); + + cid.UniqueProcess = ULongToHandle( GetCurrentProcessId() ); + cid.UniqueThread = ULongToHandle( GetCurrentThreadId() ); + status = pNtOpenProcess( &process, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( !status, "NtOpenProcess returned %lx\n", status ); + pNtClose( process ); + status = pNtOpenProcess( (HANDLE *)0xdeadbee0, PROCESS_QUERY_LIMITED_INFORMATION, &attr, &cid ); + ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcess returned %lx\n", status ); +} + +static void test_token(void) +{ + NTSTATUS status; + HANDLE handle, handle2; + + status = pNtOpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, (HANDLE *)0xdeadbee0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcessToken failed: %lx\n", status); + status = pNtOpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, (HANDLE *)0xdeadbee0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcessToken failed: %lx\n", status); + handle = (HANDLE)0xdeadbeef; + status = pNtOpenProcessToken( (HANDLE)0xdead, TOKEN_ALL_ACCESS, &handle ); + ok( status == STATUS_INVALID_HANDLE, "NtOpenProcessToken failed: %lx\n", status); + ok( !handle || broken(handle == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle ); + handle = (HANDLE)0xdeadbeef; + status = pNtOpenThreadToken( (HANDLE)0xdead, TOKEN_ALL_ACCESS, TRUE, &handle ); + ok( status == STATUS_INVALID_HANDLE, "NtOpenThreadToken failed: %lx\n", status); + ok( !handle || broken(handle == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle ); + + status = pNtOpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &handle ); + ok( status == STATUS_SUCCESS, "NtOpenProcessToken failed: %lx\n", status); + status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, &handle2 ); + ok( status == STATUS_SUCCESS, "NtOpenProcessToken failed: %lx\n", status); + pNtClose( handle2 ); + status = pNtDuplicateToken( handle, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, (HANDLE *)0xdeadbee0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NtOpenProcessToken failed: %lx\n", status); + handle2 = (HANDLE)0xdeadbeef; + status = pNtDuplicateToken( (HANDLE)0xdead, TOKEN_ALL_ACCESS, NULL, FALSE, TokenPrimary, &handle2 ); + ok( status == STATUS_INVALID_HANDLE, "NtOpenProcessToken failed: %lx\n", status); + ok( !handle2 || broken(handle2 == (HANDLE)0xdeadbeef) /* vista */, "handle set %p\n", handle2 ); + pNtClose( handle ); +} + +#define DEBUG_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define DEBUG_GENERIC_READ (STANDARD_RIGHTS_READ|DEBUG_READ_EVENT) +#define DEBUG_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|DEBUG_PROCESS_ASSIGN) +#define DESKTOP_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|DESKTOP_SWITCHDESKTOP) +#define DESKTOP_GENERIC_READ (STANDARD_RIGHTS_READ|DESKTOP_ENUMERATE|DESKTOP_READOBJECTS) +#define DESKTOP_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|DESKTOP_WRITEOBJECTS|DESKTOP_JOURNALPLAYBACK|\ + DESKTOP_JOURNALRECORD|DESKTOP_HOOKCONTROL|DESKTOP_CREATEMENU| \ + DESKTOP_CREATEWINDOW) +#define DIRECTORY_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|DIRECTORY_TRAVERSE|DIRECTORY_QUERY) +#define DIRECTORY_GENERIC_READ (STANDARD_RIGHTS_READ|DIRECTORY_TRAVERSE|DIRECTORY_QUERY) +#define DIRECTORY_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|DIRECTORY_CREATE_SUBDIRECTORY|\ + DIRECTORY_CREATE_OBJECT) +#define EVENT_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define EVENT_GENERIC_READ (STANDARD_RIGHTS_READ|EVENT_QUERY_STATE) +#define EVENT_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|EVENT_MODIFY_STATE) +#define IO_COMPLETION_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define IO_COMPLETION_GENERIC_READ (STANDARD_RIGHTS_READ|IO_COMPLETION_QUERY_STATE) +#define IO_COMPLETION_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|IO_COMPLETION_MODIFY_STATE) +#define IO_COMPLETION_RESERVE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE) +#define IO_COMPLETION_RESERVE_GENERIC_READ (STANDARD_RIGHTS_READ|0x1) +#define IO_COMPLETION_RESERVE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|0x2) +#define IO_COMPLETION_RESERVE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|0x3) +#define JOB_OBJECT_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define JOB_OBJECT_GENERIC_READ (STANDARD_RIGHTS_READ|JOB_OBJECT_QUERY) +#define JOB_OBJECT_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|JOB_OBJECT_TERMINATE|\ + JOB_OBJECT_SET_ATTRIBUTES|JOB_OBJECT_ASSIGN_PROCESS) +#define KEY_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|KEY_CREATE_LINK|KEY_NOTIFY|\ + KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE) +#define KEY_GENERIC_READ (STANDARD_RIGHTS_READ|KEY_NOTIFY|KEY_ENUMERATE_SUB_KEYS|\ + KEY_QUERY_VALUE) +#define KEY_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|KEY_CREATE_SUB_KEY|KEY_SET_VALUE) +#define KEYEDEVENT_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE) +#define KEYEDEVENT_GENERIC_READ (STANDARD_RIGHTS_READ|KEYEDEVENT_WAIT) +#define KEYEDEVENT_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|KEYEDEVENT_WAKE) +#define MUTANT_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define MUTANT_GENERIC_READ (STANDARD_RIGHTS_READ|MUTANT_QUERY_STATE) +#define MUTANT_GENERIC_WRITE (STANDARD_RIGHTS_WRITE) +#define PROCESS_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE|\ + PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_TERMINATE) +#define PROCESS_GENERIC_READ (STANDARD_RIGHTS_READ|PROCESS_VM_READ|PROCESS_QUERY_INFORMATION) +#define PROCESS_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|PROCESS_SUSPEND_RESUME|\ + PROCESS_SET_INFORMATION|PROCESS_SET_QUOTA|PROCESS_CREATE_PROCESS|\ + PROCESS_DUP_HANDLE|PROCESS_VM_WRITE|PROCESS_VM_OPERATION|\ + PROCESS_CREATE_THREAD) +#define SECTION_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SECTION_MAP_EXECUTE) +#define SECTION_GENERIC_READ (STANDARD_RIGHTS_READ|SECTION_QUERY|SECTION_MAP_READ) +#define SECTION_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|SECTION_MAP_WRITE) +#define SEMAPHORE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define SEMAPHORE_GENERIC_READ (STANDARD_RIGHTS_READ|SEMAPHORE_QUERY_STATE) +#define SEMAPHORE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|SEMAPHORE_MODIFY_STATE) +#define SYMBOLIC_LINK_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYMBOLIC_LINK_QUERY) +#define SYMBOLIC_LINK_GENERIC_READ (STANDARD_RIGHTS_READ|SYMBOLIC_LINK_QUERY) +#define SYMBOLIC_LINK_GENERIC_WRITE (STANDARD_RIGHTS_WRITE) +#define THREAD_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE|THREAD_RESUME|\ + THREAD_QUERY_LIMITED_INFORMATION) +#define THREAD_GENERIC_READ (STANDARD_RIGHTS_READ|THREAD_QUERY_INFORMATION|THREAD_GET_CONTEXT) +#define THREAD_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|THREAD_SET_LIMITED_INFORMATION|\ + THREAD_SET_INFORMATION|THREAD_SET_CONTEXT|THREAD_SUSPEND_RESUME|\ + THREAD_TERMINATE|0x04) +#define TIMER_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|SYNCHRONIZE) +#define TIMER_GENERIC_READ (STANDARD_RIGHTS_READ|TIMER_QUERY_STATE) +#define TIMER_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|TIMER_MODIFY_STATE) +#define TOKEN_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|TOKEN_IMPERSONATE|TOKEN_ASSIGN_PRIMARY) +#define TOKEN_GENERIC_READ (STANDARD_RIGHTS_READ|TOKEN_QUERY_SOURCE|TOKEN_QUERY|TOKEN_DUPLICATE) +#define TOKEN_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|TOKEN_ADJUST_SESSIONID|TOKEN_ADJUST_DEFAULT|\ + TOKEN_ADJUST_GROUPS|TOKEN_ADJUST_PRIVILEGES) +#define TYPE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE) +#define TYPE_GENERIC_READ (STANDARD_RIGHTS_READ) +#define TYPE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE) +#define USER_APC_RESERVE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE) +#define USER_APC_RESERVE_GENERIC_READ (STANDARD_RIGHTS_READ|0x1) +#define USER_APC_RESERVE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|0x2) +#define USER_APC_RESERVE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|0x3) +#define WINSTA_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|WINSTA_EXITWINDOWS|WINSTA_ACCESSGLOBALATOMS) +#define WINSTA_GENERIC_READ (STANDARD_RIGHTS_READ|WINSTA_READSCREEN|WINSTA_ENUMERATE|\ + WINSTA_READATTRIBUTES|WINSTA_ENUMDESKTOPS) +#define WINSTA_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|WINSTA_WRITEATTRIBUTES|WINSTA_CREATEDESKTOP|\ + WINSTA_ACCESSCLIPBOARD) + +#undef WINSTA_ALL_ACCESS +#undef DESKTOP_ALL_ACCESS +#define WINSTA_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|0x37f) +#define DESKTOP_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|0x1ff) +#define DEVICE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1ff) +#define TYPE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|0x1) + +static void *align_ptr( void *ptr ) +{ + ULONG_PTR align = sizeof(DWORD_PTR) - 1; + return (void *)(((DWORD_PTR)ptr + align) & ~align); +} + +static void test_duplicate_object(void) +{ + NTSTATUS status; + HANDLE handle; + + status = pNtDuplicateObject( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + &handle, PROCESS_ALL_ACCESS, 0, 0 ); + ok( !status, "NtDuplicateObject failed %lx\n", status ); + pNtClose( handle ); + status = pNtDuplicateObject( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + NULL, PROCESS_ALL_ACCESS, 0, 0 ); + ok( !status, "NtDuplicateObject failed %lx\n", status ); + + status = pNtDuplicateObject( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + (HANDLE *)0xdeadbee0, PROCESS_ALL_ACCESS, 0, 0 ); + ok( status == STATUS_ACCESS_VIOLATION, "NtDuplicateObject failed %lx\n", status ); + + handle = (HANDLE)0xdeadbeef; + status = pNtDuplicateObject( GetCurrentProcess(), (HANDLE)0xdead, GetCurrentProcess(), + &handle, PROCESS_ALL_ACCESS, 0, 0 ); + ok( status == STATUS_INVALID_HANDLE, "NtDuplicateObject failed %lx\n", status ); + ok( !handle, "handle set %p\n", handle ); + + handle = (HANDLE)0xdeadbeef; + status = pNtDuplicateObject( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + &handle, ~0u, 0, 0 ); + todo_wine + ok( status == STATUS_ACCESS_DENIED, "NtDuplicateObject failed %lx\n", status ); + todo_wine + ok( !handle, "handle set %p\n", handle ); + if (!status) pNtClose( handle ); +} + +static void test_object_types(void) +{ + static const struct { const WCHAR *name; GENERIC_MAPPING mapping; ULONG mask, broken; } tests[] = + { +#define TYPE(name,gen,extra,broken) { name, { gen ## _GENERIC_READ, gen ## _GENERIC_WRITE, \ + gen ## _GENERIC_EXECUTE, gen ## _ALL_ACCESS }, gen ## _ALL_ACCESS | extra, broken } + TYPE( L"DebugObject", DEBUG, 0, 0 ), + TYPE( L"Desktop", DESKTOP, 0, 0 ), + TYPE( L"Device", FILE, 0, 0 ), + TYPE( L"Directory", DIRECTORY, 0, 0 ), + TYPE( L"Event", EVENT, 0, 0 ), + TYPE( L"File", FILE, 0, 0 ), + TYPE( L"IoCompletion", IO_COMPLETION, 0, 0 ), + TYPE( L"IoCompletionReserve", IO_COMPLETION_RESERVE, 0, 0 ), + TYPE( L"Job", JOB_OBJECT, 0, JOB_OBJECT_IMPERSONATE ), + TYPE( L"Key", KEY, SYNCHRONIZE, 0 ), + TYPE( L"KeyedEvent", KEYEDEVENT, SYNCHRONIZE, 0 ), + TYPE( L"Mutant", MUTANT, 0, 0 ), + TYPE( L"Process", PROCESS, 0, 0 ), + TYPE( L"Section", SECTION, SYNCHRONIZE, 0 ), + TYPE( L"Semaphore", SEMAPHORE, 0, 0 ), + TYPE( L"SymbolicLink", SYMBOLIC_LINK, 0, 0xfffe ), + TYPE( L"Thread", THREAD, 0, THREAD_RESUME ), + TYPE( L"Timer", TIMER, 0, 0 ), + TYPE( L"Token", TOKEN, SYNCHRONIZE, 0 ), + TYPE( L"Type", TYPE, SYNCHRONIZE, 0 ), + TYPE( L"UserApcReserve", USER_APC_RESERVE, 0, 0 ), + TYPE( L"WindowStation", WINSTA, 0, 0 ), +#undef TYPE + }; + unsigned int i, j; + BOOLEAN tested[ARRAY_SIZE(all_types)] = { 0 }; + char buffer[256]; + OBJECT_TYPES_INFORMATION *info = (OBJECT_TYPES_INFORMATION *)buffer; + GENERIC_MAPPING map; + NTSTATUS status; + ULONG len, retlen; + + memset( buffer, 0xcc, sizeof(buffer) ); + status = pNtQueryObject( NULL, ObjectTypesInformation, info, sizeof(buffer), &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryObject failed %lx\n", status ); + ok( info->NumberOfTypes < 100 || info->NumberOfTypes == 0xcccccccc, /* wow64 */ + "wrong number of types %lu\n", info->NumberOfTypes ); + + info = malloc( len + 16 ); /* Windows gets the length wrong on WoW64 and overflows the buffer */ + memset( info, 0xcc, sizeof(*info) ); + status = pNtQueryObject( NULL, ObjectTypesInformation, info, len, &retlen ); + ok( retlen <= len + 16, "wrong len %lx/%lx\n", len, retlen ); + ok( len == retlen || broken( retlen >= len - 32 && retlen <= len + 32 ), /* wow64 */ + "wrong len %lx/%lx\n", len, retlen ); + ok( !status, "NtQueryObject failed %lx\n", status ); + if (!status) + { + OBJECT_TYPE_INFORMATION *type = align_ptr( info + 1 ); + for (i = 0; i < info->NumberOfTypes; i++) + { + add_object_type( type ); + type = align_ptr( (char *)type->TypeName.Buffer + type->TypeName.MaximumLength ); + } + } + free( info ); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + for (j = 0; j < ARRAY_SIZE(all_types); j++) + { + if (!all_types[j].TypeName.Buffer) continue; + if (wcscmp( tests[i].name, all_types[j].TypeName.Buffer )) continue; + map = all_types[j].GenericMapping; + ok( !memcmp( &map, &tests[i].mapping, sizeof(GENERIC_MAPPING) ) || + broken( !((map.GenericRead ^ tests[i].mapping.GenericRead) & ~tests[i].broken) && + !((map.GenericWrite ^ tests[i].mapping.GenericWrite) & ~tests[i].broken) && + !((map.GenericExecute ^ tests[i].mapping.GenericExecute) & ~tests[i].broken) && + !((map.GenericAll ^ tests[i].mapping.GenericAll) & ~tests[i].broken) ), + "%s: mismatched mappings %08lx,%08lx,%08lx,%08lx / %08lx,%08lx,%08lx,%08lx\n", + debugstr_w( tests[i].name ), + all_types[j].GenericMapping.GenericRead, all_types[j].GenericMapping.GenericWrite, + all_types[j].GenericMapping.GenericExecute, all_types[j].GenericMapping.GenericAll, + tests[i].mapping.GenericRead, tests[i].mapping.GenericWrite, + tests[i].mapping.GenericExecute, tests[i].mapping.GenericAll ); + ok( all_types[j].ValidAccessMask == tests[i].mask || + broken( !((all_types[j].ValidAccessMask ^ tests[i].mask) & ~tests[i].broken) ), + "%s: mismatched access mask %08lx / %08lx\n", debugstr_w( tests[i].name ), + all_types[j].ValidAccessMask, tests[i].mask ); + tested[j] = TRUE; + break; + } + + ok( j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); + } + for (j = 0; j < ARRAY_SIZE(all_types); j++) + { + if (!all_types[j].TypeName.Buffer) continue; + if (tested[j]) continue; + trace( "not tested: %s\n", debugstr_w(all_types[j].TypeName.Buffer )); + } +} + +static DWORD WINAPI test_get_next_thread_proc( void *arg ) +{ + HANDLE event = (HANDLE)arg; + + WaitForSingleObject(event, INFINITE); return 0; } -static void test_mutant(void) +static void test_get_next_thread(void) { - static const WCHAR name[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s', - '\\','t','e','s','t','_','m','u','t','a','n','t',0}; - MUTANT_BASIC_INFORMATION info; + HANDLE hprocess = GetCurrentProcess(); + HANDLE handle, thread, event, prev; + NTSTATUS status; + DWORD thread_id; + BOOL found; + + if (!pNtGetNextThread) + { + win_skip("NtGetNextThread is not available.\n"); + return; + } + + event = CreateEventA(NULL, FALSE, FALSE, NULL); + + thread = CreateThread( NULL, 0, test_get_next_thread_proc, event, 0, &thread_id ); + + status = pNtGetNextThread(hprocess, NULL, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); + + found = FALSE; + prev = NULL; + while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle))) + { + if (prev) + { + if (GetThreadId(handle) == thread_id) + found = TRUE; + pNtClose(prev); + } + else + { + ok(GetThreadId(handle) == GetCurrentThreadId(), "Got unexpected thread id %04lx, current %04lx.\n", + GetThreadId(handle), GetCurrentThreadId()); + } + prev = handle; + handle = (HANDLE)0xdeadbeef; + } + pNtClose(prev); + ok(!handle, "Got unexpected handle %p.\n", handle); + ok(status == STATUS_NO_MORE_ENTRIES, "Unexpected status %#lx.\n", status); + ok(found, "Thread not found.\n"); + + handle = (HANDLE)0xdeadbeef; + status = pNtGetNextThread((void *)0xdeadbeef, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#lx.\n", status); + ok(!handle, "Got unexpected handle %p.\n", handle); + handle = (HANDLE)0xdeadbeef; + status = pNtGetNextThread(hprocess, (void *)0xdeadbeef, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#lx.\n", status); + ok(!handle, "Got unexpected handle %p.\n", handle); + + /* Reversed search is only supported on recent enough Win10. */ + status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 1, &handle); + ok(!status || broken(status == STATUS_INVALID_PARAMETER), "Unexpected status %#lx.\n", status); + if (!status) + pNtClose(handle); + + status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 2, &handle); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); + + SetEvent(event); + WaitForSingleObject(thread, INFINITE); + + found = FALSE; + prev = NULL; + while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle))) + { + if (prev) + pNtClose(prev); + if (GetThreadId(handle) == thread_id) + found = TRUE; + prev = handle; + } + pNtClose(prev); + ok(found, "Thread not found.\n"); + + CloseHandle(thread); +} + +static void test_globalroot(void) +{ + NTSTATUS status; + IO_STATUS_BLOCK iosb; + UNICODE_STRING str; + OBJECT_ATTRIBUTES attr; + HANDLE h; + WCHAR buffer[256]; + ULONG len, full_len, i; + static const struct { const WCHAR *name, *target; } symlinks[] = { + { L"\\??\\GLOBALROOT", L"" }, + { L"\\??\\GLOBALROOT\\??\\GLOBALROOT", L"" }, + { L"\\??\\GLOBALROOT\\??\\GLOBALROOT\\??\\GLOBALROOT", L"" }, + { L"\\??\\GLOBALROOT\\DosDevices", L"\\??" }, + { L"\\??\\GLOBALROOT\\BaseNamedObjects\\Global", NULL }, + }; + + for (i = 0; i < ARRAY_SIZE(symlinks); i++) + { + pRtlInitUnicodeString(&str, symlinks[i].name); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + status = pNtOpenSymbolicLinkObject( &h, SYMBOLIC_LINK_QUERY, &attr ); + ok(status == STATUS_SUCCESS, "NtOpenSymbolicLinkObject failed %08lx\n", status); + + str.Buffer = buffer; + str.MaximumLength = sizeof(buffer); + len = 0xdeadbeef; + memset( buffer, 0xaa, sizeof(buffer) ); + status = pNtQuerySymbolicLinkObject( h, &str, &len); + ok( status == STATUS_SUCCESS, "NtQuerySymbolicLinkObject failed %08lx\n", status ); + full_len = str.Length + sizeof(WCHAR); + ok( len == full_len, "bad length %lu (expected %lu)\n", len, full_len ); + ok( buffer[len / sizeof(WCHAR) - 1] == 0, "no terminating null\n" ); + + if (symlinks[i].target) + { + ok( compare_unicode_string( &str, symlinks[i].target ), + "symlink %s: target expected %s, got %s\n", + debugstr_w( symlinks[i].name ), + debugstr_w( symlinks[i].target ), + debugstr_w( str.Buffer ) ); + } + + pNtClose(h); + } + + pRtlInitUnicodeString(&str, L"\\??\\GLOBALROOT\\Device\\Null"); + InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL); + status = pNtOpenFile(&h, GENERIC_READ | GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0); + ok(status == STATUS_SUCCESS, + "expected STATUS_SUCCESS, got %08lx\n", status); + + test_object_type(h, L"File"); + + pNtClose(h); +} + +static void test_object_identity(void) +{ + NTSTATUS status; + HANDLE h1, h2; + + if (!pNtCompareObjects) + { + win_skip("NtCompareObjects is not available.\n"); + return; + } + + status = pNtCompareObjects( GetCurrentProcess(), GetCurrentProcess() ); + ok( status == STATUS_SUCCESS, "comparing GetCurrentProcess() to self failed with %08lx\n", status ); + + status = pNtCompareObjects( GetCurrentThread(), GetCurrentThread() ); + ok( status == STATUS_SUCCESS, "comparing GetCurrentThread() to self failed with %08lx\n", status ); + + status = pNtCompareObjects( GetCurrentProcess(), GetCurrentThread() ); + ok( status == STATUS_NOT_SAME_OBJECT, "comparing GetCurrentProcess() to GetCurrentThread() returned %08lx\n", status ); + + h1 = NULL; + status = pNtDuplicateObject( GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), + &h1, 0, 0, DUPLICATE_SAME_ACCESS ); + ok( status == STATUS_SUCCESS, "failed to duplicate current process handle: %08lx\n", status); + + status = pNtCompareObjects( GetCurrentProcess(), h1 ); + ok( status == STATUS_SUCCESS, "comparing GetCurrentProcess() with %p failed with %08lx\n", h1, status ); + + pNtClose( h1 ); + + h1 = CreateFileA( "\\\\.\\NUL", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( h1 != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + + h2 = NULL; + status = pNtDuplicateObject( GetCurrentProcess(), h1, GetCurrentProcess(), + &h2, 0, 0, DUPLICATE_SAME_ACCESS ); + ok( status == STATUS_SUCCESS, "failed to duplicate handle %p: %08lx\n", h1, status); + + status = pNtCompareObjects( h1, h2 ); + ok( status == STATUS_SUCCESS, "comparing %p with %p failed with %08lx\n", h1, h2, status ); + + pNtClose( h2 ); + + h2 = CreateFileA( "\\\\.\\NUL", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( h2 != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError() ); + + status = pNtCompareObjects( h1, h2 ); + ok( status == STATUS_NOT_SAME_OBJECT, "comparing %p with %p returned %08lx\n", h1, h2, status ); + + pNtClose( h2 ); + pNtClose( h1 ); +} + +static void test_query_directory(void) +{ + static const DIRECTORY_BASIC_INFORMATION empty_info; + char buffer[200]; + DIRECTORY_BASIC_INFORMATION *info = (void *)buffer; + ULONG context, size, needed_size; + const WCHAR *name1, *name2; + HANDLE dir, child1, child2; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + NTSTATUS status; + BOOL is_wow64 = FALSE; + +#ifndef _WIN64 + if (!IsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; +#endif + + RtlInitUnicodeString( &string, L"\\BaseNamedObjects\\winetest" ); + InitializeObjectAttributes( &attr, &string, 0, 0, NULL ); + status = pNtCreateDirectoryObject( &dir, DIRECTORY_QUERY, &attr ); + ok( !status, "got %#lx\n", status ); + + context = 0xdeadbeef; + size = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, 0, TRUE, TRUE, &context, &size ); + ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == sizeof(*info) || (is_wow64 && !size), "got size %lu\n", size ); + + context = 0xdeadbeef; + size = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, 0, FALSE, TRUE, &context, &size ); + ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == sizeof(*info) || (is_wow64 && !size), "got size %lu\n", size ); + + context = 0xdeadbeef; + size = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, TRUE, &context, &size ); + ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == sizeof(*info) || (is_wow64 && !size), "got size %lu\n", size ); + if (size == sizeof(*info)) + ok( !memcmp( &info[0], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + context = 0xdeadbeef; + size = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, TRUE, &context, &size ); + ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == sizeof(*info) || (is_wow64 && !size), "got size %lu\n", size ); + if (size == sizeof(*info)) + ok( !memcmp( &info[0], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + RtlInitUnicodeString( &string, L"\\BaseNamedObjects\\winetest\\Telamon" ); + status = NtCreateMutant( &child1, GENERIC_ALL, &attr, FALSE ); + ok( !status, "got %#lx\n", status ); + + RtlInitUnicodeString( &string, L"\\BaseNamedObjects\\winetest\\Oileus" ); + status = NtCreateMutant( &child2, GENERIC_ALL, &attr, FALSE ); + ok( !status, "got %#lx\n", status ); + + context = 0xdeadbeef; + size = 0xdeadbeef; + status = NtQueryDirectoryObject( NULL, info, sizeof(buffer), TRUE, TRUE, &context, &size ); + ok( status == STATUS_INVALID_HANDLE, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == 0xdeadbeef || broken(!size) /* WoW64 */, "got size %lu\n", size); + + size = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, TRUE, NULL, &size ); + ok( status == STATUS_ACCESS_VIOLATION, "got %#lx\n", status ); + ok( size == 0xdeadbeef, "got size %lu\n", size); + + context = 0xdeadbeef; + size = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, TRUE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 1, "got context %#lx\n", context ); + ok( size && size < sizeof(buffer), "got size %lu\n", size ); + if (!wcscmp( info[0].ObjectName.Buffer, L"Oileus" )) + { + name1 = L"Oileus"; + name2 = L"Telamon"; + } + else + { + name1 = L"Telamon"; + name2 = L"Oileus"; + } + check_unicode_string( &info[0].ObjectName, name1 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, FALSE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 2, "got context %#lx\n", context ); + check_unicode_string( &info[0].ObjectName, name2 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + size = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, FALSE, &context, &size ); + ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 2, "got context %#lx\n", context ); + ok( size == sizeof(*info) || (is_wow64 && !size), "got size %lu\n", size ); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, TRUE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 1, "got context %#lx\n", context ); + check_unicode_string( &info[0].ObjectName, name1 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + needed_size = size; + + size = 0xdeadbeef; + context = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, 0, TRUE, TRUE, &context, &size ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %#lx\n", status ); + ok( context == 0xdeadbeef, "got context %#lx\n", context ); + ok( size == needed_size, "expected size %lu, got %lu\n", needed_size, size ); + + size = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, needed_size - 1, TRUE, TRUE, &context, &size ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %#lx\n", status ); + ok( size == needed_size, "expected size %lu, got %lu\n", needed_size, size ); + + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, TRUE, &context, NULL ); + ok( !status, "got %#lx\n", status ); + + context = 0; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), TRUE, FALSE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 1, "got context %#lx\n", context ); + check_unicode_string( &info[0].ObjectName, name1 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, TRUE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 2, "got context %#lx\n", context ); + check_unicode_string( &info[0].ObjectName, name1 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + check_unicode_string( &info[1].ObjectName, name2 ); + check_unicode_string( &info[1].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[2], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + needed_size = size; + size = 0xdeadbeef; + context = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, needed_size - 1, FALSE, TRUE, &context, &size ); + ok( status == STATUS_MORE_ENTRIES, "got %#lx\n", status ); + ok( context == 1, "got context %#lx\n", context ); + ok( size > 0 && size < needed_size, "got size %lu\n", size ); + check_unicode_string( &info[0].ObjectName, name1 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + size = 0xdeadbeef; + context = 0xdeadbeef; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(*info), FALSE, TRUE, &context, &size ); + ok( status == STATUS_MORE_ENTRIES + || broken(status == STATUS_BUFFER_TOO_SMALL) /* wow64 */, "got %#lx\n", status ); + if (status == STATUS_MORE_ENTRIES) + { + ok( !context, "got context %#lx\n", context ); + ok( size == sizeof(*info), "got size %lu\n", size ); + ok( !memcmp( &info[0], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + } + + size = 0xdeadbeef; + context = 0xdeadbeef; + status = NtQueryDirectoryObject( dir, info, 0, FALSE, TRUE, &context, &size ); + ok( status == STATUS_MORE_ENTRIES + || broken(status == STATUS_BUFFER_TOO_SMALL) /* wow64 */, "got %#lx\n", status ); + if (status == STATUS_MORE_ENTRIES) + { + ok( !context, "got context %#lx\n", context ); + ok( size == sizeof(*info), "got size %lu\n", size ); + } + + context = 1; + memset( buffer, 0xcc, sizeof(buffer) ); + status = NtQueryDirectoryObject( dir, info, sizeof(buffer), FALSE, FALSE, &context, &size ); + ok( !status, "got %#lx\n", status ); + ok( context == 2, "got context %#lx\n", context ); + check_unicode_string( &info[0].ObjectName, name2 ); + check_unicode_string( &info[0].ObjectTypeName, L"Mutant" ); + ok( !memcmp( &info[1], &empty_info, sizeof(*info) ), "entry was not cleared\n" ); + + pNtClose( child1 ); + pNtClose( child2 ); + pNtClose( dir ); +} + +#define test_object_name_with_null(a,b) _test_object_name_with_null(__LINE__,a,b) +static void _test_object_name_with_null(unsigned line, HANDLE handle, UNICODE_STRING *expect) +{ + char buffer[1024]; + UNICODE_STRING *str = (UNICODE_STRING *)buffer; + ULONG len = 0; + NTSTATUS status; + + memset(buffer, 0, sizeof(buffer)); + status = pNtQueryObject(handle, ObjectNameInformation, buffer, sizeof(buffer), &len); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "got %08lx\n", status); + ok_(__FILE__,line)(len >= sizeof(OBJECT_NAME_INFORMATION) + str->Length, "got %lu\n", len); + ok_(__FILE__,line)(str->Length == expect->Length, "got %u, expected %u\n", str->Length, expect->Length); + ok_(__FILE__,line)(!wcsnicmp(str->Buffer, expect->Buffer, str->Length/sizeof(WCHAR)), "got %s, expected %s\n", + debugstr_w(str->Buffer), debugstr_w(expect->Buffer)); +} + +static void test_null_in_object_name(void) +{ + WCHAR name[256], name3[256], *p, *name_exp, *name3_exp; + HANDLE handle, handle2; + NTSTATUS status; + OBJECT_ATTRIBUTES attr, attr2, attr3; + UNICODE_STRING nameU, name2U, name3U, name2U_exp, name3U_exp; + LARGE_INTEGER size; +#ifndef _WIN64 + BOOL is_wow64 = FALSE; +#endif + + trace("running as %d bit\n", (int)sizeof(void *) * 8); + + swprintf(name, ARRAY_SIZE(name), L"\\Sessions\\%u\\BaseNamedObjects\\wine_test", NtCurrentTeb()->Peb->SessionId); + swprintf(name3, ARRAY_SIZE(name3), L"\\Sessions\\%u\\BaseNamedObjects\\wine_test", NtCurrentTeb()->Peb->SessionId); + p = wcsrchr(name3, '\\'); + p[5] = 0; /* => \\wine\0test */ + + RtlInitUnicodeString(&nameU, name); + InitializeObjectAttributes(&attr, &nameU, 0, 0, NULL); + + name2U = nameU; + name2U.Length += sizeof(WCHAR); /* add terminating \0 to string length */ + InitializeObjectAttributes(&attr2, &name2U, 0, 0, NULL); + + name3U = nameU; + name3U.Buffer = name3; + InitializeObjectAttributes(&attr3, &name3U, 0, 0, NULL); + + status = pNtCreateEvent(&handle, GENERIC_ALL, &attr, NotificationEvent, FALSE); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenEvent(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + status = pNtOpenEvent(&handle2, GENERIC_ALL, &attr2); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateEvent(&handle, GENERIC_ALL, &attr2, NotificationEvent, FALSE); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenEvent(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + status = pNtOpenEvent(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateEvent(&handle, GENERIC_ALL, &attr3, NotificationEvent, FALSE); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenEvent(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateDebugObject(&handle, GENERIC_ALL, &attr, 0); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + pNtClose(handle); + status = pNtCreateDebugObject(&handle, GENERIC_ALL, &attr2, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + pNtClose(handle); + status = pNtCreateDebugObject(&handle, GENERIC_ALL, &attr3, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + pNtClose(handle); + + status = pNtCreateMutant(&handle, GENERIC_ALL, &attr, 0); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenMutant(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + status = pNtOpenMutant(&handle2, GENERIC_ALL, &attr2); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateMutant(&handle, GENERIC_ALL, &attr2, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenMutant(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + status = pNtOpenMutant(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateMutant(&handle, GENERIC_ALL, &attr3, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenMutant(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateSemaphore(&handle, GENERIC_ALL, &attr, 1, 2); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenSemaphore(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + status = pNtOpenSemaphore(&handle2, GENERIC_ALL, &attr2); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateSemaphore(&handle, GENERIC_ALL, &attr2, 1, 2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenSemaphore(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + status = pNtOpenSemaphore(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateSemaphore(&handle, GENERIC_ALL, &attr3, 1, 2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenSemaphore(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateKeyedEvent(&handle, GENERIC_ALL, &attr, 0); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenKeyedEvent(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + status = pNtOpenKeyedEvent(&handle2, GENERIC_ALL, &attr2); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateKeyedEvent(&handle, GENERIC_ALL, &attr2, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenKeyedEvent(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + status = pNtOpenKeyedEvent(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtClose(handle); + status = pNtCreateKeyedEvent(&handle, GENERIC_ALL, &attr3, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenKeyedEvent(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateIoCompletion(&handle, GENERIC_ALL, &attr, 0); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenIoCompletion(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateIoCompletion(&handle, GENERIC_ALL, &attr2, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenIoCompletion(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateIoCompletion(&handle, GENERIC_ALL, &attr3, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenIoCompletion(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateJobObject(&handle, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenJobObject(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateJobObject(&handle, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenJobObject(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateJobObject(&handle, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenJobObject(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + status = pNtCreateTimer(&handle, GENERIC_ALL, &attr, NotificationTimer); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenTimer(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateTimer(&handle, GENERIC_ALL, &attr2, NotificationTimer); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenTimer(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateTimer(&handle, GENERIC_ALL, &attr3, NotificationTimer); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenTimer(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + size.QuadPart = 4096; + status = pNtCreateSection(&handle, GENERIC_ALL, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0); + ok(!status, "got %08lx\n", status); + test_object_name(handle, name); + status = pNtOpenSection(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateSection(&handle, GENERIC_ALL, &attr2, &size, PAGE_READWRITE, SEC_COMMIT, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U); + status = pNtOpenSection(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name2U); + pNtClose(handle2); + pNtClose(handle); + status = pNtCreateSection(&handle, GENERIC_ALL, &attr3, &size, PAGE_READWRITE, SEC_COMMIT, 0); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U); + status = pNtOpenSection(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle2, &name3U); + pNtClose(handle2); + pNtClose(handle); + + wcscpy(name, L"\\Registry\\Machine\\Software\\wine_test"); + wcscpy(name3, L"\\Registry\\Machine\\Software\\wine_test"); + p = wcsrchr(name3, '\\'); + p[5] = 0; /* => \\wine\0test */ + + RtlInitUnicodeString(&nameU, name); + name2U = nameU; + name3U = nameU; + name3U.Buffer = name3; +#ifdef _WIN64 + name_exp = name; + name3_exp = name3; + name2U_exp = name2U; +#else + if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64) + { + name_exp = (WCHAR *)L"\\Registry\\Machine\\Software\\WOW6432Node\\wine_test"; + name3_exp =(WCHAR *) L"\\Registry\\Machine\\Software\\WOW6432Node\\wine\0test"; + } + else + { + name_exp = name; + name3_exp = name3; + } + RtlInitUnicodeString(&name2U_exp, name_exp); +#endif + name3U_exp = name2U_exp; + name3U_exp.Buffer = name3_exp; + name2U.Length += sizeof(WCHAR); /* add terminating \0 to string length */ + name2U_exp.Length += sizeof(WCHAR); /* add terminating \0 to string length */ + + status = pNtCreateKey(&handle, GENERIC_ALL, &attr, 0, NULL, 0, NULL); + ok(!status || status == STATUS_ACCESS_DENIED || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* win8 */, "got %08lx\n", status); + if (!status) + { + test_object_name(handle, name_exp); + status = pNtOpenKey(&handle2, GENERIC_ALL, &attr); + ok(!status, "got %08lx\n", status); + test_object_name(handle2, name_exp); + pNtClose(handle2); + status = pNtOpenKey(&handle2, GENERIC_ALL, &attr2); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtDeleteKey(handle); + pNtClose(handle); + status = pNtCreateKey(&handle, GENERIC_ALL, &attr2, 0, NULL, 0, NULL); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U_exp); + status = pNtOpenKey(&handle2, GENERIC_ALL, &attr2); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name2U_exp); + pNtClose(handle2); + status = pNtOpenKey(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "got %08lx\n", status); + pNtDeleteKey(handle); + pNtClose(handle); + status = pNtCreateKey(&handle, GENERIC_ALL, &attr3, 0, NULL, 0, NULL); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U_exp); + status = pNtOpenKey(&handle2, GENERIC_ALL, &attr3); + ok(!status, "got %08lx\n", status); + test_object_name_with_null(handle, &name3U_exp); + pNtClose(handle2); + pNtDeleteKey(handle); + pNtClose(handle); + } + else + skip("Limited access to \\Registry\\Machine\\Software key, skipping the tests\n"); +} + +static void test_object_permanence(void) +{ + static const struct object_permanence_test { + const char *name; + ULONG initial_attr; + ACCESS_MASK access; + BOOLEAN make_temporary; + BOOLEAN make_permanent; + NTSTATUS make_temp_status; + } tests[] = { + { + .name = "permanent object persists", + .initial_attr = OBJ_PERMANENT, + .access = GENERIC_ALL, + }, + { + .name = "NtMakeTemporaryObject() succeeds", + .initial_attr = OBJ_PERMANENT, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + }, + { + .name = "NtMakeTemporaryObject() fails w/o DELETE access", + .initial_attr = OBJ_PERMANENT, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_temporary = TRUE, + .make_temp_status = STATUS_ACCESS_DENIED, + }, + { + .name = "NtMakePermanentObject() succeeds even if already permanent", + .initial_attr = OBJ_PERMANENT, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_permanent = TRUE, + }, + { + .name = "NtMakePermanentObject() reverses effect of NtMakeTemporaryObject()", + .initial_attr = OBJ_PERMANENT, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + .make_permanent = TRUE, + }, + + { + .name = "temporary object disappears", + .initial_attr = 0, + .access = GENERIC_ALL, + }, + { + .name = "NtMakeTemporaryObject() succeeds even if already temporary", + .initial_attr = 0, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + }, + { + .name = "NtMakeTemporaryObject() fails w/o DELETE access even if already temporary", + .initial_attr = 0, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_temporary = TRUE, + .make_temp_status = STATUS_ACCESS_DENIED, + }, + { + .name = "NtMakePermanentObject() makes an object persist", + .initial_attr = 0, + .access = EVENT_ALL_ACCESS & ~DELETE, + .make_permanent = TRUE, + }, + { + .name = "NtMakePermanentObject() is not annulled by calling NtMakeTemporaryObject() on an already temporary object", + .initial_attr = 0, + .access = GENERIC_ALL, + .make_temporary = TRUE, + .make_temp_status = STATUS_SUCCESS, + .make_permanent = TRUE, + }, + }; + const struct object_permanence_test *test; + HANDLE process_token = NULL, thread_token = NULL; + SECURITY_QUALITY_OF_SERVICE token_qos = { + .Length = sizeof(token_qos), + .ImpersonationLevel = SecurityDelegation, + .ContextTrackingMode = SECURITY_STATIC_TRACKING, + .EffectiveOnly = FALSE, + }; + OBJECT_ATTRIBUTES token_attr = { + .Length = sizeof(token_attr), + .SecurityQualityOfService = &token_qos, + }; + TOKEN_PRIVILEGES new_privs = { + .PrivilegeCount = 1, + .Privileges = { + { + .Luid = { .LowPart = SE_CREATE_PERMANENT_PRIVILEGE }, + .Attributes = SE_PRIVILEGE_ENABLED, + }, + }, + }; + NTSTATUS status; + BOOL creatpermapriv = FALSE; + + status = NtOpenProcessToken( GetCurrentProcess(), TOKEN_DUPLICATE, &process_token ); + ok( status == STATUS_SUCCESS, "NtOpenProcessToken returned %08lx\n", status ); + + status = NtDuplicateToken( process_token, TOKEN_IMPERSONATE | TOKEN_ADJUST_PRIVILEGES, + &token_attr, FALSE, TokenImpersonation, &thread_token ); + ok( status == STATUS_SUCCESS, "NtDuplicateToken returned %08lx\n", status ); + NtClose( process_token ); + + status = NtAdjustPrivilegesToken( thread_token, FALSE, &new_privs, sizeof(new_privs), NULL, NULL ); + ok( status == STATUS_SUCCESS || status == STATUS_NOT_ALL_ASSIGNED, "NtAdjustPrivilegesToken returned %08lx\n", status ); + creatpermapriv = (status == STATUS_SUCCESS); + + status = NtSetInformationThread( GetCurrentThread(), ThreadImpersonationToken, &thread_token, sizeof(thread_token) ); + ok( status == STATUS_SUCCESS, "NtSetInformationThread returned %08lx\n", status ); + NtClose( thread_token ); + + if (!creatpermapriv) skip( "no privileges, tests may be limited\n" ); + + for (test = &tests[0]; test != &tests[ARRAY_SIZE(tests)]; test++) + { + NTSTATUS make_perma_status = creatpermapriv ? STATUS_SUCCESS : STATUS_PRIVILEGE_NOT_HELD; + HANDLE handle, handle2; + OBJECT_BASIC_INFORMATION obi; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + BOOL is_permanent; + ULONG len = 0; + + winetest_push_context( "test#%Iu", test - &tests[0] ); + trace( "(%s)\n", test->name ); + + RtlInitUnicodeString( &name, L"\\BaseNamedObjects\\test_object_permanence" ); + InitializeObjectAttributes( &attr, &name, test->initial_attr, 0, NULL ); + status = NtCreateEvent( &handle, test->access, &attr, NotificationEvent, FALSE ); + if (test->initial_attr & OBJ_PERMANENT) + { + todo_wine_if(status == STATUS_SUCCESS || status == STATUS_PRIVILEGE_NOT_HELD) + ok( status == make_perma_status, "NtCreateEvent returned %08lx (expected %08lx)\n", status, make_perma_status ); + } + else + { + ok( status == STATUS_SUCCESS, "NtCreateEvent returned %08lx\n", status ); + } + if (NT_ERROR(status)) + { + winetest_pop_context(); + continue; + } + is_permanent = (test->initial_attr & OBJ_PERMANENT) != 0; + + status = NtQueryObject( handle, ObjectBasicInformation, &obi, sizeof(obi), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(test->initial_attr != 0) + ok( obi.Attributes == test->initial_attr, "expected attr %08lx, got %08lx\n", test->initial_attr, obi.Attributes ); + + if (test->make_temporary) + { + if (test->make_temp_status == STATUS_ACCESS_DENIED) + ok( !(obi.GrantedAccess & DELETE), "expected no DELETE access in %08lx\n", obi.GrantedAccess ); + if (test->make_temp_status == STATUS_SUCCESS) + ok( !!(obi.GrantedAccess & DELETE), "expected DELETE access in %08lx\n", obi.GrantedAccess ); + + status = NtMakeTemporaryObject( handle ); + ok( status == test->make_temp_status, "NtMakeTemporaryObject returned %08lx\n", status ); + if (!NT_ERROR(status)) is_permanent = FALSE; + } + + if (winetest_debug > 1) + trace( "NOTE: object still has unclosed handle (%p) and shouldn't be deleted", handle ); + + winetest_push_context( "first handle (%p) still open", handle ); + status = pNtOpenEvent( &handle2, GENERIC_ALL, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenEvent returned %08lx\n", status ); + if (!NT_ERROR(status)) + { + ULONG expect_attr = (obi.Attributes & ~OBJ_PERMANENT) | (is_permanent ? OBJ_PERMANENT : 0); + OBJECT_BASIC_INFORMATION obi2; + + status = NtQueryObject( handle2, ObjectBasicInformation, &obi2, sizeof(obi2), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(expect_attr != 0) + ok( obi2.Attributes == expect_attr, "expected attr %08lx, got %08lx\n", expect_attr, obi2.Attributes ); + + NtClose( handle2 ); + } + winetest_pop_context(); + + if (test->make_permanent) + { + status = NtMakePermanentObject( handle ); + todo_wine_if(status == STATUS_SUCCESS || status == STATUS_PRIVILEGE_NOT_HELD) + ok( status == make_perma_status, "NtMakePermanentObject returned %08lx expected (%08lx)\n", status, make_perma_status ); + if (!NT_ERROR(status)) is_permanent = TRUE; + } + + if (winetest_debug > 1) + trace( "NOTE: about to close earlier handle (%p) which should be the last", handle ); + NtClose( handle ); + + winetest_push_context( "first handle closed" ); + status = pNtOpenEvent( &handle, GENERIC_ALL, &attr ); + ok( status == (is_permanent ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND), "NtOpenEvent returned %08lx\n", status ); + if (!NT_ERROR(status)) + { + ULONG expect_attr = (obi.Attributes & ~OBJ_PERMANENT) | (is_permanent ? OBJ_PERMANENT : 0); + OBJECT_BASIC_INFORMATION obi_new; + + status = NtQueryObject( handle, ObjectBasicInformation, &obi_new, sizeof(obi_new), &len ); + ok( status == STATUS_SUCCESS, "NtQueryObject returned %08lx\n", status ); + todo_wine_if(expect_attr != 0) + ok( obi_new.Attributes == expect_attr, "expected attr %08lx, got %08lx\n", expect_attr, obi_new.Attributes ); + + /* ensure object is deleted */ + NtMakeTemporaryObject( handle ); + NtClose( handle ); + } + winetest_pop_context(); + + winetest_pop_context(); + } + + thread_token = NULL; + status = NtSetInformationThread( GetCurrentThread(), ThreadImpersonationToken, &thread_token, sizeof(thread_token) ); + ok( status == STATUS_SUCCESS, "NtSetInformationThread returned %08lx\n", status ); +} + +static void test_zero_access(void) +{ + UNICODE_STRING target = RTL_CONSTANT_STRING( L"\\DosDevices" ); + OBJECT_BASIC_INFORMATION obj_info; + IO_STATUS_BLOCK iosb; + LARGE_INTEGER size, timeout; OBJECT_ATTRIBUTES attr; UNICODE_STRING str; + NTSTATUS status, status2; + WCHAR name[256]; + CLIENT_ID cid; + HANDLE h1, h2; + DWORD err; + + size.QuadPart = 4096; + timeout.QuadPart = -10000; + swprintf( name, ARRAY_SIZE(name), L"\\Sessions\\%u\\BaseNamedObjects\\test_object", NtCurrentTeb()->Peb->SessionId ); + pRtlInitUnicodeString( &str, name ); + InitializeObjectAttributes( &attr, &str, OBJ_OPENIF, 0, NULL ); + + status = pNtCreateEvent( &h1, 0, &attr, NotificationEvent, FALSE ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenEvent( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateEvent( &h1, EVENT_ALL_ACCESS, &attr, NotificationEvent, FALSE ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateEvent( &h2, EVENT_ALL_ACCESS, &attr, NotificationEvent, FALSE ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateEvent( &h2, 0, &attr, NotificationEvent, FALSE ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenEvent( &h2, EVENT_ALL_ACCESS, &attr ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtOpenEvent( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + InitializeObjectAttributes( &attr, &str, OBJ_INHERIT, 0, NULL ); + status = pNtOpenEvent( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + + status = pNtDuplicateObject( GetCurrentProcess(), h1, GetCurrentProcess(), &h2, 0, 0, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtDuplicateObject( GetCurrentProcess(), h1, GetCurrentProcess(), &h2, EVENT_ALL_ACCESS, 0, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h2 ); + CloseHandle( h1 ); + + InitializeObjectAttributes( &attr, &str, OBJ_OPENIF, 0, NULL ); + status = pNtCreateMutant( &h1, 0, &attr, FALSE ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenMutant( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateMutant( &h1, MUTANT_ALL_ACCESS, &attr, FALSE ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateMutant( &h2, MUTANT_ALL_ACCESS, &attr, FALSE ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateMutant( &h2, 0, &attr, FALSE ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenMutant( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateSemaphore( &h1, 0, &attr, 1, 2 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenSemaphore( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateSemaphore( &h1, SEMAPHORE_ALL_ACCESS, &attr, 1, 2 ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateSemaphore( &h2, SEMAPHORE_ALL_ACCESS, &attr, 1, 2 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateSemaphore( &h2, 0, &attr, 1, 2 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenSemaphore( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateKeyedEvent( &h1, 0, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenKeyedEvent( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateKeyedEvent( &h1, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateKeyedEvent( &h2, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateKeyedEvent( &h2, 0, &attr, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenKeyedEvent( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateIoCompletion( &h1, 0, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenIoCompletion( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateIoCompletion( &h1, IO_COMPLETION_ALL_ACCESS, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + status = NtCreateIoCompletion( &h2, IO_COMPLETION_ALL_ACCESS, &attr, 0 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateIoCompletion( &h2, 0, &attr, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenIoCompletion( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateJobObject( &h1, 0, &attr ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenJobObject( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateJobObject( &h1, JOB_OBJECT_ALL_ACCESS, &attr ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateJobObject( &h2, JOB_OBJECT_ALL_ACCESS, &attr ); + ok( status == STATUS_OBJECT_NAME_EXISTS || broken( status == STATUS_ACCESS_DENIED), /* <= win10 1507 */ + "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtCreateJobObject( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenJobObject( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateDirectoryObject( &h1, 0, &attr ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenDirectoryObject( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateDirectoryObject( &h1, DIRECTORY_ALL_ACCESS, &attr ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateDirectoryObject( &h2, DIRECTORY_ALL_ACCESS, &attr ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateDirectoryObject( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenDirectoryObject( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateTimer( &h1, 0, &attr, NotificationTimer ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenTimer( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateTimer( &h1, TIMER_ALL_ACCESS, &attr, NotificationTimer ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateTimer( &h2, TIMER_ALL_ACCESS, &attr, NotificationTimer ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateTimer( &h2, 0, &attr, NotificationTimer ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenTimer( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateSymbolicLinkObject( &h1, 0, &attr, &target ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenSymbolicLinkObject( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateSymbolicLinkObject( &h1, SYMBOLIC_LINK_ALL_ACCESS, &attr, &target ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateSymbolicLinkObject( &h2, SYMBOLIC_LINK_ALL_ACCESS, &attr, &target ); + ok( !status || broken( status == STATUS_OBJECT_NAME_EXISTS ), /* <= win10 1507 */ + "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateSymbolicLinkObject( &h2, 0, &attr, &target ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenSymbolicLinkObject( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateSection( &h1, 0, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenSection( &h1, 0, &attr ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "got %#lx.\n", status ); + status = pNtCreateSection( &h1, SECTION_ALL_ACCESS, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateSection( &h2, SECTION_ALL_ACCESS, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateSection( &h2, 0, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + status = pNtOpenSection( &h2, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + status = pNtCreateDebugObject( &h1, 0, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtCreateDebugObject( &h1, DEBUG_ALL_ACCESS, &attr, 0 ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateDebugObject( &h2, DEBUG_ALL_ACCESS, &attr, 0 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "got %#lx.\n", status ); + CloseHandle( h2 ); + status = pNtCreateDebugObject( &h2, 0, &attr, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\Device\\Null" ); + status = NtCreateFile( &h1, GENERIC_READ | SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = NtCreateFile( &h1, 0, &attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\??\\c:\\windows\\system32\\ntdll.dll" ); + status = NtCreateFile( &h1, GENERIC_READ | SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = NtCreateFile( &h1, 0, &attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\Device\\MailSlot\\abc" ); + status = pNtCreateMailslotFile( &h1, 0, &attr, &iosb, 0, 0, 0, NULL ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + status = pNtCreateMailslotFile( &h1, GENERIC_ALL, &attr, &iosb, 0, 0, 0, NULL ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateMailslotFile( &h2, GENERIC_READ, &attr, &iosb, 0, 0, 0, NULL ); + ok( status == STATUS_OBJECT_NAME_COLLISION, "got %#lx.\n", status ); + status = pNtCreateMailslotFile( &h2, 0, &attr, &iosb, 0, 0, 0, NULL ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\??\\PIPE\\test\\pipe" ); + status = pNtCreateNamedPipeFile( &h1, 0, &attr, &iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, + FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + status = pNtCreateNamedPipeFile( &h1, GENERIC_READ | GENERIC_WRITE, &attr, &iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, + FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout ); + ok( !status, "got %#lx.\n", status ); + status = pNtCreateNamedPipeFile( &h2, GENERIC_READ, &attr, &iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, + FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout ); + ok( status == STATUS_INSTANCE_NOT_AVAILABLE, "got %#lx.\n", status ); + status = pNtCreateNamedPipeFile( &h2, 0, &attr, &iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, + FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h2 ); + CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\REGISTRY\\Machine" ); + status = pNtCreateKey( &h1, 0, &attr, 0, 0, 0, 0 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + status = pNtCreateKey( &h1, KEY_READ, &attr, 0, 0, 0, 0 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenKey( &h1, KEY_READ, &attr ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenKey( &h1, 0, &attr ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + + pRtlInitUnicodeString( &str, L"\\REGISTRY\\Machine\\Software\\foobar-test" ); + status = pNtCreateKey( &h1, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( !status || status == STATUS_ACCESS_DENIED || broken(status == STATUS_OBJECT_PATH_NOT_FOUND), /* win7 */ + "got %#lx.\n", status ); + if (!status) + { + pNtDeleteKey( h1 ); + CloseHandle( h1 ); + } + status2 = pNtCreateKey( &h1, 0, &attr, 0, 0, 0, 0 ); + ok( status2 == status, "got %#lx / %#lx.\n", status2, status ); + if (!status2) + { + pNtDeleteKey( h1 ); + CloseHandle( h1 ); + } + + status = pNtOpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &h1 ); + ok( !status, "got %#lx.\n", status ); + CloseHandle( h1 ); + status = pNtOpenProcessToken( GetCurrentProcess(), 0, &h1 ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + +#if !defined(__REACTOS__) || DLL_EXPORT_VERSION >= 0x600 + status = NtGetNextThread(GetCurrentProcess(), NULL, 0, 0, 0, &h1); + todo_wine ok( status == STATUS_NO_MORE_ENTRIES, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); +#endif + + InitializeObjectAttributes( &attr, NULL, 0, 0, NULL ); + cid.UniqueProcess = ULongToHandle( GetCurrentProcessId() ); + cid.UniqueThread = 0; + status = pNtOpenProcess( &h1, 0, &attr, &cid ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + + InitializeObjectAttributes( &attr, NULL, 0, 0, NULL ); + cid.UniqueProcess = 0; + cid.UniqueThread = ULongToHandle( GetCurrentThreadId() ); + status = pNtOpenThread( &h1, 0, &attr, &cid ); + todo_wine ok( status == STATUS_ACCESS_DENIED, "got %#lx.\n", status ); + if (NT_SUCCESS(status)) CloseHandle( h1 ); + + h1 = CreateWindowStationA( "WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); + err = GetLastError(); + if (h1) + { + CloseWindowStation( h1 ); + h1 = CreateWindowStationA( "WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); + ok( h1 != 0, "CreateWindowStationA failed err %lu\n", GetLastError()); + CloseWindowStation( h1 ); + h1 = CreateWindowStationA( "WinSta0", 0, 0, NULL ); + err = GetLastError(); + todo_wine ok( !h1, "CreateWindowStationA succeeded\n" ); + todo_wine ok( err == ERROR_ACCESS_DENIED, "CreateWindowStationA failed %lu\n", err ); + if (h1) CloseWindowStation( h1 ); + } + else if (err == ERROR_ACCESS_DENIED) win_skip( "Not enough privileges for CreateWindowStation\n" ); + + h1 = OpenWindowStationA( "WinSta0", TRUE, WINSTA_ALL_ACCESS ); + ok( h1 != 0, "OpenWindowStation failed\n" ); + CloseWindowStation( h1 ); + h1 = OpenWindowStationA( "WinSta0", TRUE, 0 ); + err = GetLastError(); + todo_wine ok( !h1, "OpenWindowStation succeeeded\n" ); + todo_wine ok( err == ERROR_ACCESS_DENIED, "OpenWindowStation failed %lu\n", err ); + if (h1) CloseWindowStation( h1 ); + + h1 = CreateDesktopA( "default", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + ok( h1 != 0, "CreateDesktopA failed %lu\n", GetLastError() ); + CloseDesktop( h1 ); + SetLastError( 0xdeadbeef ); + h1 = CreateDesktopA( "default", NULL, NULL, 0, 0, NULL ); + err = GetLastError(); + todo_wine ok( !h1, "CreateDesktopA succeeded\n" ); + todo_wine ok( err == ERROR_ACCESS_DENIED, "CreateDesktopA failed %lu\n", err ); + if (h1) CloseDesktop( h1 ); + + h1 = OpenDesktopA( "default", 0, TRUE, DESKTOP_ALL_ACCESS ); + ok( h1 != 0, "OpenDesktopA failed %lu\n", GetLastError() ); + CloseDesktop( h1 ); + h1 = OpenDesktopA( "default", 0, TRUE, 0 ); + ok( h1 != 0, "OpenDesktopA failed %lu\n", GetLastError() ); + NtQueryObject( h1, ObjectBasicInformation, &obj_info, sizeof(obj_info), NULL ); + ok( obj_info.GrantedAccess == (DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS), + "wrong access %lx\n", obj_info.GrantedAccess ); + CloseDesktop( h1 ); + h1 = OpenDesktopA( "default", 0, TRUE, DESKTOP_CREATEWINDOW ); + ok( h1 != 0, "OpenDesktopA failed %lu\n", GetLastError() ); + NtQueryObject( h1, ObjectBasicInformation, &obj_info, sizeof(obj_info), NULL ); + ok( obj_info.GrantedAccess == (DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_CREATEWINDOW), + "wrong access %lx\n", obj_info.GrantedAccess ); + CloseDesktop( h1 ); + + h1 = OpenInputDesktop( 0, FALSE, DESKTOP_ALL_ACCESS ); + ok( h1 != 0, "OpenInputDesktop failed %lu\n", GetLastError() ); + CloseDesktop( h1 ); + h1 = OpenInputDesktop( 0, FALSE, 0 ); + ok( h1 != 0, "OpenInputDesktop failed %lu\n", GetLastError() ); + NtQueryObject( h1, ObjectBasicInformation, &obj_info, sizeof(obj_info), NULL ); + ok( obj_info.GrantedAccess == (DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS), + "wrong access %lx\n", obj_info.GrantedAccess ); + CloseDesktop( h1 ); + h1 = OpenInputDesktop( 0, FALSE, DESKTOP_ENUMERATE ); + ok( h1 != 0, "OpenInputDesktop failed %lu\n", GetLastError() ); + NtQueryObject( h1, ObjectBasicInformation, &obj_info, sizeof(obj_info), NULL ); + ok( obj_info.GrantedAccess == (DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_ENUMERATE), + "wrong access %lx\n", obj_info.GrantedAccess ); + CloseDesktop( h1 ); +} + +static void test_NtAllocateReserveObject(void) +{ + UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\BaseNamedObjects\\test_NtAllocateReserveObject_name"); + OBJECT_ATTRIBUTES attr; NTSTATUS status; - HANDLE mutant; - HANDLE thread; - DWORD ret; - ULONG len; - LONG prev; + HANDLE handle; + int i; - pRtlInitUnicodeString(&str, name); - InitializeObjectAttributes(&attr, &str, 0, 0, NULL); - status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE); - ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status ); + static const struct + { + MEMORY_RESERVE_OBJECT_TYPE type; + const WCHAR *type_name; + } + tests[] = + { + {MemoryReserveObjectTypeUserApc, L"UserApcReserve"}, + {MemoryReserveObjectTypeIoCompletion, L"IoCompletionReserve"}, + }; - /* bogus */ - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL); - ok( status == STATUS_INFO_LENGTH_MISMATCH, - "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); - status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL); - ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */ - "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08x\n", status ); - status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_INVALID_HANDLE, - "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08x\n", status ); + if (!pNtAllocateReserveObject) + { + win_skip("NtAllocateReserveObject is unavailable.\n"); + return; + } - /* new */ - len = -1; - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); - ok( len == sizeof(info), "got %u\n", len ); + InitializeObjectAttributes(&attr, &name, 0, NULL, NULL); - ret = WaitForSingleObject( mutant, 1000 ); - ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); + /* Parameter checks */ + status = pNtAllocateReserveObject(NULL, &attr, MemoryReserveObjectTypeUserApc); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == -1, "expected -1, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + status = pNtAllocateReserveObject(&handle, NULL, MemoryReserveObjectTypeUserApc); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = NtClose(handle); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); - prev = 0xdeadbeef; - status = pNtReleaseMutant(mutant, &prev); - ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); - ok( prev == -1, "NtQueryRelease failed, expected -1, got %d\n", prev ); + status = pNtAllocateReserveObject(&handle, NULL, MemoryReserveObjectTypeIoCompletion + 1); + ok(status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status); - prev = 0xdeadbeef; - status = pNtReleaseMutant(mutant, &prev); - ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); - ok( prev == 0, "NtQueryRelease failed, expected 0, got %d\n", prev ); + status = pNtAllocateReserveObject(&handle, &attr, MemoryReserveObjectTypeUserApc); + ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status); - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == 1, "expected 1, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + attr.ObjectName = NULL; + status = pNtAllocateReserveObject(&handle, &attr, MemoryReserveObjectTypeUserApc); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = NtClose(handle); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); - /* abandoned */ - thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL ); - ret = WaitForSingleObject( thread, 1000 ); - ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); - CloseHandle( thread ); + /* Test creating objects */ + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + winetest_push_context("type %d", tests[i].type); - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == 1, "expected 0, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState ); + status = pNtAllocateReserveObject(&handle, NULL, tests[i].type); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); - ret = WaitForSingleObject( mutant, 1000 ); - ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08x\n", ret ); + test_object_type(handle, tests[i].type_name); - memset(&info, 0xcc, sizeof(info)); - status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); - ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); - ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); - ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); - ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); - - NtClose( mutant ); + status = NtClose(handle); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + winetest_pop_context(); + } } START_TEST(om) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); - HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); - if (!hntdll) - { - skip("not running on NT, skipping test\n"); - return; - } - - pCreateWaitableTimerA = (void *)GetProcAddress(hkernel32, "CreateWaitableTimerA"); - - pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz"); - pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString"); + pNtAllocateReserveObject= (void *)GetProcAddress(hntdll, "NtAllocateReserveObject"); pNtCreateEvent = (void *)GetProcAddress(hntdll, "NtCreateEvent"); pNtCreateJobObject = (void *)GetProcAddress(hntdll, "NtCreateJobObject"); pNtOpenJobObject = (void *)GetProcAddress(hntdll, "NtOpenJobObject"); @@ -2118,11 +3885,7 @@ START_TEST(om) pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateMutant = (void *)GetProcAddress(hntdll, "NtCreateMutant"); pNtOpenEvent = (void *)GetProcAddress(hntdll, "NtOpenEvent"); - pNtQueryEvent = (void *)GetProcAddress(hntdll, "NtQueryEvent"); - pNtPulseEvent = (void *)GetProcAddress(hntdll, "NtPulseEvent"); pNtOpenMutant = (void *)GetProcAddress(hntdll, "NtOpenMutant"); - pNtQueryMutant = (void *)GetProcAddress(hntdll, "NtQueryMutant"); - pNtReleaseMutant = (void *)GetProcAddress(hntdll, "NtReleaseMutant"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); @@ -2142,12 +3905,20 @@ START_TEST(om) pNtReleaseSemaphore = (void *)GetProcAddress(hntdll, "NtReleaseSemaphore"); pNtCreateKeyedEvent = (void *)GetProcAddress(hntdll, "NtCreateKeyedEvent"); pNtOpenKeyedEvent = (void *)GetProcAddress(hntdll, "NtOpenKeyedEvent"); - pNtWaitForKeyedEvent = (void *)GetProcAddress(hntdll, "NtWaitForKeyedEvent"); - pNtReleaseKeyedEvent = (void *)GetProcAddress(hntdll, "NtReleaseKeyedEvent"); pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion"); pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); - pNtQuerySystemInformation = (void *)GetProcAddress(hntdll, "NtQuerySystemInformation"); + pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile"); + pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess"); + pNtCreateDebugObject = (void *)GetProcAddress(hntdll, "NtCreateDebugObject"); + pNtGetNextThread = (void *)GetProcAddress(hntdll, "NtGetNextThread"); + pNtOpenProcessToken = (void *)GetProcAddress(hntdll, "NtOpenProcessToken"); + pNtOpenThreadToken = (void *)GetProcAddress(hntdll, "NtOpenThreadToken"); + pNtDuplicateToken = (void *)GetProcAddress(hntdll, "NtDuplicateToken"); + pNtDuplicateObject = (void *)GetProcAddress(hntdll, "NtDuplicateObject"); + pNtCompareObjects = (void *)GetProcAddress(hntdll, "NtCompareObjects"); + pNtOpenThread = (void *)GetProcAddress(hntdll, "NtOpenThread"); + test_null_in_object_name(); test_case_sensitive(); test_namespace_pipe(); test_name_collisions(); @@ -2155,10 +3926,17 @@ START_TEST(om) test_directory(); test_symboliclink(); test_query_object(); - test_query_object_types(); test_type_mismatch(); - test_event(); - test_mutant(); - test_keyed_events(); test_null_device(); + test_process(); + test_token(); + test_duplicate_object(); + test_object_types(); + test_get_next_thread(); + test_globalroot(); + test_object_identity(); + test_query_directory(); + test_object_permanence(); + test_zero_access(); + test_NtAllocateReserveObject(); } diff --git a/modules/rostests/winetests/ntdll/path.c b/modules/rostests/winetests/ntdll/path.c index ff5f868f259..db798e8877b 100644 --- a/modules/rostests/winetests/ntdll/path.c +++ b/modules/rostests/winetests/ntdll/path.c @@ -18,7 +18,16 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "ntdll_test.h" +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winnls.h" +#include "ddk/ntddk.h" +#include "wine/test.h" static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen, LPCSTR src, DWORD srclen ); @@ -28,7 +37,9 @@ static ULONG (WINAPI *pRtlIsDosDeviceName_U)( PCWSTR dos_name ); static NTSTATUS (WINAPI *pRtlOemStringToUnicodeString)(UNICODE_STRING *, const STRING *, BOOLEAN ); static BOOLEAN (WINAPI *pRtlIsNameLegalDOS8Dot3)(const UNICODE_STRING*,POEM_STRING,PBOOLEAN); static DWORD (WINAPI *pRtlGetFullPathName_U)(const WCHAR*,ULONG,WCHAR*,WCHAR**); +static BOOLEAN (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR*, UNICODE_STRING*, WCHAR**, CURDIR*); static NTSTATUS (WINAPI *pRtlDosPathNameToNtPathName_U_WithStatus)(const WCHAR*, UNICODE_STRING*, WCHAR**, CURDIR*); +static NTSTATUS (WINAPI *pNtOpenFile)( HANDLE*, ACCESS_MASK, OBJECT_ATTRIBUTES*, IO_STATUS_BLOCK*, ULONG, ULONG ); static void test_RtlDetermineDosPathNameType_U(void) { @@ -68,6 +79,18 @@ static void test_RtlDetermineDosPathNameType_U(void) { "//.foo", 1 }, { "\\\\.", 7 }, { "//.", 7 }, + { "\\\\?\\foo", 6 }, + { "//?/foo", 6 }, + { "/\\?/foo", 6 }, + { "\\\\?foo", 1 }, + { "//?foo", 1 }, + { "\\\\?", 7 }, + { "//?", 7 }, + { "CONIN$", 5 }, + { "CONOUT$", 5 }, + { "CONERR$", 5 }, + { "\\\\.\\CONIN$", 6 }, + { "\\\\.\\CONOUT$", 6 }, { NULL, 0 } }; @@ -105,44 +128,53 @@ static void test_RtlIsDosDeviceName_U(void) { "\\\\.\\CON", 8, 6, TRUE }, /* fails on win8 */ { "\\\\.\\con", 8, 6, TRUE }, /* fails on win8 */ { "\\\\.\\CON2", 0, 0 }, + { "\\\\.\\CONIN$", 0, 0 }, + { "\\\\.\\CONOUT$",0, 0 }, { "", 0, 0 }, { "\\\\foo\\nul", 0, 0 }, { "c:\\nul:", 6, 6 }, { "c:\\nul\\", 0, 0 }, { "c:\\nul\\foo", 0, 0 }, - { "c:\\nul::", 6, 6, TRUE }, /* fails on nt4 */ - { "c:\\nul::::::", 6, 6, TRUE }, /* fails on nt4 */ - { "c:prn ", 4, 6 }, - { "c:prn.......", 4, 6 }, - { "c:prn... ...", 4, 6 }, - { "c:NUL .... ", 4, 6, TRUE }, /* fails on nt4 */ + { "c:\\nul::", 6, 6 }, + { "c:\\nul::::::", 6, 6, TRUE }, /* fails on win11 */ + { "c:prn ", 4, 6, TRUE }, /* fails on win11 */ + { "c:prn.......", 4, 6, TRUE }, /* fails on win11 */ + { "c:prn... ...", 4, 6, TRUE }, /* fails on win11 */ + { "c:NUL .... ", 4, 6 }, { "c: . . .", 0, 0 }, { "c:", 0, 0 }, { " . . . :", 0, 0 }, { ":", 0, 0 }, { "c:nul. . . :", 4, 6 }, - { "c:nul . . :", 4, 6, TRUE }, /* fails on nt4 */ + { "c:nul . . :", 4, 6 }, { "c:nul0", 0, 0 }, - { "c:prn:aaa", 4, 6, TRUE }, /* fails on win9x */ - { "c:PRN:.txt", 4, 6 }, - { "c:aux:.txt...", 4, 6 }, - { "c:prn:.txt:", 4, 6 }, - { "c:nul:aaa", 4, 6, TRUE }, /* fails on win9x */ + { "c:prn:aaa", 4, 6, TRUE }, /* fails on win11 */ + { "c:PRN:.txt", 4, 6, TRUE }, /* fails on win11 */ + { "c:aux:.txt...", 4, 6, TRUE }, /* fails on win11 */ + { "c:prn:.txt:", 4, 6, TRUE }, /* fails on win11 */ + { "c:nul:aaa", 4, 6, TRUE }, /* fails on win11 */ { "con:", 0, 6 }, { "lpt1:", 0, 8 }, - { "c:com5:", 4, 8 }, + { "c:com5:", 4, 8, TRUE }, /* fails on win11 */ { "CoM4:", 0, 8 }, { "lpt9:", 0, 8 }, { "c:\\lpt0.txt", 0, 0 }, + { "CONIN$", 0, 12, TRUE }, /* fails on win7 */ + { "CONOUT$", 0, 14, TRUE }, /* fails on win7 */ + { "CONERR$", 0, 0 }, + { "CON", 0, 6 }, + { "PIPE", 0, 0 }, + { "\\??\\CONIN$", 8, 12, TRUE }, /* fails on win7 */ + { "\\??\\CONOUT$", 8, 14, TRUE }, /* fails on win7 */ + { "\\??\\CONERR$", 0, 0 }, + { "\\??\\CON", 8, 6, TRUE }, /* fails on win11 */ { "c:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\nul.txt", 1000, 6 }, - // ReactOS r54114 - { "c:\\nul", 6, 6 }, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\nul.txt", 1000, 6, TRUE }, /* fails on win11 */ { NULL, 0 } }; @@ -233,14 +265,13 @@ static void test_RtlIsNameLegalDOS8Dot3(void) ok( spaces == test->spaces, "Wrong spaces value %d/%d for '%s'\n", spaces, test->spaces, test->path ); if (strlen(test->path) <= 12) { + STRING test_str; char str[13]; - int i; strcpy( str, test->path ); - for (i = 0; str[i]; i++) str[i] = toupper(str[i]); - ok( oem_ret.Length == strlen(test->path), "Wrong length %d/%d for '%s'\n", - oem_ret.Length, lstrlenA(test->path), test->path ); - ok( !memcmp( oem_ret.Buffer, str, oem_ret.Length ), - "Wrong string '%.*s'/'%s'\n", oem_ret.Length, oem_ret.Buffer, str ); + RtlInitString( &test_str, str ); + RtlUpperString( &test_str, &test_str ); + ok( !RtlCompareString(&oem_ret, &test_str, FALSE), + "Wrong string '%.*s'/'%s'\n", oem_ret.Length, oem_ret.Buffer, test->path ); } } } @@ -261,6 +292,7 @@ static void test_RtlGetFullPathName_U(void) static const struct test tests[] = { { "c:/test", "c:\\test", "test"}, + { "c:/test/", "c:\\test\\", NULL}, { "c:/test ", "c:\\test", "test"}, { "c:/test.", "c:\\test", "test"}, { "c:/test .... .. ", "c:\\test", "test"}, @@ -282,27 +314,80 @@ static void test_RtlGetFullPathName_U(void) { "c:/test../file", "c:\\test.\\file", "file", "c:\\test..\\file", "file"}, /* vista */ { "c:\\test", "c:\\test", "test"}, - { NULL, NULL, NULL} + { "c:\\test\\*.", "c:\\test\\*", "*"}, + { "c:\\test\\a*b.*", "c:\\test\\a*b.*", "a*b.*"}, + { "c:\\test\\a*b*.", "c:\\test\\a*b*", "a*b*"}, + { "C:\\test", "C:\\test", "test"}, + { "c:/", "c:\\", NULL}, + { "c:.", "C:\\windows", "windows"}, + { "c:foo", "C:\\windows\\foo", "foo"}, + { "c:foo/bar", "C:\\windows\\foo\\bar", "bar"}, + { "c:./foo", "C:\\windows\\foo", "foo"}, + { "\\foo", "C:\\foo", "foo"}, + { "foo", "C:\\windows\\foo", "foo"}, + { ".", "C:\\windows", "windows"}, + { "..", "C:\\", NULL}, + { "...", "C:\\windows\\", NULL}, + { "./foo", "C:\\windows\\foo", "foo"}, + { "foo/..", "C:\\windows", "windows"}, + { "\\windows\\nul", "\\\\.\\nul", NULL}, + { "C:\\nonexistent\\nul", "\\\\.\\nul", NULL}, + { "C:\\con\\con", "\\\\.\\con", NULL, + "C:\\con\\con", "con"}, /* win11 */ + { "C:NUL.", "\\\\.\\NUL", NULL}, + { "C:NUL", "\\\\.\\NUL", NULL}, + { "AUX", "\\\\.\\AUX", NULL}, + { "COM1", "\\\\.\\COM1", NULL}, + { "?<>*\"|:", "C:\\windows\\?<>*\"|:", "?<>*\"|:"}, + + { "\\\\foo", "\\\\foo", NULL}, + { "//foo", "\\\\foo", NULL}, + { "\\/foo", "\\\\foo", NULL}, + { "//", "\\\\", NULL}, + { "//foo/", "\\\\foo\\", NULL}, + + { "//.", "\\\\.\\", NULL}, + { "//./", "\\\\.\\", NULL}, + { "//.//", "\\\\.\\", NULL}, + { "//./foo", "\\\\.\\foo", "foo"}, + { "//./foo/", "\\\\.\\foo\\", NULL}, + { "//./foo/bar", "\\\\.\\foo\\bar", "bar"}, + { "//./foo/.", "\\\\.\\foo", "foo"}, + { "//./foo/..", "\\\\.\\", NULL}, + + { "//?/", "\\\\?\\", NULL}, + { "//?//", "\\\\?\\", NULL}, + { "//?/foo", "\\\\?\\foo", "foo"}, + { "//?/foo/", "\\\\?\\foo\\", NULL}, + { "//?/foo/bar", "\\\\?\\foo\\bar", "bar"}, + { "//?/foo/.", "\\\\?\\foo", "foo"}, + { "//?/foo/..", "\\\\?\\", NULL}, + + { "CONIN$", "\\\\.\\CONIN$", NULL, + "C:\\windows\\CONIN$", "CONIN$"}, + { "CONOUT$", "\\\\.\\CONOUT$", NULL, + "C:\\windows\\CONOUT$", "CONOUT$"}, + + /* RtlGetFullPathName_U() can't understand the global namespace prefix */ + { "\\??\\foo", "C:\\??\\foo", "foo"}, + { 0 } }; const struct test *test; WCHAR pathbufW[2*MAX_PATH], rbufferW[MAX_PATH]; - CHAR rbufferA[MAX_PATH], rfileA[MAX_PATH]; + char rbufferA[MAX_PATH], rfileA[MAX_PATH], curdir[MAX_PATH]; ULONG ret; WCHAR *file_part; DWORD reslen; UINT len; - if (!pRtlGetFullPathName_U) - { - win_skip("RtlGetFullPathName_U is not available\n"); - return; - } + GetCurrentDirectoryA(sizeof(curdir), curdir); + SetCurrentDirectoryA("C:\\windows\\"); file_part = (WCHAR *)0xdeadbeef; lstrcpyW(rbufferW, deadbeefW); ret = pRtlGetFullPathName_U(NULL, MAX_PATH, rbufferW, &file_part); - ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %u\n", ret); + ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %lu\n", ret); ok(!lstrcmpW(rbufferW, deadbeefW), "Expected the output buffer to be untouched, got %s\n", wine_dbgstr_w(rbufferW)); ok(file_part == (WCHAR *)0xdeadbeef || @@ -312,7 +397,7 @@ static void test_RtlGetFullPathName_U(void) file_part = (WCHAR *)0xdeadbeef; lstrcpyW(rbufferW, deadbeefW); ret = pRtlGetFullPathName_U(emptyW, MAX_PATH, rbufferW, &file_part); - ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %u\n", ret); + ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %lu\n", ret); ok(!lstrcmpW(rbufferW, deadbeefW), "Expected the output buffer to be untouched, got %s\n", wine_dbgstr_w(rbufferW)); ok(file_part == (WCHAR *)0xdeadbeef || @@ -325,7 +410,7 @@ static void test_RtlGetFullPathName_U(void) pRtlMultiByteToUnicodeN(pathbufW , sizeof(pathbufW), NULL, test->path, strlen(test->path)+1 ); ret = pRtlGetFullPathName_U( pathbufW,MAX_PATH, rbufferW, &file_part); ok( ret == len || (test->alt_rname && ret == strlen(test->alt_rname)*sizeof(WCHAR)), - "Wrong result %d/%d for \"%s\"\n", ret, len, test->path ); + "Wrong result %ld/%d for \"%s\"\n", ret, len, test->path ); ok(pRtlUnicodeToMultiByteN(rbufferA,MAX_PATH,&reslen,rbufferW,(lstrlenW(rbufferW) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS, "RtlUnicodeToMultiByteN failed\n"); ok(!lstrcmpA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpA(rbufferA,test->alt_rname)), @@ -343,45 +428,321 @@ static void test_RtlGetFullPathName_U(void) ok( !test->rfile, "Got NULL expected \"%s\"\n", test->rfile ); } } + + SetCurrentDirectoryA(curdir); } -static void test_RtlDosPathNameToNtPathName_U_WithStatus(void) +static void test_RtlDosPathNameToNtPathName_U(void) { - static const WCHAR emptyW[] = { 0 }; - WCHAR path[MAX_PATH]; + char curdir[MAX_PATH]; UNICODE_STRING nameW; + WCHAR *file_part; NTSTATUS status; + BOOL ret; + int i; - if (!pRtlDosPathNameToNtPathName_U_WithStatus) + static const struct { - win_skip("RtlDosPathNameToNtPathName_U_WithStatus() is not supported.\n"); - return; + const WCHAR *dos; + const WCHAR *nt; + int file_offset; /* offset to file part */ + const WCHAR *alt_nt; + BOOL may_fail; + } + tests[] = + { + {L"c:\\", L"\\??\\c:\\", -1}, + {L"c:\\test\\*.", L"\\??\\c:\\test\\*", 12}, + {L"c:/", L"\\??\\c:\\", -1}, + {L"c:/foo", L"\\??\\c:\\foo", 7}, + {L"c:/foo.", L"\\??\\c:\\foo", 7}, + {L"c:/foo ", L"\\??\\c:\\foo", 7}, + {L"c:/foo . .", L"\\??\\c:\\foo", 7}, + {L"c:/foo.a", L"\\??\\c:\\foo.a", 7}, + {L"c:/foo a", L"\\??\\c:\\foo a", 7}, + {L"c:/foo*", L"\\??\\c:\\foo*", 7}, + {L"c:/foo*a", L"\\??\\c:\\foo*a", 7}, + {L"c:/foo?", L"\\??\\c:\\foo?", 7}, + {L"c:/foo?a", L"\\??\\c:\\foo?a", 7}, + {L"c:/foo<", L"\\??\\c:\\foo<", 7}, + {L"c:/foo", L"\\??\\c:\\foo>", 7}, + {L"c:/foo>a", L"\\??\\c:\\foo>a", 7}, + {L"c:/foo/", L"\\??\\c:\\foo\\", -1}, + {L"c:/foo//", L"\\??\\c:\\foo\\", -1}, + {L"C:/foo", L"\\??\\C:\\foo", 7}, + {L"C:/foo/bar", L"\\??\\C:\\foo\\bar", 11}, + {L"C:/foo/bar", L"\\??\\C:\\foo\\bar", 11}, + {L"c:.", L"\\??\\C:\\windows", 7}, + {L"c:foo", L"\\??\\C:\\windows\\foo", 15}, + {L"c:foo/bar", L"\\??\\C:\\windows\\foo\\bar", 19}, + {L"c:./foo", L"\\??\\C:\\windows\\foo", 15}, + {L"c:/./foo", L"\\??\\c:\\foo", 7}, + {L"c:/..", L"\\??\\c:\\", -1}, + {L"c:/foo/.", L"\\??\\c:\\foo", 7}, + {L"c:/foo/./bar", L"\\??\\c:\\foo\\bar", 11}, + {L"c:/foo/../bar", L"\\??\\c:\\bar", 7}, + {L"\\foo", L"\\??\\C:\\foo", 7}, + {L"foo", L"\\??\\C:\\windows\\foo", 15}, + {L".", L"\\??\\C:\\windows", 7}, + {L"./", L"\\??\\C:\\windows\\", -1}, + {L"..", L"\\??\\C:\\", -1}, + {L"...", L"\\??\\C:\\windows\\", -1}, + {L"./foo", L"\\??\\C:\\windows\\foo", 15}, + {L"foo/..", L"\\??\\C:\\windows", 7}, + {L"\\windows\\nul", L"\\??\\nul", -1}, + {L"C:NUL.", L"\\??\\NUL", -1}, + {L"C:NUL", L"\\??\\NUL", -1}, + {L"AUX" , L"\\??\\AUX", -1}, + {L"COM1" , L"\\??\\COM1", -1}, + {L"?<>*\"|:", L"\\??\\C:\\windows\\?<>*\"|:", 15}, + {L"?:", L"\\??\\?:\\", -1}, + + {L"\\\\foo", L"\\??\\UNC\\foo", -1}, + {L"//foo", L"\\??\\UNC\\foo", -1}, + {L"\\/foo", L"\\??\\UNC\\foo", -1}, + {L"//", L"\\??\\UNC\\", -1}, + {L"//foo/", L"\\??\\UNC\\foo\\", -1}, + + {L"//.", L"\\??\\", -1}, + {L"//./", L"\\??\\", -1}, + {L"//.//", L"\\??\\", -1}, + {L"//./foo", L"\\??\\foo", 4}, + {L"//./foo/", L"\\??\\foo\\", -1}, + {L"//./foo/bar", L"\\??\\foo\\bar", 8}, + {L"//./foo/.", L"\\??\\foo", 4}, + {L"//./foo/..", L"\\??\\", -1}, + {L"//./foo. . ", L"\\??\\foo", 4}, + + {L"//?", L"\\??\\", -1}, + {L"//?/", L"\\??\\", -1}, + {L"//?//", L"\\??\\", -1}, + {L"//?/foo", L"\\??\\foo", 4}, + {L"//?/foo/", L"\\??\\foo\\", -1}, + {L"//?/foo/bar", L"\\??\\foo\\bar", 8}, + {L"//?/foo/.", L"\\??\\foo", 4}, + {L"//?/foo/..", L"\\??\\", -1}, + {L"//?/foo. . ", L"\\??\\foo", 4}, + + {L"\\\\.", L"\\??\\", -1}, + {L"\\\\.\\", L"\\??\\", -1}, + {L"\\\\.\\/", L"\\??\\", -1}, + {L"\\\\.\\foo", L"\\??\\foo", 4}, + {L"\\\\.\\foo/", L"\\??\\foo\\", -1}, + {L"\\\\.\\foo/bar", L"\\??\\foo\\bar", 8}, + {L"\\\\.\\foo/.", L"\\??\\foo", 4}, + {L"\\\\.\\foo/..", L"\\??\\", -1}, + {L"\\\\.\\foo. . ", L"\\??\\foo", 4}, + {L"\\\\.\\CON", L"\\??\\CON", 4, NULL, TRUE}, /* broken on win7 */ + {L"\\\\.\\CONIN$", L"\\??\\CONIN$", 4}, + {L"\\\\.\\CONOUT$", L"\\??\\CONOUT$", 4}, + + {L"\\\\?", L"\\??\\", -1}, + {L"\\\\?\\", L"\\??\\", -1}, + + {L"\\\\?\\/", L"\\??\\/", 4}, + {L"\\\\?\\foo", L"\\??\\foo", 4}, + {L"\\\\?\\foo/", L"\\??\\foo/", 4}, + {L"\\\\?\\foo/bar", L"\\??\\foo/bar", 4}, + {L"\\\\?\\foo/.", L"\\??\\foo/.", 4}, + {L"\\\\?\\foo/..", L"\\??\\foo/..", 4}, + {L"\\\\?\\\\", L"\\??\\\\", -1}, + {L"\\\\?\\\\\\", L"\\??\\\\\\", -1}, + {L"\\\\?\\foo\\", L"\\??\\foo\\", -1}, + {L"\\\\?\\foo\\bar",L"\\??\\foo\\bar", 8}, + {L"\\\\?\\foo\\.", L"\\??\\foo\\.", 8}, + {L"\\\\?\\foo\\..", L"\\??\\foo\\..", 8}, + {L"\\\\?\\foo. . ", L"\\??\\foo. . ", 4}, + + {L"\\??", L"\\??\\C:\\??", 7}, + {L"\\??\\", L"\\??\\C:\\??\\", -1}, + + {L"\\??\\/", L"\\??\\/", 4}, + {L"\\??\\foo", L"\\??\\foo", 4}, + {L"\\??\\foo/", L"\\??\\foo/", 4}, + {L"\\??\\foo/bar", L"\\??\\foo/bar", 4}, + {L"\\??\\foo/.", L"\\??\\foo/.", 4}, + {L"\\??\\foo/..", L"\\??\\foo/..", 4}, + {L"\\??\\\\", L"\\??\\\\", -1}, + {L"\\??\\\\\\", L"\\??\\\\\\", -1}, + {L"\\??\\foo\\", L"\\??\\foo\\", -1}, + {L"\\??\\foo\\bar", L"\\??\\foo\\bar", 8}, + {L"\\??\\foo\\.", L"\\??\\foo\\.", 8}, + {L"\\??\\foo\\..", L"\\??\\foo\\..", 8}, + {L"\\??\\foo. . ", L"\\??\\foo. . ", 4}, + + {L"CONIN$", L"\\??\\CONIN$", -1, L"\\??\\C:\\windows\\CONIN$" /* win7 */ }, + {L"CONOUT$", L"\\??\\CONOUT$", -1, L"\\??\\C:\\windows\\CONOUT$" /* win7 */ }, + {L"cOnOuT$", L"\\??\\cOnOuT$", -1, L"\\??\\C:\\windows\\cOnOuT$" /* win7 */ }, + {L"CONERR$", L"\\??\\C:\\windows\\CONERR$", 15}, + }; + static const WCHAR *error_paths[] = { + NULL, L"", L" ", L"C:\\nonexistent\\nul" + }; + + GetCurrentDirectoryA(sizeof(curdir), curdir); + SetCurrentDirectoryA("C:\\windows\\"); + + for (i = 0; i < ARRAY_SIZE(error_paths); ++i) + { + winetest_push_context("%s", debugstr_w(error_paths[i])); + + ret = pRtlDosPathNameToNtPathName_U(error_paths[i], &nameW, &file_part, NULL); + ok(!ret, "Got %d.\n", ret); + + if (pRtlDosPathNameToNtPathName_U_WithStatus) + { + status = pRtlDosPathNameToNtPathName_U_WithStatus(error_paths[i], &nameW, &file_part, NULL); + ok(status == STATUS_OBJECT_NAME_INVALID, "Got status %#lx.\n", status); + } + + winetest_pop_context(); } - GetCurrentDirectoryW( MAX_PATH, path ); + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + ret = pRtlDosPathNameToNtPathName_U(tests[i].dos, &nameW, &file_part, NULL); + if (!ret && tests[i].may_fail) + { + win_skip("skipping broken %s\n", debugstr_w(tests[i].dos)); + continue; + } + ok(ret == TRUE, "%s: Got %d.\n", debugstr_w(tests[i].dos), ret); - status = pRtlDosPathNameToNtPathName_U_WithStatus( path, &nameW, NULL, NULL ); - ok(!status, "Failed convert to nt path, %#x.\n", status); + if (pRtlDosPathNameToNtPathName_U_WithStatus) + { + RtlFreeUnicodeString(&nameW); + status = pRtlDosPathNameToNtPathName_U_WithStatus(tests[i].dos, &nameW, &file_part, NULL); + ok(status == STATUS_SUCCESS, "%s: Got status %#lx.\n", debugstr_w(tests[i].dos), status); + } - status = pRtlDosPathNameToNtPathName_U_WithStatus( NULL, &nameW, NULL, NULL ); - ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */, - "Unexpected status %#x.\n", status); + ok(!wcscmp(nameW.Buffer, tests[i].nt) + || (tests[i].alt_nt && broken(!wcscmp(nameW.Buffer, tests[i].alt_nt))), + "%s: Expected %s, got %s.\n", debugstr_w(tests[i].dos), + debugstr_w(tests[i].nt), debugstr_w(nameW.Buffer)); - status = pRtlDosPathNameToNtPathName_U_WithStatus( emptyW, &nameW, NULL, NULL ); - ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */, - "Unexpected status %#x.\n", status); + if (!wcscmp(nameW.Buffer, tests[i].nt)) + { + if (tests[i].file_offset > 0) + ok(file_part == nameW.Buffer + tests[i].file_offset, + "%s: Expected file part %s, got %s.\n", debugstr_w(tests[i].dos), + debugstr_w(nameW.Buffer + tests[i].file_offset), debugstr_w(file_part)); + else + ok(file_part == NULL, "%s: Expected NULL file part, got %s.\n", + debugstr_w(tests[i].dos), debugstr_w(file_part)); + } - RtlFreeUnicodeString( &nameW ); + RtlFreeUnicodeString(&nameW); + } + + SetCurrentDirectoryA(curdir); } +static void test_nt_names(void) +{ + static const struct { const WCHAR *root, *name; NTSTATUS expect, broken; } tests[] = + { + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll", STATUS_SUCCESS }, + { NULL, L"\\??\\C:\\\\windows\\system32\\kernel32.dll", STATUS_SUCCESS, STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\", STATUS_FILE_IS_A_DIRECTORY }, + { NULL, L"\\??\\C:\\\\\\windows\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\.\\kernel32.dll", STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\..\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\.\\windows\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll ", STATUS_OBJECT_NAME_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll..", STATUS_OBJECT_NAME_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows \\system32 \\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows.\\system32.\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows/system32/kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll*", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32?\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"C:\\windows\\system32?\\kernel32.dll", STATUS_OBJECT_PATH_SYNTAX_BAD }, + { NULL, L"/??\\C:\\windows\\system32\\kernel32.dll", STATUS_OBJECT_PATH_SYNTAX_BAD }, + { NULL, L"\\??" L"/C:\\windows\\system32\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:/windows\\system32\\kernel32.dll", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\", STATUS_FILE_IS_A_DIRECTORY }, + { NULL, L"\\??\\C:\\windows\\SyStEm32\\", STATUS_FILE_IS_A_DIRECTORY }, + { NULL, L"\\??\\C:\\windows\\system32\\\\", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\foobar\\", STATUS_OBJECT_NAME_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll\\", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\kernel32.dll\\foo", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\system32\\Kernel32.Dll\\", STATUS_OBJECT_NAME_INVALID }, + { NULL, L"\\??\\C:\\windows\\system32\\Kernel32.Dll\\foo", STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, L"\\??\\C:\\windows\\sys\001", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\", NULL, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\", NULL, STATUS_SUCCESS }, + { L"\\??\\C:\\\\", NULL, STATUS_SUCCESS, STATUS_OBJECT_NAME_INVALID }, + { L"/??\\C:\\", NULL, STATUS_OBJECT_PATH_SYNTAX_BAD }, + { L"\\??\\C:/", NULL, STATUS_OBJECT_NAME_NOT_FOUND }, + { L"\\??" L"/C:", NULL, STATUS_OBJECT_NAME_NOT_FOUND }, + { L"\\??" L"/C:\\", NULL, STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows", NULL, STATUS_SUCCESS }, + { L"\\??\\C:\\windows\\", NULL, STATUS_SUCCESS }, + { L"\\??\\C:\\windows\\.", NULL, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\.\\", NULL, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\..", NULL, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\..\\", NULL, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\", L"windows\\system32\\kernel32.dll", STATUS_SUCCESS }, + { L"\\??\\C:\\\\", L"windows\\system32\\kernel32.dll", STATUS_SUCCESS, STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows", L"system32\\kernel32.dll", STATUS_SUCCESS }, + { L"\\??\\C:\\windows\\", L"system32\\kernel32.dll", STATUS_SUCCESS }, + { L"\\??\\C:\\windows\\", L"system32\\", STATUS_FILE_IS_A_DIRECTORY }, + { L"\\??\\C:\\windows\\", L"SyStEm32\\", STATUS_FILE_IS_A_DIRECTORY }, + { L"\\??\\C:\\windows\\", L"system32\\\\", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L"system32\\foobar\\", STATUS_OBJECT_NAME_NOT_FOUND }, + { L"\\??\\C:\\windows\\", L"system32\\kernel32.dll\\", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L"system32\\kernel32.dll\\foo", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows\\", L"system32\\Kernel32.Dll\\", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L"system32\\Kernel32.Dll\\foo", STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows\\", L"\\system32\\kernel32.dll", STATUS_INVALID_PARAMETER }, + { L"\\??\\C:\\windows\\", L"/system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L".\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + { L"\\??\\C:\\windows\\", L"..\\windows\\system32\\kernel32.dll", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L".", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L"..", STATUS_OBJECT_NAME_INVALID }, + { L"\\??\\C:\\windows\\", L"sys\001", STATUS_OBJECT_NAME_INVALID }, + { L"C:\\", L"windows\\system32\\kernel32.dll", STATUS_OBJECT_PATH_SYNTAX_BAD }, + }; + unsigned int i; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE handle; + + InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL ); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + attr.RootDirectory = 0; + handle = 0; + status = STATUS_SUCCESS; + if (tests[i].root) + { + RtlInitUnicodeString( &nameW, tests[i].root ); + status = pNtOpenFile( &attr.RootDirectory, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io, + FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | + FILE_OPEN_FOR_BACKUP_INTENT | FILE_DIRECTORY_FILE ); + } + if (!status && tests[i].name) + { + RtlInitUnicodeString( &nameW, tests[i].name ); + status = pNtOpenFile( &handle, FILE_GENERIC_READ, &attr, &io, FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); + } + if (attr.RootDirectory) NtClose( attr.RootDirectory ); + if (handle) NtClose( handle ); + ok( status == tests[i].expect || broken( tests[i].broken && status == tests[i].broken ), + "%u: got %lx / %lx for %s + %s\n", i, status, tests[i].expect, + debugstr_w( tests[i].root ), debugstr_w( tests[i].name )); + } +} + + START_TEST(path) { HMODULE mod = GetModuleHandleA("ntdll.dll"); - if (!mod) - { - win_skip("Not running on NT, skipping tests\n"); - return; - } pRtlMultiByteToUnicodeN = (void *)GetProcAddress(mod,"RtlMultiByteToUnicodeN"); pRtlUnicodeToMultiByteN = (void *)GetProcAddress(mod,"RtlUnicodeToMultiByteN"); @@ -390,11 +751,14 @@ START_TEST(path) pRtlOemStringToUnicodeString = (void *)GetProcAddress(mod,"RtlOemStringToUnicodeString"); pRtlIsNameLegalDOS8Dot3 = (void *)GetProcAddress(mod,"RtlIsNameLegalDOS8Dot3"); pRtlGetFullPathName_U = (void *)GetProcAddress(mod,"RtlGetFullPathName_U"); + pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(mod, "RtlDosPathNameToNtPathName_U"); pRtlDosPathNameToNtPathName_U_WithStatus = (void *)GetProcAddress(mod, "RtlDosPathNameToNtPathName_U_WithStatus"); + pNtOpenFile = (void *)GetProcAddress(mod, "NtOpenFile"); test_RtlDetermineDosPathNameType_U(); test_RtlIsDosDeviceName_U(); test_RtlIsNameLegalDOS8Dot3(); test_RtlGetFullPathName_U(); - test_RtlDosPathNameToNtPathName_U_WithStatus(); + test_RtlDosPathNameToNtPathName_U(); + test_nt_names(); } diff --git a/modules/rostests/winetests/ntdll/pipe.c b/modules/rostests/winetests/ntdll/pipe.c index 35d61b40adf..c3b2d17a050 100644 --- a/modules/rostests/winetests/ntdll/pipe.c +++ b/modules/rostests/winetests/ntdll/pipe.c @@ -30,6 +30,16 @@ #include "wine/test.h" #include "winternl.h" #include "winioctl.h" +#ifdef __REACTOS__ +/* Wine's headers aren't compatible */ +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define CTL_CODE( DeviceType, Function, Method, Access ) ( \ + (DWORD)((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +/* This isn't in the Windows SDK */ +#define FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif #ifndef __WINE_WINTERNL_H @@ -51,6 +61,13 @@ typedef struct { ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION; +typedef struct _FILE_PIPE_WAIT_FOR_BUFFER { + LARGE_INTEGER Timeout; + ULONG NameLength; + BOOLEAN TimeoutSpecified; + WCHAR Name[1]; +} FILE_PIPE_WAIT_FOR_BUFFER, *PFILE_PIPE_WAIT_FOR_BUFFER; + #ifndef FILE_SYNCHRONOUS_IO_ALERT #define FILE_SYNCHRONOUS_IO_ALERT 0x10 #endif @@ -62,9 +79,17 @@ typedef struct { #ifndef FSCTL_PIPE_LISTEN #define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif + +#ifndef FSCTL_PIPE_WAIT +#define FSCTL_PIPE_WAIT CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif #endif +static BOOL is_wow64; +static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *); + static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size); +static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *); static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access, POBJECT_ATTRIBUTES attr, PIO_STATUS_BLOCK iosb, ULONG sharing, ULONG dispo, ULONG options, @@ -73,10 +98,13 @@ static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access, ULONG inbound_quota, ULONG outbound_quota, PLARGE_INTEGER timeout); static NTSTATUS (WINAPI *pNtQueryInformationFile) (IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass); +static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, void *, ULONG, ULONG *); static NTSTATUS (WINAPI *pNtQueryVolumeInformationFile)(HANDLE handle, PIO_STATUS_BLOCK io, void *buffer, ULONG length, FS_INFORMATION_CLASS info_class); static NTSTATUS (WINAPI *pNtSetInformationFile) (HANDLE handle, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class); static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status); static NTSTATUS (WINAPI *pNtCancelIoFileEx) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status); +static NTSTATUS (WINAPI *pNtCancelSynchronousIoFile) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status); +static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER); static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source); static HANDLE (WINAPI *pOpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); @@ -93,32 +121,74 @@ static BOOL init_func_ptrs(void) } loadfunc(NtFsControlFile) + loadfunc(NtCreateDirectoryObject) loadfunc(NtCreateNamedPipeFile) loadfunc(NtQueryInformationFile) + loadfunc(NtQueryObject) loadfunc(NtQueryVolumeInformationFile) loadfunc(NtSetInformationFile) loadfunc(NtCancelIoFile) +#ifndef __REACTOS__ + loadfunc(NtCancelSynchronousIoFile) +#endif loadfunc(RtlInitUnicodeString) + loadfunc(NtRemoveIoCompletion) /* not fatal */ pNtCancelIoFileEx = (void *)GetProcAddress(module, "NtCancelIoFileEx"); module = GetModuleHandleA("kernel32.dll"); pOpenThread = (void *)GetProcAddress(module, "OpenThread"); pQueueUserAPC = (void *)GetProcAddress(module, "QueueUserAPC"); + pIsWow64Process = (void *)GetProcAddress(module, "IsWow64Process"); +#ifdef __REACTOS__ + pNtCancelSynchronousIoFile = (void *)GetProcAddress(module, "NtCancelSynchronousIoFile"); +#endif return TRUE; } +static HANDLE create_process(const char *arg) +{ + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi; + char cmdline[MAX_PATH]; + char **argv; + BOOL ret; + + si.cb = sizeof(si); + winetest_get_mainargs(&argv); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "got %lu.\n", GetLastError()); + ret = CloseHandle(pi.hThread); + ok(ret, "got %lu.\n", GetLastError()); + return pi.hProcess; +} + static inline BOOL is_signaled( HANDLE obj ) { return WaitForSingleObject( obj, 0 ) == WAIT_OBJECT_0; } +#define test_file_access(a,b) _test_file_access(__LINE__,a,b) +static void _test_file_access(unsigned line, HANDLE handle, DWORD expected_access) +{ + FILE_ACCESS_INFORMATION info; + IO_STATUS_BLOCK io; + NTSTATUS status; + + memset(&info, 0x11, sizeof(info)); + status = NtQueryInformationFile(handle, &io, &info, sizeof(info), FileAccessInformation); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok_(__FILE__,line)(info.AccessFlags == expected_access, "got access %08lx expected %08lx\n", + info.AccessFlags, expected_access); +} + static const WCHAR testpipe[] = { '\\', '\\', '.', '\\', 'p', 'i', 'p', 'e', '\\', 't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 }; static const WCHAR testpipe_nt[] = { '\\', '?', '?', '\\', 'p', 'i', 'p', 'e', '\\', 't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 }; -static NTSTATUS create_pipe(PHANDLE handle, ULONG sharing, ULONG options) +static NTSTATUS create_pipe(PHANDLE handle, ULONG access, ULONG sharing, ULONG options) { IO_STATUS_BLOCK iosb; OBJECT_ATTRIBUTES attr; @@ -131,14 +201,14 @@ static NTSTATUS create_pipe(PHANDLE handle, ULONG sharing, ULONG options) attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.ObjectName = &name; - attr.Attributes = 0x40; /*case insensitive */ + attr.Attributes = OBJ_CASE_INSENSITIVE; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; timeout.QuadPart = -100000000; - res = pNtCreateNamedPipeFile(handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, sharing, 2 /*FILE_CREATE*/, - options, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); + res = pNtCreateNamedPipeFile(handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE | access, &attr, &iosb, sharing, + FILE_CREATE, options, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); return res; } @@ -157,6 +227,48 @@ static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0); } +static NTSTATUS wait_pipe(HANDLE handle, PUNICODE_STRING name, const LARGE_INTEGER* timeout) +{ + HANDLE event; + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK iosb; + FILE_PIPE_WAIT_FOR_BUFFER *pipe_wait; + ULONG pipe_wait_size; + + pipe_wait_size = offsetof(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) + name->Length; + pipe_wait = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pipe_wait_size); + if (!pipe_wait) return STATUS_NO_MEMORY; + + pipe_wait->TimeoutSpecified = !!timeout; + pipe_wait->NameLength = name->Length; + if (timeout) pipe_wait->Timeout = *timeout; + memcpy(pipe_wait->Name, name->Buffer, name->Length); + + InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); + status = NtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, FALSE); + if (status != STATUS_SUCCESS) + { + ok(0, "NtCreateEvent failure: %#lx\n", status); + HeapFree(GetProcessHeap(), 0, pipe_wait); + return status; + } + + memset(&iosb, 0, sizeof(iosb)); + iosb.Status = STATUS_PENDING; + status = pNtFsControlFile(handle, event, NULL, NULL, &iosb, FSCTL_PIPE_WAIT, + pipe_wait, pipe_wait_size, NULL, 0); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = iosb.Status; + } + + NtClose(event); + HeapFree(GetProcessHeap(), 0, pipe_wait); + return status; +} + static void test_create_invalid(void) { IO_STATUS_BLOCK iosb; @@ -172,7 +284,7 @@ static void test_create_invalid(void) attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.ObjectName = &name; - attr.Attributes = 0x40; /*case insensitive */ + attr.Attributes = OBJ_CASE_INSENSITIVE; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; @@ -181,36 +293,36 @@ static void test_create_invalid(void) /* create a pipe with FILE_OVERWRITE */ res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ, 4 /*FILE_OVERWRITE*/, 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); - todo_wine ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res); + ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %lx\n", res); if (!res) CloseHandle(handle); /* create a pipe with FILE_OVERWRITE_IF */ res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ, 5 /*FILE_OVERWRITE_IF*/, 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); - todo_wine ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res); + ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %lx\n", res); if (!res) CloseHandle(handle); /* create a pipe with sharing = 0 */ res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, 0, 2 /*FILE_CREATE*/, 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); - ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res); + ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %lx\n", res); if (!res) CloseHandle(handle); /* create a pipe without r/w access */ res = pNtCreateNamedPipeFile(&handle, SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /*FILE_CREATE*/, 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); - res = pNtQueryInformationFile(handle, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24); - ok(res == STATUS_ACCESS_DENIED, "NtQueryInformationFile returned %x\n", res); + res = pNtQueryInformationFile(handle, &iosb, &info, sizeof(info), FilePipeLocalInformation); + ok(res == STATUS_ACCESS_DENIED, "NtQueryInformationFile returned %lx\n", res); /* test FILE_CREATE creation disposition */ res = pNtCreateNamedPipeFile(&handle2, SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /*FILE_CREATE*/, 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout); - todo_wine ok(res == STATUS_ACCESS_DENIED, "NtCreateNamedPipeFile returned %x\n", res); + ok(res == STATUS_ACCESS_DENIED, "NtCreateNamedPipeFile returned %lx\n", res); if (!res) CloseHandle(handle2); @@ -230,35 +342,35 @@ static void test_create(void) static const DWORD sharing[] = { FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE }; static const DWORD pipe_config[]= { 1, 0, 2 }; - for (j = 0; j < sizeof(sharing) / sizeof(DWORD); j++) { - for (k = 0; k < sizeof(access) / sizeof(DWORD); k++) { + for (j = 0; j < ARRAY_SIZE(sharing); j++) { + for (k = 0; k < ARRAY_SIZE(access); k++) { HANDLE hclient; BOOL should_succeed = TRUE; - res = create_pipe(&hserver, sharing[j], 0); + res = create_pipe(&hserver, 0, sharing[j], 0); if (res) { - ok(0, "NtCreateNamedPipeFile returned %x, sharing: %x\n", res, sharing[j]); + ok(0, "NtCreateNamedPipeFile returned %lx, sharing: %lx\n", res, sharing[j]); continue; } res = listen_pipe(hserver, hEvent, &iosb, FALSE); - ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); - res = pNtQueryInformationFile(hserver, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24); - ok(!res, "NtQueryInformationFile for server returned %x, sharing: %x\n", res, sharing[j]); - ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n", + res = pNtQueryInformationFile(hserver, &iosb, &info, sizeof(info), FilePipeLocalInformation); + ok(!res, "NtQueryInformationFile for server returned %lx, sharing: %lx\n", res, sharing[j]); + ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %ld, expected %ld\n", info.NamedPipeConfiguration, pipe_config[j]); hclient = CreateFileW(testpipe, access[k], 0, 0, OPEN_EXISTING, 0, 0); if (hclient != INVALID_HANDLE_VALUE) { - res = pNtQueryInformationFile(hclient, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24); - ok(!res, "NtQueryInformationFile for client returned %x, access: %x, sharing: %x\n", + res = pNtQueryInformationFile(hclient, &iosb, &info, sizeof(info), FilePipeLocalInformation); + ok(!res, "NtQueryInformationFile for client returned %lx, access: %lx, sharing: %lx\n", res, access[k], sharing[j]); - ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n", + ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %ld, expected %ld\n", info.NamedPipeConfiguration, pipe_config[j]); res = listen_pipe(hclient, hEvent, &iosb, FALSE); - ok(res == STATUS_ILLEGAL_FUNCTION, "expected STATUS_ILLEGAL_FUNCTION, got %x\n", res); + ok(res == STATUS_ILLEGAL_FUNCTION, "expected STATUS_ILLEGAL_FUNCTION, got %lx\n", res); CloseHandle(hclient); } @@ -268,10 +380,10 @@ static void test_create(void) should_succeed &= !!(sharing[j] & FILE_SHARE_READ); if (should_succeed) - ok(hclient != INVALID_HANDLE_VALUE, "CreateFile failed for sharing %x, access: %x, GetLastError: %d\n", + ok(hclient != INVALID_HANDLE_VALUE, "CreateFile failed for sharing %lx, access: %lx, GetLastError: %ld\n", sharing[j], access[k], GetLastError()); else - ok(hclient == INVALID_HANDLE_VALUE, "CreateFile succeeded for sharing %x, access: %x\n", sharing[j], access[k]); + ok(hclient == INVALID_HANDLE_VALUE, "CreateFile succeeded for sharing %lx, access: %lx\n", sharing[j], access[k]); CloseHandle(hserver); } @@ -288,20 +400,20 @@ static void test_overlapped(void) NTSTATUS res; hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError()); + ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %lx\n", GetLastError()); - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); memset(&iosb, 0x55, sizeof(iosb)); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); - ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); + ok(iosb.Status == 0x55555555, "iosb.Status got changed to %lx\n", iosb.Status); hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); - ok(hClient != INVALID_HANDLE_VALUE, "can't open pipe, GetLastError: %x\n", GetLastError()); + ok(hClient != INVALID_HANDLE_VALUE, "can't open pipe, GetLastError: %lx\n", GetLastError()); - ok(U(iosb).Status == 0, "Wrong iostatus %x\n", U(iosb).Status); + ok(iosb.Status == 0, "Wrong iostatus %lx\n", iosb.Status); ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); ok(!ioapc_called, "IOAPC ran too early\n"); @@ -313,20 +425,20 @@ static void test_overlapped(void) CloseHandle(hPipe); CloseHandle(hClient); - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); ok(hClient != INVALID_HANDLE_VALUE || broken(GetLastError() == ERROR_PIPE_BUSY) /* > Win 8 */, - "can't open pipe, GetLastError: %x\n", GetLastError()); + "can't open pipe, GetLastError: %lx\n", GetLastError()); if (hClient != INVALID_HANDLE_VALUE) { SetEvent(hEvent); memset(&iosb, 0x55, sizeof(iosb)); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - ok(res == STATUS_PIPE_CONNECTED, "NtFsControlFile returned %x\n", res); - ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status); + ok(res == STATUS_PIPE_CONNECTED, "NtFsControlFile returned %lx\n", res); + ok(iosb.Status == 0x55555555, "iosb.Status got changed to %lx\n", iosb.Status); ok(!is_signaled(hEvent), "hEvent not signaled\n"); CloseHandle(hClient); @@ -336,73 +448,6 @@ static void test_overlapped(void) CloseHandle(hEvent); } -static void test_completion(void) -{ - static const char buf[] = "testdata"; - FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; - HANDLE port, pipe, client; - IO_STATUS_BLOCK iosb; - OVERLAPPED ov, *pov; - IO_STATUS_BLOCK io; - NTSTATUS status; - DWORD num_bytes; - ULONG_PTR key; - DWORD dwret; - BOOL ret; - - memset(&ov, 0, sizeof(ov)); - ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); - ok(ov.hEvent != INVALID_HANDLE_VALUE, "CreateEvent failed, error %u\n", GetLastError()); - - status = create_pipe(&pipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); - ok(!status, "NtCreateNamedPipeFile returned %x\n", status); - status = listen_pipe(pipe, ov.hEvent, &iosb, FALSE); - ok(status == STATUS_PENDING, "NtFsControlFile returned %x\n", status); - - client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %u\n", GetLastError()); - dwret = WaitForSingleObject(ov.hEvent, 0); - ok(dwret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", dwret); - - port = CreateIoCompletionPort(client, NULL, 0xdeadbeef, 0); - ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); - - ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); - ok(ret, "WriteFile failed, error %u\n", GetLastError()); - ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); - - key = 0; - pov = NULL; - ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); - ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); - ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); - ok(pov == &ov, "expected %p, got %p\n", &ov, pov); - - info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; - status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - - info.Flags = 0; - status = pNtQueryInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); - ok((info.Flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) != 0, "got %08x\n", info.Flags); - - ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); - ok(ret, "WriteFile failed, error %u\n", GetLastError()); - ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); - - pov = (void *)0xdeadbeef; - ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); - ok(!ret, "GetQueuedCompletionStatus succeeded\n"); - ok(pov == NULL, "expected NULL, got %p\n", pov); - - CloseHandle(ov.hEvent); - CloseHandle(client); - CloseHandle(pipe); - CloseHandle(port); -} - static BOOL userapc_called; static void CALLBACK userapc(ULONG_PTR dwParam) { @@ -420,7 +465,7 @@ static DWORD WINAPI thread(PVOID main_thread) DWORD ret; userapc_called = FALSE; ret = pQueueUserAPC(&userapc, main_thread, 0); - ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError()); + ok(ret, "can't queue user apc, GetLastError: %lx\n", GetLastError()); CloseHandle(main_thread); } @@ -450,36 +495,36 @@ static void test_alertable(void) memset(&iosb, 0x55, sizeof(iosb)); hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError()); + ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %lx\n", GetLastError()); - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); /* queue an user apc before calling listen */ userapc_called = FALSE; ret = pQueueUserAPC(&userapc, GetCurrentThread(), 0); - ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError()); + ok(ret, "can't queue user apc, GetLastError: %lx\n", GetLastError()); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %x\n", res); + todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %lx\n", res); - todo_wine ok(userapc_called, "user apc didn't run\n"); - ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status); - todo_wine ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_TIMEOUT, "hEvent signaled\n"); + ok(userapc_called, "user apc didn't run\n"); + ok(iosb.Status == 0x55555555 || iosb.Status == STATUS_CANCELLED, "iosb.Status got changed to %lx\n", iosb.Status); + ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == (iosb.Status == STATUS_CANCELLED ? 0 : WAIT_TIMEOUT), "hEvent signaled\n"); ok(!ioapc_called, "IOAPC ran\n"); /* queue an user apc from a different thread */ hThread = CreateThread(NULL, 0, &thread, pOpenThread(MAXIMUM_ALLOWED, FALSE, GetCurrentThreadId()), 0, 0); - ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError()); + ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %lx\n", GetLastError()); /* wine_todo: the earlier NtFsControlFile call gets cancelled after the pipe gets set into listen state instead of before, so this NtFsControlFile will fail STATUS_INVALID_HANDLE */ res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %x\n", res); + todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %lx\n", res); ok(userapc_called, "user apc didn't run\n"); - ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status); - ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_TIMEOUT, "hEvent signaled\n"); + ok(iosb.Status == 0x55555555 || iosb.Status == STATUS_CANCELLED, "iosb.Status got changed to %lx\n", iosb.Status); + ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == (iosb.Status == STATUS_CANCELLED ? 0 : WAIT_TIMEOUT), "hEvent signaled\n"); ok(!ioapc_called, "IOAPC ran\n"); WaitForSingleObject(hThread, INFINITE); @@ -493,14 +538,14 @@ static void test_alertable(void) /* finally try without an apc */ hThread = CreateThread(NULL, 0, &thread, 0, 0, 0); - ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError()); + ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %lx\n", GetLastError()); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - todo_wine ok(!res, "NtFsControlFile returned %x\n", res); + ok(!res, "NtFsControlFile returned %lx\n", res); ok(open_succeeded, "couldn't open client side pipe\n"); - ok(!U(iosb).Status, "Wrong iostatus %x\n", U(iosb).Status); - todo_wine ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); + ok(!iosb.Status, "Wrong iostatus %lx\n", iosb.Status); + ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); @@ -520,24 +565,24 @@ static void test_nonalertable(void) memset(&iosb, 0x55, sizeof(iosb)); hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError()); + ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %lx\n", GetLastError()); - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); hThread = CreateThread(NULL, 0, &thread, 0, 0, 0); - ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError()); + ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %lx\n", GetLastError()); userapc_called = FALSE; ret = pQueueUserAPC(&userapc, GetCurrentThread(), 0); - ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError()); + ok(ret, "can't queue user apc, GetLastError: %lx\n", GetLastError()); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - todo_wine ok(!res, "NtFsControlFile returned %x\n", res); + ok(!res, "NtFsControlFile returned %lx\n", res); ok(open_succeeded, "couldn't open client side pipe\n"); - todo_wine ok(!U(iosb).Status, "Wrong iostatus %x\n", U(iosb).Status); - todo_wine ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); + ok(!iosb.Status, "Wrong iostatus %lx\n", iosb.Status); + ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); ok(!ioapc_called, "IOAPC ran too early\n"); ok(!userapc_called, "user apc ran too early\n"); @@ -562,20 +607,20 @@ static void test_cancelio(void) NTSTATUS res; hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); - ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError()); + ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %lx\n", GetLastError()); - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); memset(&iosb, 0x55, sizeof(iosb)); res = listen_pipe(hPipe, hEvent, &iosb, TRUE); - ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); res = pNtCancelIoFile(hPipe, &cancel_sb); - ok(!res, "NtCancelIoFile returned %x\n", res); + ok(!res, "NtCancelIoFile returned %lx\n", res); - ok(U(iosb).Status == STATUS_CANCELLED, "Wrong iostatus %x\n", U(iosb).Status); + ok(iosb.Status == STATUS_CANCELLED, "Wrong iostatus %lx\n", iosb.Status); ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); ok(!ioapc_called, "IOAPC ran too early\n"); @@ -584,23 +629,32 @@ static void test_cancelio(void) ok(ioapc_called, "IOAPC didn't run\n"); + res = pNtCancelIoFile(hPipe, &cancel_sb); + ok(!res, "NtCancelIoFile returned %lx\n", res); + ok(iosb.Status == STATUS_CANCELLED, "Wrong iostatus %lx\n", iosb.Status); + CloseHandle(hPipe); if (pNtCancelIoFileEx) { - res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + res = create_pipe(&hPipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); memset(&iosb, 0x55, sizeof(iosb)); res = listen_pipe(hPipe, hEvent, &iosb, FALSE); - ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); res = pNtCancelIoFileEx(hPipe, &iosb, &cancel_sb); - ok(!res, "NtCancelIoFileEx returned %x\n", res); + ok(!res, "NtCancelIoFileEx returned %lx\n", res); - ok(U(iosb).Status == STATUS_CANCELLED, "Wrong iostatus %x\n", U(iosb).Status); + ok(iosb.Status == STATUS_CANCELLED, "Wrong iostatus %lx\n", iosb.Status); ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); + iosb.Status = 0xdeadbeef; + res = pNtCancelIoFileEx(hPipe, NULL, &cancel_sb); + ok(res == STATUS_NOT_FOUND, "NtCancelIoFileEx returned %lx\n", res); + ok(iosb.Status == 0xdeadbeef, "Wrong iostatus %lx\n", iosb.Status); + CloseHandle(hPipe); } else @@ -609,6 +663,145 @@ static void test_cancelio(void) CloseHandle(hEvent); } +struct synchronousio_thread_args +{ + HANDLE pipe; + IO_STATUS_BLOCK iosb; +}; + +static DWORD WINAPI synchronousio_thread(void *arg) +{ + struct synchronousio_thread_args *ctx = arg; + NTSTATUS res; + + res = listen_pipe(ctx->pipe, NULL, &ctx->iosb, FALSE); + ok(res == STATUS_CANCELLED, "NtFsControlFile returned %lx\n", res); + return 0; +} + +static void test_cancelsynchronousio(void) +{ + DWORD ret; + NTSTATUS res; + HANDLE event; + HANDLE thread; + HANDLE client; + IO_STATUS_BLOCK iosb; + struct synchronousio_thread_args ctx; + +#ifdef __REACTOS__ + if (pNtCancelSynchronousIoFile == NULL) + { + win_skip("NtCancelSynchronousIoFile not available\n"); + return; + } +#endif // __REACTOS__ + + /* bogus values */ + res = pNtCancelSynchronousIoFile((HANDLE)0xdeadbeef, NULL, &iosb); + ok(res == STATUS_INVALID_HANDLE, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, NULL); + ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, (IO_STATUS_BLOCK*)0xdeadbeef); + ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), (IO_STATUS_BLOCK*)0xdeadbeef, &iosb); + ok(res == STATUS_NOT_FOUND || broken(is_wow64 && res == STATUS_ACCESS_VIOLATION), + "NtCancelSynchronousIoFile returned %lx\n", res); + if (res != STATUS_ACCESS_VIOLATION) + { + ok(iosb.Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", iosb.Status); + ok(iosb.Information == 0, "iosb.Information got changed to %Iu\n", iosb.Information); + } + + /* synchronous i/o */ + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + + /* NULL io */ + ctx.iosb.Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + thread = CreateThread(NULL, 0, synchronousio_thread, &ctx, 0, 0); + /* wait for I/O to start, which transitions the pipe handle from signaled to nonsignaled state. */ + while ((ret = WaitForSingleObject(ctx.pipe, 0)) == WAIT_OBJECT_0) Sleep(1); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %lu (error %lu)\n", ret, GetLastError()); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(thread, NULL, &iosb); + ok(res == STATUS_SUCCESS, "Failed to cancel I/O\n"); + ok(iosb.Status == STATUS_SUCCESS, "iosb.Status got changed to %lx\n", iosb.Status); + ok(iosb.Information == 0, "iosb.Information got changed to %Iu\n", iosb.Information); + ret = WaitForSingleObject(thread, 1000); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(thread); + CloseHandle(ctx.pipe); + ok(ctx.iosb.Status == 0xdeadbabe || ctx.iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", ctx.iosb.Status); + ok(ctx.iosb.Information == (ctx.iosb.Status == STATUS_CANCELLED ? 0 : 0xdeadbeef), "wrong info %Iu\n", ctx.iosb.Information); + + /* specified io */ + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + + ctx.iosb.Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + thread = CreateThread(NULL, 0, synchronousio_thread, &ctx, 0, 0); + /* wait for I/O to start, which transitions the pipe handle from signaled to nonsignaled state. */ + while ((ret = WaitForSingleObject(ctx.pipe, 0)) == WAIT_OBJECT_0) Sleep(1); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %lu (error %lu)\n", ret, GetLastError()); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(thread, &iosb, &iosb); + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(NULL, &ctx.iosb, &iosb); + ok(res == STATUS_INVALID_HANDLE, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(thread, &ctx.iosb, &iosb); + ok(res == STATUS_SUCCESS || broken(is_wow64 && res == STATUS_NOT_FOUND), + "Failed to cancel I/O\n"); + ok(iosb.Status == STATUS_SUCCESS || broken(is_wow64 && iosb.Status == STATUS_NOT_FOUND), + "iosb.Status got changed to %lx\n", iosb.Status); + ok(iosb.Information == 0, "iosb.Information got changed to %Iu\n", iosb.Information); + if (res == STATUS_NOT_FOUND) + { + res = pNtCancelSynchronousIoFile(thread, NULL, &iosb); + ok(res == STATUS_SUCCESS, "Failed to cancel I/O\n"); + ok(iosb.Status == STATUS_SUCCESS, "iosb.Status got changed to %lx\n", iosb.Status); + } + ret = WaitForSingleObject(thread, 1000); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(thread); + CloseHandle(ctx.pipe); + ok(ctx.iosb.Status == 0xdeadbabe || ctx.iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", ctx.iosb.Status); + ok(ctx.iosb.Information == (ctx.iosb.Status == STATUS_CANCELLED ? 0 : 0xdeadbeef), "wrong info %Iu\n", ctx.iosb.Information); + + /* asynchronous i/o */ + ctx.iosb.Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + event = CreateEventW(NULL, TRUE, FALSE, NULL); + ok(event != INVALID_HANDLE_VALUE, "Can't create event, GetLastError: %lx\n", GetLastError()); + res = listen_pipe(ctx.pipe, event, &ctx.iosb, FALSE); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, &iosb); + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + ok(iosb.Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", iosb.Status); + ok(iosb.Information == 0, "iosb.Information got changed to %Iu\n", iosb.Information); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), &ctx.iosb, &iosb); + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + ok(iosb.Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", iosb.Status); + ok(iosb.Information == 0, "iosb.Information got changed to %Iu\n", iosb.Information); + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_TIMEOUT, "wait returned %lx\n", ret); + client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "can't open pipe: %lu\n", GetLastError()); + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(ctx.pipe); + CloseHandle(event); + CloseHandle(client); +} + static void _check_pipe_handle_state(int line, HANDLE handle, ULONG read, ULONG completion) { IO_STATUS_BLOCK iosb; @@ -617,11 +810,11 @@ static void _check_pipe_handle_state(int line, HANDLE handle, ULONG read, ULONG if (handle != INVALID_HANDLE_VALUE) { memset(&fpi, 0x55, sizeof(fpi)); - res = pNtQueryInformationFile(handle, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok_(__FILE__, line)(!res, "NtQueryInformationFile returned %x\n", res); - ok_(__FILE__, line)(fpi.ReadMode == read, "Unexpected ReadMode, expected %x, got %x\n", + res = pNtQueryInformationFile(handle, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok_(__FILE__, line)(!res, "NtQueryInformationFile returned %lx\n", res); + ok_(__FILE__, line)(fpi.ReadMode == read, "Unexpected ReadMode, expected %lx, got %lx\n", read, fpi.ReadMode); - ok_(__FILE__, line)(fpi.CompletionMode == completion, "Unexpected CompletionMode, expected %x, got %x\n", + ok_(__FILE__, line)(fpi.CompletionMode == completion, "Unexpected CompletionMode, expected %lx, got %lx\n", completion, fpi.CompletionMode); } } @@ -629,6 +822,7 @@ static void _check_pipe_handle_state(int line, HANDLE handle, ULONG read, ULONG static void test_filepipeinfo(void) { + FILE_PIPE_LOCAL_INFORMATION local_info; IO_STATUS_BLOCK iosb; OBJECT_ATTRIBUTES attr; UNICODE_STRING name; @@ -642,48 +836,48 @@ static void test_filepipeinfo(void) attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.ObjectName = &name; - attr.Attributes = 0x40; /* case insensitive */ + attr.Attributes = OBJ_CASE_INSENSITIVE; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; timeout.QuadPart = -100000000; /* test with INVALID_HANDLE_VALUE */ - res = pNtQueryInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtQueryInformationFile returned %x\n", res); + res = pNtQueryInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtQueryInformationFile returned %lx\n", res); fpi.ReadMode = 0; fpi.CompletionMode = 0; - res = pNtSetInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtSetInformationFile returned %lx\n", res); /* server end with read-only attributes */ res = pNtCreateNamedPipeFile(&hServer, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /* FILE_CREATE */, 0, 0, 0, 1, 0xFFFFFFFF, 500, 500, &timeout); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); check_pipe_handle_state(hServer, 0, 1); hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); ok(hClient != INVALID_HANDLE_VALUE || broken(GetLastError() == ERROR_PIPE_BUSY) /* > Win 8 */, - "can't open pipe, GetLastError: %x\n", GetLastError()); + "can't open pipe, GetLastError: %lx\n", GetLastError()); check_pipe_handle_state(hServer, 0, 1); check_pipe_handle_state(hClient, 0, 0); fpi.ReadMode = 0; fpi.CompletionMode = 0; - res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %lx\n", res); check_pipe_handle_state(hServer, 0, 1); check_pipe_handle_state(hClient, 0, 0); fpi.ReadMode = 1; /* invalid on a byte stream pipe */ fpi.CompletionMode = 1; - res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %lx\n", res); check_pipe_handle_state(hServer, 0, 1); check_pipe_handle_state(hClient, 0, 0); @@ -692,8 +886,8 @@ static void test_filepipeinfo(void) { fpi.ReadMode = 1; /* invalid on a byte stream pipe */ fpi.CompletionMode = 1; - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_INVALID_PARAMETER, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_INVALID_PARAMETER, "NtSetInformationFile returned %lx\n", res); } check_pipe_handle_state(hServer, 0, 1); @@ -703,8 +897,8 @@ static void test_filepipeinfo(void) { fpi.ReadMode = 0; fpi.CompletionMode = 1; - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(!res, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(!res, "NtSetInformationFile returned %lx\n", res); } check_pipe_handle_state(hServer, 0, 1); @@ -714,13 +908,13 @@ static void test_filepipeinfo(void) { fpi.ReadMode = 0; fpi.CompletionMode = 2; /* not in range 0-1 */ - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %lx\n", res); fpi.ReadMode = 2; /* not in range 0-1 */ fpi.CompletionMode = 0; - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %lx\n", res); } CloseHandle(hClient); @@ -729,8 +923,8 @@ static void test_filepipeinfo(void) fpi.ReadMode = 0; fpi.CompletionMode = 0; - res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %lx\n", res); CloseHandle(hServer); @@ -738,13 +932,13 @@ static void test_filepipeinfo(void) res = pNtCreateNamedPipeFile(&hServer, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /* FILE_CREATE */, 0, 1, 1, 0, 0xFFFFFFFF, 500, 500, &timeout); - ok(!res, "NtCreateNamedPipeFile returned %x\n", res); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); check_pipe_handle_state(hServer, 1, 0); hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); ok(hClient != INVALID_HANDLE_VALUE || broken(GetLastError() == ERROR_PIPE_BUSY) /* > Win 8 */, - "can't open pipe, GetLastError: %x\n", GetLastError()); + "can't open pipe, GetLastError: %lx\n", GetLastError()); check_pipe_handle_state(hServer, 1, 0); check_pipe_handle_state(hClient, 0, 0); @@ -753,8 +947,8 @@ static void test_filepipeinfo(void) { fpi.ReadMode = 1; fpi.CompletionMode = 1; - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(!res, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(!res, "NtSetInformationFile returned %lx\n", res); } check_pipe_handle_state(hServer, 1, 0); @@ -762,8 +956,8 @@ static void test_filepipeinfo(void) fpi.ReadMode = 0; fpi.CompletionMode = 1; - res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(!res, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(!res, "NtSetInformationFile returned %lx\n", res); check_pipe_handle_state(hServer, 0, 1); check_pipe_handle_state(hClient, 1, 1); @@ -772,13 +966,13 @@ static void test_filepipeinfo(void) { fpi.ReadMode = 0; fpi.CompletionMode = 2; /* not in range 0-1 */ - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %lx\n", res); fpi.ReadMode = 2; /* not in range 0-1 */ fpi.CompletionMode = 0; - res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %lx\n", res); } CloseHandle(hClient); @@ -787,19 +981,48 @@ static void test_filepipeinfo(void) fpi.ReadMode = 1; fpi.CompletionMode = 0; - res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23); - ok(!res, "NtSetInformationFile returned %x\n", res); + res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), FilePipeInformation); + ok(!res, "NtSetInformationFile returned %lx\n", res); check_pipe_handle_state(hServer, 1, 0); CloseHandle(hServer); + + res = pNtCreateNamedPipeFile(&hServer, + FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, + 0, 1, 1, 0, 0xFFFFFFFF, 500, 500, &timeout); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + + res = NtCreateFile(&hClient, SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok(!res, "NtCreateFile returned %lx\n", res); + + test_file_access(hClient, SYNCHRONIZE); + + res = pNtQueryInformationFile(hClient, &iosb, &local_info, sizeof(local_info), + FilePipeLocalInformation); + ok(res == STATUS_ACCESS_DENIED, + "NtQueryInformationFile(FilePipeLocalInformation) returned: %lx\n", res); + + res = pNtQueryInformationFile(hClient, &iosb, &local_info, sizeof(local_info), + FilePipeInformation); + ok(res == STATUS_ACCESS_DENIED, + "NtQueryInformationFile(FilePipeInformation) returned: %lx\n", res); + + res = pNtQueryInformationFile(hClient, &iosb, &local_info, sizeof(local_info), + FileNameInformation); + ok(res == STATUS_SUCCESS, "NtQueryInformationFile(FileNameInformation) returned: %lx\n", res); + + CloseHandle(hClient); + CloseHandle(hServer); } static void WINAPI apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved ) { int *count = arg; (*count)++; - ok( !reserved, "reserved is not 0: %x\n", reserved ); + ok( !reserved, "reserved is not 0: %lx\n", reserved ); } static void test_peek(HANDLE pipe) @@ -811,16 +1034,16 @@ static void test_peek(HANDLE pipe) memset(&iosb, 0x55, sizeof(iosb)); status = NtFsControlFile(pipe, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf)); - ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status); - ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status); - ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable); + ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %lx\n", status); + ok(!iosb.Status, "iosb.Status = %lx\n", iosb.Status); + ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %lu\n", buf.ReadDataAvailable); ResetEvent(event); memset(&iosb, 0x55, sizeof(iosb)); status = NtFsControlFile(pipe, event, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, &buf, sizeof(buf)); - ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %x\n", status); - ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %u\n", buf.ReadDataAvailable); - ok(!iosb.Status, "iosb.Status = %x\n", iosb.Status); + ok(!status || status == STATUS_PENDING, "NtFsControlFile failed: %lx\n", status); + ok(buf.ReadDataAvailable == 1, "ReadDataAvailable = %lu\n", buf.ReadDataAvailable); + ok(!iosb.Status, "iosb.Status = %lx\n", iosb.Status); ok(is_signaled(event), "event is not signaled\n"); CloseHandle(event); @@ -830,30 +1053,31 @@ static void test_peek(HANDLE pipe) static BOOL create_pipe_pair( HANDLE *read, HANDLE *write, ULONG flags, ULONG type, ULONG size ) { - const BOOL server_reader = flags & PIPE_ACCESS_INBOUND; HANDLE client, server; server = CreateNamedPipeA(PIPENAME, flags, PIPE_WAIT | type, 1, size, size, NMPWAIT_USE_DEFAULT_WAIT, NULL); ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); - client = CreateFileA(PIPENAME, server_reader ? GENERIC_WRITE : GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0, + client = CreateFileA(PIPENAME, (flags & PIPE_ACCESS_INBOUND ? GENERIC_WRITE : 0) + | (flags & PIPE_ACCESS_OUTBOUND ? GENERIC_READ : 0) + | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, flags & FILE_FLAG_OVERLAPPED, 0); - ok(client != INVALID_HANDLE_VALUE, "CreateFile failed (%d)\n", GetLastError()); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed (%ld)\n", GetLastError()); - if(server_reader) + if ((type & PIPE_READMODE_MESSAGE) && (flags & PIPE_ACCESS_OUTBOUND)) + { + DWORD read_mode = PIPE_READMODE_MESSAGE; + ok(SetNamedPipeHandleState(client, &read_mode, NULL, NULL), "Change mode\n"); + } + + if (flags & PIPE_ACCESS_INBOUND) { *read = server; *write = client; } else { - if(type & PIPE_READMODE_MESSAGE) - { - DWORD read_mode = PIPE_READMODE_MESSAGE; - ok(SetNamedPipeHandleState(client, &read_mode, NULL, NULL), "Change mode\n"); - } - *read = client; *write = server; } @@ -874,21 +1098,21 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; /* try read with no data */ - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; ok( is_signaled( read ), "read handle is not signaled\n" ); status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( read ), "read handle is signaled\n" ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + ok(ret && written == 1, "WriteFile error %ld\n", GetLastError()); /* iosb updated here by async i/o */ - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( !is_signaled( read ), "read handle is signaled\n" ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); @@ -900,20 +1124,20 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* with no event, the pipe handle itself gets signaled */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; ok( !is_signaled( read ), "read handle is signaled\n" ); status = NtReadFile( read, 0, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( read ), "read handle is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + ok(ret && written == 1, "WriteFile error %ld\n", GetLastError()); /* iosb updated here by async i/o */ - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( read ), "read handle is not signaled\n" ); ok( !apc_count, "apc was called\n" ); apc_count = 0; @@ -924,18 +1148,18 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* now read with data ready */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; ResetEvent( event ); ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + ok(ret && written == 1, "WriteFile error %ld\n", GetLastError()); test_peek(read); status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, FALSE ); /* non-alertable sleep */ @@ -945,23 +1169,41 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* now partial read with data ready */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; ResetEvent( event ); ret = WriteFile( write, buffer, 2, &written, NULL ); - ok(ret && written == 2, "WriteFile error %d\n", GetLastError()); - status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - if (pipe_type & PIPE_READMODE_MESSAGE) + ok(ret && written == 2, "WriteFile error %ld\n", GetLastError()); + + memset( &iosb, 0xcc, sizeof(iosb) ); + status = NtFsControlFile( read, NULL, NULL, NULL, &iosb, FSCTL_PIPE_PEEK, NULL, 0, buffer, + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[1]) ); + if (pipe_type & PIPE_TYPE_MESSAGE) { - ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", status ); - ok( U(iosb).Status == STATUS_BUFFER_OVERFLOW, "wrong status %x\n", U(iosb).Status ); + ok( status == STATUS_BUFFER_OVERFLOW || status == STATUS_PENDING, + "FSCTL_PIPE_PEEK returned %lx\n", status ); + ok( iosb.Status == STATUS_BUFFER_OVERFLOW, "wrong status %lx\n", iosb.Status ); } else { - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); + ok( !status || status == STATUS_PENDING, "FSCTL_PIPE_PEEK returned %lx\n", status ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); } - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( iosb.Information == FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[1]), + "wrong info %Iu\n", iosb.Information ); + + status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); + if (pipe_type & PIPE_READMODE_MESSAGE) + { + ok( status == STATUS_BUFFER_OVERFLOW, "wrong status %lx\n", status ); + ok( iosb.Status == STATUS_BUFFER_OVERFLOW, "wrong status %lx\n", iosb.Status ); + } + else + { + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + } + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, FALSE ); /* non-alertable sleep */ @@ -970,9 +1212,9 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) ok( apc_count == 1, "apc not called\n" ); apc_count = 0; status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_SUCCESS, "wrong status %x\n", status ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_SUCCESS, "wrong status %lx\n", status ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, FALSE ); /* non-alertable sleep */ @@ -982,35 +1224,35 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* try read with no data */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; ok( is_signaled( event ), "event is not signaled\n" ); /* check that read resets the event */ status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); ret = WriteFile( write, buffer, 1, &written, NULL ); - ok(ret && written == 1, "WriteFile error %d\n", GetLastError()); + ok(ret && written == 1, "WriteFile error %ld\n", GetLastError()); /* partial read is good enough */ Sleep(1); /* FIXME: needed for wine to run the i/o apc */ ok( is_signaled( event ), "event is not signaled\n" ); - ok( U(iosb).Status == 0, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 1, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 1, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ ok( apc_count == 1, "apc was not called\n" ); /* read from disconnected pipe */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; CloseHandle( write ); status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_PIPE_BROKEN, "wrong status %x\n", status ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_PIPE_BROKEN, "wrong status %lx\n", status ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !is_signaled( event ), "event is signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1019,12 +1261,12 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* read from disconnected pipe, with invalid event handle */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( read, (HANDLE)0xdeadbeef, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_INVALID_HANDLE, "wrong status %lx\n", status ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ ok( !apc_count, "apc was called\n" ); @@ -1032,13 +1274,13 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* read from closed handle */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; SetEvent( event ); status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 1, NULL, NULL ); - ok( status == STATUS_INVALID_HANDLE, "wrong status %x\n", status ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( status == STATUS_INVALID_HANDLE, "wrong status %lx\n", status ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); /* not reset on invalid handle */ ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1047,18 +1289,18 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* disconnect while async read is in progress */ if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); CloseHandle( write ); Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_PIPE_BROKEN, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_PIPE_BROKEN, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1067,49 +1309,49 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; ret = DuplicateHandle(GetCurrentProcess(), read, GetCurrentProcess(), &handle, 0, TRUE, DUPLICATE_SAME_ACCESS); - ok(ret, "Failed to duplicate handle: %d\n", GetLastError()); + ok(ret, "Failed to duplicate handle: %ld\n", GetLastError()); apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( handle, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); /* Cancel by other handle */ status = pNtCancelIoFile( read, &iosb2 ); - ok(status == STATUS_SUCCESS, "failed to cancel by different handle: %x\n", status); + ok(status == STATUS_SUCCESS, "failed to cancel by different handle: %lx\n", status); Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ ok( apc_count == 1, "apc was not called\n" ); apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); /* Close queued handle */ CloseHandle( read ); SleepEx( 1, TRUE ); /* alertable sleep */ - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); status = pNtCancelIoFile( read, &iosb2 ); ok(status == STATUS_INVALID_HANDLE, "cancelled by closed handle?\n"); status = pNtCancelIoFile( handle, &iosb2 ); - ok(status == STATUS_SUCCESS, "failed to cancel: %x\n", status); + ok(status == STATUS_SUCCESS, "failed to cancel: %lx\n", status); Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1123,19 +1365,19 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) if (!create_pipe_pair( &read, &write, FILE_FLAG_OVERLAPPED | pipe_flags, pipe_type, 4096 )) return; apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); status = pNtCancelIoFileEx( read, &iosb, &iosb2 ); ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n"); Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1143,25 +1385,25 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) /* Duplicate iosb */ apc_count = 0; - U(iosb).Status = 0xdeadbabe; + iosb.Status = 0xdeadbabe; iosb.Information = 0xdeadbeef; status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); status = NtReadFile( read, event, apc, &apc_count, &iosb, buffer, 2, NULL, NULL ); - ok( status == STATUS_PENDING, "wrong status %x\n", status ); + ok( status == STATUS_PENDING, "wrong status %lx\n", status ); ok( !is_signaled( event ), "event is signaled\n" ); - ok( U(iosb).Status == 0xdeadbabe, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0xdeadbeef, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == 0xdeadbabe, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0xdeadbeef, "wrong info %Iu\n", iosb.Information ); ok( !apc_count, "apc was called\n" ); status = pNtCancelIoFileEx( read, &iosb, &iosb2 ); ok(status == STATUS_SUCCESS, "Failed to cancel I/O\n"); Sleep(1); /* FIXME: needed for wine to run the i/o apc */ - ok( U(iosb).Status == STATUS_CANCELLED, "wrong status %x\n", U(iosb).Status ); - ok( iosb.Information == 0, "wrong info %lu\n", iosb.Information ); + ok( iosb.Status == STATUS_CANCELLED, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 0, "wrong info %Iu\n", iosb.Information ); ok( is_signaled( event ), "event is not signaled\n" ); ok( !apc_count, "apc was called\n" ); SleepEx( 1, TRUE ); /* alertable sleep */ @@ -1176,6 +1418,475 @@ static void read_pipe_test(ULONG pipe_flags, ULONG pipe_type) CloseHandle(event); } +static void test_transceive(void) +{ + IO_STATUS_BLOCK iosb; + HANDLE caller, callee; + HANDLE event = CreateEventA( NULL, TRUE, FALSE, NULL ); + char buffer[128]; + DWORD written; + BOOL ret; + NTSTATUS status; + + if (!create_pipe_pair( &caller, &callee, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096 )) return; + + status = NtFsControlFile( caller, event, NULL, NULL, &iosb, FSCTL_PIPE_TRANSCEIVE, + (BYTE*)"test", 4, buffer, sizeof(buffer) ); + ok( status == STATUS_PENDING, "NtFsControlFile(FSCTL_PIPE_TRANSCEIVE) returned %lx\n", status); + ok( !is_signaled( event ), "event is signaled\n" ); + + ret = WriteFile( callee, buffer, 2, &written, NULL ); + ok(ret && written == 2, "WriteFile error %ld\n", GetLastError()); + + ok( iosb.Status == 0, "wrong status %lx\n", iosb.Status ); + ok( iosb.Information == 2, "wrong info %Iu\n", iosb.Information ); + ok( is_signaled( event ), "event is not signaled\n" ); + + CloseHandle( caller ); + CloseHandle( callee ); +} + +#define test_no_queued_completion(a) _test_no_queued_completion(__LINE__,a) +static void _test_no_queued_completion(unsigned line, HANDLE port) +{ + OVERLAPPED *pov; + DWORD num_bytes; + ULONG_PTR key; + BOOL ret; + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 10); + ok_(__FILE__,line)(!ret && GetLastError() == WAIT_TIMEOUT, + "GetQueuedCompletionStatus returned %x(%lu)\n", ret, GetLastError()); +} + +#define test_queued_completion(a,b,c,d) _test_queued_completion(__LINE__,a,b,c,d) +static void _test_queued_completion(unsigned line, HANDLE port, IO_STATUS_BLOCK *io, + NTSTATUS expected_status, ULONG expected_information) +{ + LARGE_INTEGER timeout = {{0}}; + ULONG_PTR value = 0xdeadbeef; + IO_STATUS_BLOCK iosb; + ULONG_PTR key; + NTSTATUS status; + + status = pNtRemoveIoCompletion(port, &key, &value, &iosb, &timeout); + ok_(__FILE__,line)(status == STATUS_SUCCESS, "NtRemoveIoCompletion returned %lx\n", status); + ok_(__FILE__,line)(value == (ULONG_PTR)io, "value = %Ix\n", value); + ok_(__FILE__,line)(io->Status == expected_status, "Status = %lx\n", io->Status); + ok_(__FILE__,line)(io->Information == expected_information, + "Information = %Iu\n", io->Information); +} + +static void test_completion(void) +{ + static const char buf[] = "testdata"; + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + FILE_PIPE_PEEK_BUFFER peek_buf; + char read_buf[16]; + HANDLE port, pipe, client, event; + OVERLAPPED ov; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD num_bytes; + BOOL ret; + + create_pipe_pair( &pipe, &client, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096 ); + + status = pNtQueryInformationFile(pipe, &io, &info, sizeof(info), + FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS || broken(status == STATUS_INVALID_INFO_CLASS), + "status = %lx\n", status); + if (status) + { + win_skip("FileIoCompletionNotificationInformation not supported\n"); + CloseHandle(pipe); + CloseHandle(client); + return; + } + + memset(&ov, 0, sizeof(ov)); + ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + ok(ov.hEvent != INVALID_HANDLE_VALUE, "CreateEvent failed, error %lu\n", GetLastError()); + + port = CreateIoCompletionPort(client, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %lu\n", GetLastError()); + + ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); + test_queued_completion(port, (IO_STATUS_BLOCK*)&ov, STATUS_SUCCESS, num_bytes); + + status = NtFsControlFile(client, NULL, NULL, &io, &io, FSCTL_PIPE_PEEK, + NULL, 0, &peek_buf, sizeof(peek_buf)); + ok(status == STATUS_PENDING || status == STATUS_SUCCESS, "FSCTL_PIPE_PEEK returned %lx\n", status); + test_queued_completion(port, &io, STATUS_SUCCESS, FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data)); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + + ret = WriteFile(client, buf, sizeof(buf), &num_bytes, &ov); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); + test_no_queued_completion(port); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, &ov); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %lu\n", num_bytes); + + status = NtReadFile(client, NULL, NULL, &io, &io, read_buf, 1, NULL, NULL); + ok(status == STATUS_BUFFER_OVERFLOW || status == STATUS_PENDING, "status = %lx\n", status); + ok(io.Status == STATUS_BUFFER_OVERFLOW, "Status = %lx\n", io.Status); + ok(io.Information == 1, "Information = %Iu\n", io.Information); + if(status == STATUS_PENDING) /* win8+ */ + test_queued_completion(port, &io, STATUS_BUFFER_OVERFLOW, 1); + else + test_no_queued_completion(port); + + status = NtReadFile(client, NULL, NULL, &io, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_SUCCESS, "status = %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == sizeof(buf)-1, "Information = %Iu\n", io.Information); + test_no_queued_completion(port); + + status = NtFsControlFile(client, NULL, NULL, &io, &io, FSCTL_PIPE_PEEK, + NULL, 0, &peek_buf, sizeof(peek_buf)); + ok(status == STATUS_PENDING || status == STATUS_SUCCESS, "FSCTL_PIPE_PEEK returned %lx\n", status); + if(status == STATUS_PENDING) /* win8+ */ + test_queued_completion(port, &io, STATUS_SUCCESS, FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data)); + else + test_no_queued_completion(port); + + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, ov.hEvent, NULL, &io, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(ov.hEvent), "event is signtaled\n"); + test_no_queued_completion(port); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + test_queued_completion(port, &io, STATUS_SUCCESS, sizeof(buf)); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + status = NtFsControlFile(client, NULL, NULL, &io, &io, FSCTL_PIPE_PEEK, + NULL, 0, &peek_buf, sizeof(peek_buf)); + ok(status == STATUS_PENDING || status == STATUS_BUFFER_OVERFLOW, + "FSCTL_PIPE_PEEK returned %lx\n", status); + if(status == STATUS_PENDING) /* win8+ */ + test_queued_completion(port, &io, STATUS_BUFFER_OVERFLOW, sizeof(peek_buf)); + else + test_no_queued_completion(port); + + CloseHandle(ov.hEvent); + CloseHandle(client); + CloseHandle(pipe); + CloseHandle(port); + + event = CreateEventW(NULL, TRUE, TRUE, NULL); + create_pipe_pair( &pipe, &client, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096 ); + + ok(is_signaled(client), "client is not signaled\n"); + + /* no event, APC nor completion: only signals on handle */ + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, NULL, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(client), "client is signaled\n"); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == sizeof(buf), "Information = %Iu\n", io.Information); + + /* event with no APC nor completion: signals only event */ + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, event, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + ok(!is_signaled(event), "event is signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(!is_signaled(client), "client is signaled\n"); + ok(is_signaled(event), "event is not signaled\n"); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == sizeof(buf), "Information = %Iu\n", io.Information); + + /* APC with no event: handle is signaled */ + ioapc_called = FALSE; + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, NULL, ioapc, &io, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(client), "client is signaled\n"); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == sizeof(buf), "Information = %Iu\n", io.Information); + + ok(!ioapc_called, "ioapc called\n"); + SleepEx(0, TRUE); + ok(ioapc_called, "ioapc not called\n"); + + /* completion with no completion port: handle signaled */ + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, NULL, NULL, &io, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(client), "client is not signaled\n"); + + port = CreateIoCompletionPort(client, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %lu\n", GetLastError()); + + /* skipping completion on success: handle is signaled */ + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(is_signaled(client), "client is not signaled\n"); + + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, NULL, NULL, &io, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(client, buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(client), "client is not signaled\n"); + + /* skipping set event on handle: handle is never signaled */ + info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + status = pNtSetInformationFile(client, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(!is_signaled(client), "client is not signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(!is_signaled(client), "client is signaled\n"); + test_queued_completion(port, &io, STATUS_SUCCESS, sizeof(buf)); + + memset(&io, 0xcc, sizeof(io)); + status = NtReadFile(client, NULL, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_PENDING, "status = %lx\n", status); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(client, buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(!is_signaled(client), "client is signaled\n"); + + ret = WriteFile(pipe, buf, sizeof(buf), &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(!is_signaled(client), "client is signaled\n"); + + CloseHandle(port); + CloseHandle(client); + CloseHandle(pipe); +} + +struct blocking_thread_args +{ + HANDLE wait; + HANDLE done; + enum { + BLOCKING_THREAD_WRITE, + BLOCKING_THREAD_READ, + BLOCKING_THREAD_QUIT + } cmd; + HANDLE client; + HANDLE pipe; + HANDLE event; +}; + +static DWORD WINAPI blocking_thread(void *arg) +{ + struct blocking_thread_args *ctx = arg; + static const char buf[] = "testdata"; + char read_buf[32]; + DWORD res, num_bytes; + BOOL ret; + + for (;;) + { + res = WaitForSingleObject(ctx->wait, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + if (res != WAIT_OBJECT_0) break; + switch(ctx->cmd) { + case BLOCKING_THREAD_WRITE: + Sleep(100); + if(ctx->event) + ok(!is_signaled(ctx->event), "event is signaled\n"); + ok(!ioapc_called, "ioapc called\n"); + ok(!is_signaled(ctx->client), "client is signaled\n"); + ok(is_signaled(ctx->pipe), "pipe is not signaled\n"); + ret = WriteFile(ctx->pipe, buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(ctx->pipe), "pipe is not signaled\n"); + break; + case BLOCKING_THREAD_READ: + Sleep(100); + if(ctx->event) + ok(!is_signaled(ctx->event), "event is signaled\n"); + ok(!ioapc_called, "ioapc called\n"); + ok(!is_signaled(ctx->client), "client is signaled\n"); + ok(is_signaled(ctx->pipe), "pipe is not signaled\n"); + ret = ReadFile(ctx->pipe, read_buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + ok(is_signaled(ctx->pipe), "pipe is not signaled\n"); + break; + case BLOCKING_THREAD_QUIT: + return 0; + default: + ok(0, "unvalid command\n"); + } + SetEvent(ctx->done); + } + + return 1; +} + +static void test_blocking(ULONG options) +{ + struct blocking_thread_args ctx; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + char read_buf[16]; + HANDLE thread; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD res, num_bytes; + BOOL ret; + + ctx.wait = CreateEventW(NULL, FALSE, FALSE, NULL); + ctx.done = CreateEventW(NULL, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, blocking_thread, &ctx, 0, 0); + ok(thread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %lx\n", GetLastError()); + + status = create_pipe(&ctx.pipe, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + options); + ok(status == STATUS_SUCCESS, "NtCreateNamedPipeFile returned %lx\n", status); + + pRtlInitUnicodeString(&name, testpipe_nt); + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + status = NtCreateFile(&ctx.client, SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE, &attr, &io, + NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, + options, NULL, 0 ); + ok(status == STATUS_SUCCESS, "NtCreateFile returned %lx\n", status); + + ok(is_signaled(ctx.client), "client is not signaled\n"); + ok(is_signaled(ctx.pipe), "pipe is not signaled\n"); + + /* blocking read with no event nor APC */ + ioapc_called = FALSE; + memset(&io, 0xff, sizeof(io)); + ctx.cmd = BLOCKING_THREAD_WRITE; + ctx.event = NULL; + SetEvent(ctx.wait); + status = NtReadFile(ctx.client, NULL, NULL, NULL, &io, read_buf, sizeof(read_buf), NULL, NULL); + ok(status == STATUS_SUCCESS, "status = %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == 1, "Information = %Iu\n", io.Information); + ok(is_signaled(ctx.client), "client is not signaled\n"); + + res = WaitForSingleObject(ctx.done, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + + /* blocking read with event and APC */ + ioapc_called = FALSE; + memset(&io, 0xff, sizeof(io)); + ctx.cmd = BLOCKING_THREAD_WRITE; + ctx.event = CreateEventW(NULL, TRUE, TRUE, NULL); + SetEvent(ctx.wait); + status = NtReadFile(ctx.client, ctx.event, ioapc, &io, &io, read_buf, + sizeof(read_buf), NULL, NULL); + ok(status == STATUS_SUCCESS, "status = %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == 1, "Information = %Iu\n", io.Information); + ok(is_signaled(ctx.event), "event is not signaled\n"); + todo_wine + ok(is_signaled(ctx.client), "client is not signaled\n"); + + if (!(options & FILE_SYNCHRONOUS_IO_ALERT)) + ok(!ioapc_called, "ioapc called\n"); + SleepEx(0, TRUE); /* alertable wait state */ + ok(ioapc_called, "ioapc not called\n"); + + res = WaitForSingleObject(ctx.done, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + ioapc_called = FALSE; + CloseHandle(ctx.event); + ctx.event = NULL; + + /* blocking flush */ + ret = WriteFile(ctx.client, read_buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + + ioapc_called = FALSE; + memset(&io, 0xff, sizeof(io)); + ctx.cmd = BLOCKING_THREAD_READ; + SetEvent(ctx.wait); + status = NtFlushBuffersFile(ctx.client, &io); + ok(status == STATUS_SUCCESS, "status = %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == 0, "Information = %Iu\n", io.Information); + ok(is_signaled(ctx.client), "client is not signaled\n"); + + res = WaitForSingleObject(ctx.done, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + + CloseHandle(ctx.pipe); + CloseHandle(ctx.client); + + /* flush is blocking even in overlapped mode */ + create_pipe_pair(&ctx.pipe, &ctx.client, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096); + + ok(is_signaled(ctx.client), "client is not signaled\n"); + + ret = WriteFile(ctx.client, read_buf, 1, &num_bytes, NULL); + ok(ret, "WriteFile failed, error %lu\n", GetLastError()); + + ok(is_signaled(ctx.client), "client is not signaled\n"); + + ioapc_called = FALSE; + memset(&io, 0xff, sizeof(io)); + ctx.cmd = BLOCKING_THREAD_READ; + SetEvent(ctx.wait); + status = NtFlushBuffersFile(ctx.client, &io); + ok(status == STATUS_SUCCESS, "status = %lx\n", status); + ok(io.Status == STATUS_SUCCESS, "Status = %lx\n", io.Status); + ok(io.Information == 0, "Information = %Iu\n", io.Information); + /* client signaling is inconsistent in this case */ + + res = WaitForSingleObject(ctx.done, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + + CloseHandle(ctx.pipe); + CloseHandle(ctx.client); + + ctx.cmd = BLOCKING_THREAD_QUIT; + SetEvent(ctx.wait); + res = WaitForSingleObject(thread, 10000); + ok(res == WAIT_OBJECT_0, "wait returned %lx\n", res); + + CloseHandle(ctx.wait); + CloseHandle(ctx.done); + CloseHandle(thread); +} + static void test_volume_info(void) { FILE_FS_DEVICE_INFORMATION *device_info; @@ -1188,37 +1899,45 @@ static void test_volume_info(void) PIPE_TYPE_MESSAGE, 4096 )) return; memset( buffer, 0xaa, sizeof(buffer) ); + memset( &iosb, 0xaa, sizeof(iosb) ); status = pNtQueryVolumeInformationFile( read, &iosb, buffer, sizeof(buffer), FileFsDeviceInformation ); - ok( status == STATUS_SUCCESS, "NtQueryVolumeInformationFile failed: %x\n", status ); - ok( iosb.Information == sizeof(*device_info), "Information = %lu\n", iosb.Information ); + ok( status == STATUS_SUCCESS, "NtQueryVolumeInformationFile failed: %lx\n", status ); + ok( iosb.Status == STATUS_SUCCESS, "got status %#lx\n", iosb.Status ); + ok( iosb.Information == sizeof(*device_info), "Information = %Iu\n", iosb.Information ); device_info = (FILE_FS_DEVICE_INFORMATION*)buffer; - ok( device_info->DeviceType == FILE_DEVICE_NAMED_PIPE, "DeviceType = %u\n", device_info->DeviceType ); + ok( device_info->DeviceType == FILE_DEVICE_NAMED_PIPE, "DeviceType = %lu\n", device_info->DeviceType ); ok( !(device_info->Characteristics & ~FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL), - "Characteristics = %x\n", device_info->Characteristics ); + "Characteristics = %lx\n", device_info->Characteristics ); memset( buffer, 0xaa, sizeof(buffer) ); + memset( &iosb, 0xaa, sizeof(iosb) ); status = pNtQueryVolumeInformationFile( write, &iosb, buffer, sizeof(buffer), FileFsDeviceInformation ); - ok( status == STATUS_SUCCESS, "NtQueryVolumeInformationFile failed: %x\n", status ); - ok( iosb.Information == sizeof(*device_info), "Information = %lu\n", iosb.Information ); + ok( status == STATUS_SUCCESS, "NtQueryVolumeInformationFile failed: %lx\n", status ); + ok( iosb.Status == STATUS_SUCCESS, "got status %#lx\n", iosb.Status ); + ok( iosb.Information == sizeof(*device_info), "Information = %Iu\n", iosb.Information ); device_info = (FILE_FS_DEVICE_INFORMATION*)buffer; - ok( device_info->DeviceType == FILE_DEVICE_NAMED_PIPE, "DeviceType = %u\n", device_info->DeviceType ); + ok( device_info->DeviceType == FILE_DEVICE_NAMED_PIPE, "DeviceType = %lu\n", device_info->DeviceType ); ok( !(device_info->Characteristics & ~FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL), - "Characteristics = %x\n", device_info->Characteristics ); + "Characteristics = %lx\n", device_info->Characteristics ); CloseHandle( read ); CloseHandle( write ); } -#define test_file_name_fail(a,b) _test_file_name_fail(__LINE__,a,b) -static void _test_file_name_fail(unsigned line, HANDLE pipe, NTSTATUS expected_status) +#define test_file_name_fail(a,b,c) _test_file_name_fail(__LINE__,a,b,c) +static void _test_file_name_fail(unsigned line, HANDLE pipe, NTSTATUS expected_status, BOOL todo) { char buffer[512]; IO_STATUS_BLOCK iosb; NTSTATUS status; + status = NtQueryInformationFile( pipe, &iosb, buffer, 0, FileNameInformation ); + ok_(__FILE__,line)( status == STATUS_INFO_LENGTH_MISMATCH, + "expected STATUS_INFO_LENGTH_MISMATCH, got %#lx\n", status ); + status = NtQueryInformationFile( pipe, &iosb, buffer, sizeof(buffer), FileNameInformation ); - ok_(__FILE__,line)( status == expected_status, "NtQueryInformationFile failed: %x, expected %x\n", - status, expected_status ); + todo_wine_if (todo) + ok_(__FILE__,line)( status == expected_status, "expected %#lx, got %#lx\n", expected_status, status ); } #define test_file_name(a) _test_file_name(__LINE__,a) @@ -1235,33 +1954,494 @@ static void _test_file_name(unsigned line, HANDLE pipe) memset( buffer, 0xaa, sizeof(buffer) ); memset( &iosb, 0xaa, sizeof(iosb) ); status = NtQueryInformationFile( pipe, &iosb, buffer, sizeof(buffer), FileNameInformation ); - ok_(__FILE__,line)( status == STATUS_SUCCESS, "NtQueryInformationFile failed: %x\n", status ); - ok_(__FILE__,line)( iosb.Status == STATUS_SUCCESS, "Status = %x\n", iosb.Status ); + ok_(__FILE__,line)( status == STATUS_SUCCESS, "NtQueryInformationFile failed: %lx\n", status ); + ok_(__FILE__,line)( iosb.Status == STATUS_SUCCESS, "Status = %lx\n", iosb.Status ); ok_(__FILE__,line)( iosb.Information == sizeof(name_info->FileNameLength) + sizeof(nameW), - "Information = %lu\n", iosb.Information ); - ok( name_info->FileNameLength == sizeof(nameW), "FileNameLength = %u\n", name_info->FileNameLength ); + "Information = %Iu\n", iosb.Information ); + ok( name_info->FileNameLength == sizeof(nameW), "FileNameLength = %lu\n", name_info->FileNameLength ); ok( !memcmp(name_info->FileName, nameW, sizeof(nameW)), "FileName = %s\n", wine_dbgstr_w(name_info->FileName) ); /* too small buffer */ memset( buffer, 0xaa, sizeof(buffer) ); memset( &iosb, 0xaa, sizeof(iosb) ); status = NtQueryInformationFile( pipe, &iosb, buffer, 20, FileNameInformation ); - ok( status == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile failed: %x\n", status ); - ok( iosb.Status == STATUS_BUFFER_OVERFLOW, "Status = %x\n", iosb.Status ); - ok( iosb.Information == 20, "Information = %lu\n", iosb.Information ); - ok( name_info->FileNameLength == sizeof(nameW), "FileNameLength = %u\n", name_info->FileNameLength ); + ok( status == STATUS_BUFFER_OVERFLOW, "NtQueryInformationFile failed: %lx\n", status ); + ok( iosb.Status == STATUS_BUFFER_OVERFLOW, "Status = %lx\n", iosb.Status ); + ok( iosb.Information == 20, "Information = %Iu\n", iosb.Information ); + ok( name_info->FileNameLength == sizeof(nameW), "FileNameLength = %lu\n", name_info->FileNameLength ); ok( !memcmp(name_info->FileName, nameW, 16), "FileName = %s\n", wine_dbgstr_w(name_info->FileName) ); /* too small buffer */ memset( buffer, 0xaa, sizeof(buffer) ); memset( &iosb, 0xaa, sizeof(iosb) ); status = NtQueryInformationFile( pipe, &iosb, buffer, 4, FileNameInformation ); - ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile failed: %x\n", status ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtQueryInformationFile failed: %lx\n", status ); +} + +static HANDLE create_pipe_server(void) +{ + HANDLE handle; + NTSTATUS status; + + status = create_pipe(&handle, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0); + ok(status == STATUS_SUCCESS, "create_pipe failed: %lx\n", status); + return handle; +} + +static HANDLE connect_pipe(HANDLE server) +{ + HANDLE client; + + client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "can't open pipe: %lu\n", GetLastError()); + + return client; +} + +static HANDLE connect_and_write_pipe(HANDLE server) +{ + BYTE buf[10] = {0}; + HANDLE client; + DWORD written; + BOOL res; + + client = connect_pipe(server); + + res = WriteFile(client, buf, sizeof(buf), &written, NULL); + ok(res, "WriteFile failed: %lu\n", GetLastError()); + res = WriteFile(server, buf, sizeof(buf), &written, NULL); + ok(res, "WriteFile failed: %lu\n", GetLastError()); + + return client; +} + +static void test_pipe_state(HANDLE pipe, BOOL is_server, DWORD state) +{ + FILE_PIPE_PEEK_BUFFER peek_buf; + IO_STATUS_BLOCK io; + static char buf[] = "test"; + NTSTATUS status, expected_status; + + memset(&peek_buf, 0xcc, sizeof(peek_buf)); + memset(&io, 0xcc, sizeof(io)); + status = NtFsControlFile(pipe, NULL, NULL, NULL, &io, FSCTL_PIPE_PEEK, NULL, 0, &peek_buf, sizeof(peek_buf)); + if (!status || status == STATUS_PENDING) + status = io.Status; + switch (state) + { + case FILE_PIPE_DISCONNECTED_STATE: + expected_status = is_server ? STATUS_INVALID_PIPE_STATE : STATUS_PIPE_DISCONNECTED; + break; + case FILE_PIPE_LISTENING_STATE: + expected_status = STATUS_INVALID_PIPE_STATE; + break; + case FILE_PIPE_CONNECTED_STATE: + expected_status = STATUS_SUCCESS; + break; + default: + expected_status = STATUS_PIPE_BROKEN; + break; + } + ok(status == expected_status, "status = %lx, expected %lx in %s state %lu\n", + status, expected_status, is_server ? "server" : "client", state); + if (!status) + ok(peek_buf.NamedPipeState == state, "NamedPipeState = %lu, expected %lu\n", + peek_buf.NamedPipeState, state); + + if (state != FILE_PIPE_CONNECTED_STATE) + { + if (state == FILE_PIPE_CLOSING_STATE) + expected_status = STATUS_INVALID_PIPE_STATE; + status = NtFsControlFile(pipe, NULL, NULL, NULL, &io, FSCTL_PIPE_TRANSCEIVE, + buf, 1, buf+1, 1); + if (!status || status == STATUS_PENDING) + status = io.Status; + ok(status == expected_status, + "NtFsControlFile(FSCTL_PIPE_TRANSCEIVE) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + } + + memset(&io, 0xcc, sizeof(io)); + status = NtFlushBuffersFile(pipe, &io); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + { + ok(status == STATUS_PIPE_DISCONNECTED, "status = %lx in %s state %lu\n", + status, is_server ? "server" : "client", state); + } + else + { + ok(status == STATUS_SUCCESS, "status = %lx in %s state %lu\n", + status, is_server ? "server" : "client", state); + ok(io.Status == status, "io.Status = %lx\n", io.Status); + ok(!io.Information, "io.Information = %Ix\n", io.Information); + } + + if (state != FILE_PIPE_CONNECTED_STATE) + { + switch (state) + { + case FILE_PIPE_DISCONNECTED_STATE: + expected_status = STATUS_PIPE_DISCONNECTED; + break; + case FILE_PIPE_LISTENING_STATE: + expected_status = STATUS_PIPE_LISTENING; + break; + default: + expected_status = STATUS_PIPE_BROKEN; + break; + } + status = NtReadFile(pipe, NULL, NULL, NULL, &io, buf, 1, NULL, NULL); + ok(status == expected_status, "NtReadFile failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + } + + if (is_server && (state == FILE_PIPE_CLOSING_STATE || state == FILE_PIPE_CONNECTED_STATE)) + { + memset(&io, 0xcc, sizeof(io)); + status = listen_pipe(pipe, NULL, &io, FALSE); + ok(status == (state == FILE_PIPE_CLOSING_STATE ? STATUS_PIPE_CLOSING : STATUS_PIPE_CONNECTED), + "status = %lx in %lu state\n", status, state); + } +} + +static void test_pipe_with_data_state(HANDLE pipe, BOOL is_server, DWORD state) +{ + FILE_PIPE_LOCAL_INFORMATION local_info; + FILE_PIPE_INFORMATION pipe_info; + FILE_PIPE_PEEK_BUFFER peek_buf; + IO_STATUS_BLOCK io; + char buf[256] = "test"; + NTSTATUS status, expected_status; + FILE_STANDARD_INFORMATION std_info; + + memset(&io, 0xcc, sizeof(io)); + status = pNtQueryInformationFile(pipe, &io, &local_info, sizeof(local_info), FilePipeLocalInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + if (!status) + { + ok(local_info.NamedPipeState == state, "%s NamedPipeState = %lu, expected %lu\n", + is_server ? "server" : "client", local_info.NamedPipeState, state); + if (state != FILE_PIPE_DISCONNECTED_STATE && state != FILE_PIPE_LISTENING_STATE) + ok(local_info.ReadDataAvailable != 0, "ReadDataAvailable, expected non-zero, in %s state %lu\n", + is_server ? "server" : "client", state); + else + ok(local_info.ReadDataAvailable == 0, "ReadDataAvailable, expected zero, in %s state %lu\n", + is_server ? "server" : "client", state); + } + + status = pNtQueryInformationFile(pipe, &io, &std_info, sizeof(std_info), FileStandardInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FileStandardInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FileStandardInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + if (!status) + { + ok(std_info.AllocationSize.QuadPart == local_info.InboundQuota + local_info.OutboundQuota, + "got %I64u, expected %lu.\n", + std_info.AllocationSize.QuadPart, local_info.InboundQuota + local_info.OutboundQuota); + ok(std_info.EndOfFile.QuadPart == local_info.ReadDataAvailable, "got %I64u.\n", std_info.EndOfFile.QuadPart); + ok(std_info.NumberOfLinks == 1, "got %lu.\n", std_info.NumberOfLinks); + todo_wine ok(std_info.DeletePending, "got %d.\n", std_info.DeletePending); + ok(!std_info.Directory, "got %d.\n", std_info.Directory); + } + + status = pNtQueryInformationFile(pipe, &io, &pipe_info, sizeof(pipe_info), FilePipeInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FilePipeInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + + status = NtQueryInformationFile(pipe, &io, buf, sizeof(buf), FileNameInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FileNameInformation) failed: %lx\n", status); + else + todo_wine_if(!is_server && state == FILE_PIPE_CLOSING_STATE) + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FileNameInformation) failed: %lx\n", status); + + memset(&peek_buf, 0xcc, sizeof(peek_buf)); + memset(&io, 0xcc, sizeof(io)); + status = NtFsControlFile(pipe, NULL, NULL, NULL, &io, FSCTL_PIPE_PEEK, NULL, 0, &peek_buf, sizeof(peek_buf)); + if (!status || status == STATUS_PENDING) + status = io.Status; + switch (state) + { + case FILE_PIPE_DISCONNECTED_STATE: + expected_status = is_server ? STATUS_INVALID_PIPE_STATE : STATUS_PIPE_DISCONNECTED; + break; + case FILE_PIPE_LISTENING_STATE: + expected_status = STATUS_INVALID_PIPE_STATE; + break; + default: + expected_status = STATUS_BUFFER_OVERFLOW; + break; + } + ok(status == expected_status, "status = %lx, expected %lx in %s state %lu\n", + status, expected_status, is_server ? "server" : "client", state); + if (status == STATUS_BUFFER_OVERFLOW) + ok(peek_buf.NamedPipeState == state, "NamedPipeState = %lu, expected %lu\n", + peek_buf.NamedPipeState, state); + + switch (state) + { + case FILE_PIPE_DISCONNECTED_STATE: + expected_status = STATUS_PIPE_DISCONNECTED; + break; + case FILE_PIPE_LISTENING_STATE: + expected_status = STATUS_PIPE_LISTENING; + break; + case FILE_PIPE_CONNECTED_STATE: + expected_status = STATUS_SUCCESS; + break; + default: + expected_status = STATUS_PIPE_CLOSING; + break; + } + status = NtWriteFile(pipe, NULL, NULL, NULL, &io, buf, 1, NULL, NULL); + ok(status == expected_status, "NtWriteFile failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + + if (state == FILE_PIPE_CLOSING_STATE) + expected_status = STATUS_SUCCESS; + status = NtReadFile(pipe, NULL, NULL, NULL, &io, buf, 1, NULL, NULL); + ok(status == expected_status, "NtReadFile failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); +} + +static void pipe_for_each_state(HANDLE (*create_server)(void), + HANDLE (*connect_client)(HANDLE), + void (*test)(HANDLE pipe, BOOL is_server, DWORD pipe_state)) +{ + HANDLE client, server; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + HANDLE event; + BOOL ret; + + event = CreateEventW(NULL, TRUE, FALSE, NULL); + + server = create_server(); + test(server, TRUE, FILE_PIPE_LISTENING_STATE); + + status = listen_pipe(server, event, &iosb, FALSE); + ok(status == STATUS_PENDING, "listen_pipe returned %lx\n", status); + test(server, TRUE, FILE_PIPE_LISTENING_STATE); + + client = connect_client(server); + test(server, TRUE, FILE_PIPE_CONNECTED_STATE); + test(client, FALSE, FILE_PIPE_CONNECTED_STATE); + + /* server closed, but not disconnected */ + CloseHandle(server); + test(client, FALSE, FILE_PIPE_CLOSING_STATE); + CloseHandle(client); + + server = create_server(); + status = listen_pipe(server, event, &iosb, FALSE); + ok(status == STATUS_PENDING, "listen_pipe returned %lx\n", status); + + client = connect_client(server); + ret = DisconnectNamedPipe(server); + ok(ret, "DisconnectNamedPipe failed: %lu\n", GetLastError()); + test(server, TRUE, FILE_PIPE_DISCONNECTED_STATE); + test(client, FALSE, FILE_PIPE_DISCONNECTED_STATE); + CloseHandle(server); + test(client, FALSE, FILE_PIPE_DISCONNECTED_STATE); + CloseHandle(client); + + server = create_server(); + status = listen_pipe(server, event, &iosb, FALSE); + ok(status == STATUS_PENDING, "listen_pipe returned %lx\n", status); + + client = connect_client(server); + CloseHandle(client); + test(server, TRUE, FILE_PIPE_CLOSING_STATE); + ret = DisconnectNamedPipe(server); + ok(ret, "DisconnectNamedPipe failed: %lu\n", GetLastError()); + test(server, TRUE, FILE_PIPE_DISCONNECTED_STATE); + + status = listen_pipe(server, event, &iosb, FALSE); + ok(status == STATUS_PENDING, "listen_pipe returned %lx\n", status); + client = connect_client(server); + test(server, TRUE, FILE_PIPE_CONNECTED_STATE); + test(client, FALSE, FILE_PIPE_CONNECTED_STATE); + CloseHandle(client); + CloseHandle(server); + + CloseHandle(event); +} + +static HANDLE create_local_info_test_pipe(void) +{ + IO_STATUS_BLOCK iosb; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + LARGE_INTEGER timeout; + HANDLE pipe; + NTSTATUS status; + + pRtlInitUnicodeString(&name, testpipe_nt); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + timeout.QuadPart = -100000000; + + status = pNtCreateNamedPipeFile(&pipe, FILE_READ_ATTRIBUTES | SYNCHRONIZE | GENERIC_WRITE, + &attr, &iosb, FILE_SHARE_READ, FILE_CREATE, 0, 1, 0, 0, 1, + 100, 200, &timeout); + ok(status == STATUS_SUCCESS, "NtCreateNamedPipeFile failed: %lx\n", status); + + return pipe; +} + +static HANDLE connect_pipe_reader(HANDLE server) +{ + HANDLE client; + + client = CreateFileW(testpipe, GENERIC_READ | FILE_WRITE_ATTRIBUTES, 0, 0, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "can't open pipe: %lu\n", GetLastError()); + + return client; +} + +static void test_pipe_local_info(HANDLE pipe, BOOL is_server, DWORD state) +{ + FILE_PIPE_LOCAL_INFORMATION local_info; + FILE_PIPE_INFORMATION pipe_info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + LARGE_INTEGER timeout; + HANDLE new_pipe; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + memset(&iosb, 0xcc, sizeof(iosb)); + memset(&local_info, 0xcc, sizeof(local_info)); + status = pNtQueryInformationFile(pipe, &iosb, &local_info, sizeof(local_info), FilePipeLocalInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + if (!status) + { + ok(local_info.NamedPipeType == 1, "NamedPipeType = %lu\n", local_info.NamedPipeType); + ok(local_info.NamedPipeConfiguration == 1, "NamedPipeConfiguration = %lu\n", + local_info.NamedPipeConfiguration); + ok(local_info.MaximumInstances == 1, "MaximumInstances = %lu\n", local_info.MaximumInstances); + if (!is_server && state == FILE_PIPE_CLOSING_STATE) + ok(local_info.CurrentInstances == 0 || broken(local_info.CurrentInstances == 1 /* winxp */), + "CurrentInstances = %lu\n", local_info.CurrentInstances); + else + ok(local_info.CurrentInstances == 1, + "CurrentInstances = %lu\n", local_info.CurrentInstances); + ok(local_info.InboundQuota == 100, "InboundQuota = %lu\n", local_info.InboundQuota); + ok(local_info.ReadDataAvailable == 0, "ReadDataAvailable = %lu\n", + local_info.ReadDataAvailable); + ok(local_info.OutboundQuota == 200, "OutboundQuota = %lu\n", local_info.OutboundQuota); + todo_wine + ok(local_info.WriteQuotaAvailable == (is_server ? 200 : 100), "WriteQuotaAvailable = %lu\n", + local_info.WriteQuotaAvailable); + ok(local_info.NamedPipeState == state, "%s NamedPipeState = %lu, expected %lu\n", + is_server ? "server" : "client", local_info.NamedPipeState, state); + ok(local_info.NamedPipeEnd == is_server, "NamedPipeEnd = %lu\n", local_info.NamedPipeEnd); + + /* try to create another, incompatible, instance of pipe */ + pRtlInitUnicodeString(&name, testpipe_nt); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + timeout.QuadPart = -100000000; + + status = pNtCreateNamedPipeFile(&new_pipe, FILE_READ_ATTRIBUTES | SYNCHRONIZE | GENERIC_READ, + &attr, &iosb, FILE_SHARE_WRITE, FILE_CREATE, 0, 0, 0, 0, 1, + 100, 200, &timeout); + if (!local_info.CurrentInstances) + ok(status == STATUS_SUCCESS, "NtCreateNamedPipeFile failed: %lx\n", status); + else + ok(status == STATUS_INSTANCE_NOT_AVAILABLE, "NtCreateNamedPipeFile failed: %lx\n", status); + if (!status) CloseHandle(new_pipe); + + memset(&iosb, 0xcc, sizeof(iosb)); + status = pNtQueryInformationFile(pipe, &iosb, &local_info, sizeof(local_info), + FilePipeLocalInformation); + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + + if (!is_server && state == FILE_PIPE_CLOSING_STATE) + ok(local_info.CurrentInstances == 0 || broken(local_info.CurrentInstances == 1 /* winxp */), + "CurrentInstances = %lu\n", local_info.CurrentInstances); + else + ok(local_info.CurrentInstances == 1, + "CurrentInstances = %lu\n", local_info.CurrentInstances); + } + + memset(&iosb, 0xcc, sizeof(iosb)); + status = pNtQueryInformationFile(pipe, &iosb, &pipe_info, sizeof(pipe_info), FilePipeInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + + if (!status) + { + ok(pipe_info.ReadMode == 0, "ReadMode = %lu\n", pipe_info.ReadMode); + ok(pipe_info.CompletionMode == 0, "CompletionMode = %lu\n", pipe_info.CompletionMode); + } + + pipe_info.ReadMode = 0; + pipe_info.CompletionMode = 0; + memset(&iosb, 0xcc, sizeof(iosb)); + status = pNtSetInformationFile(pipe, &iosb, &pipe_info, sizeof(pipe_info), FilePipeInformation); + if (!is_server && state == FILE_PIPE_DISCONNECTED_STATE) + ok(status == STATUS_PIPE_DISCONNECTED, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); + else + ok(status == STATUS_SUCCESS, + "NtQueryInformationFile(FilePipeLocalInformation) failed in %s state %lu: %lx\n", + is_server ? "server" : "client", state, status); } static void test_file_info(void) { - HANDLE server, client; + HANDLE server, client, device; if (!create_pipe_pair( &server, &client, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE, 4096 )) return; @@ -1270,10 +2450,17 @@ static void test_file_info(void) test_file_name( server ); DisconnectNamedPipe( server ); - test_file_name_fail( client, STATUS_PIPE_DISCONNECTED ); + test_file_name_fail( client, STATUS_PIPE_DISCONNECTED, FALSE ); CloseHandle( server ); CloseHandle( client ); + + device = CreateFileA("\\\\.\\pipe", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + ok(device != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError()); + + test_file_name_fail( device, STATUS_INVALID_PARAMETER, TRUE ); + + CloseHandle( device ); } static PSECURITY_DESCRIPTOR get_security_descriptor(HANDLE handle, BOOL todo) @@ -1286,14 +2473,14 @@ static PSECURITY_DESCRIPTOR get_security_descriptor(HANDLE handle, BOOL todo) NULL, 0, &length); todo_wine_if(todo && status == STATUS_PIPE_DISCONNECTED) ok(status == STATUS_BUFFER_TOO_SMALL, - "Failed to query object security descriptor length: %08x\n", status); + "Failed to query object security descriptor length: %08lx\n", status); if(status != STATUS_BUFFER_TOO_SMALL) return NULL; ok(length != 0, "length = 0\n"); sec_desc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, length); status = NtQuerySecurityObject(handle, GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, sec_desc, length, &length); - ok(status == STATUS_SUCCESS, "Failed to query object security descriptor: %08x\n", status); + ok(status == STATUS_SUCCESS, "Failed to query object security descriptor: %08lx\n", status); return sec_desc; } @@ -1306,16 +2493,16 @@ static TOKEN_OWNER *get_current_owner(void) BOOL ret; ret = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); - ok(ret, "Failed to get process token: %u\n", GetLastError()); + ok(ret, "Failed to get process token: %lu\n", GetLastError()); ret = GetTokenInformation(token, TokenOwner, NULL, 0, &length); ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, - "GetTokenInformation failed: %u\n", GetLastError()); - ok(length != 0, "Failed to get token owner information length: %u\n", GetLastError()); + "GetTokenInformation failed: %lu\n", GetLastError()); + ok(length != 0, "Failed to get token owner information length: %lu\n", GetLastError()); owner = HeapAlloc(GetProcessHeap(), 0, length); ret = GetTokenInformation(token, TokenOwner, owner, length, &length); - ok(ret, "Failed to get token owner information: %u)\n", GetLastError()); + ok(ret, "Failed to get token owner information: %lu)\n", GetLastError()); CloseHandle(token); return owner; @@ -1329,16 +2516,16 @@ static TOKEN_PRIMARY_GROUP *get_current_group(void) BOOL ret; ret = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token); - ok(ret, "Failed to get process token: %u\n", GetLastError()); + ok(ret, "Failed to get process token: %lu\n", GetLastError()); ret = GetTokenInformation(token, TokenPrimaryGroup, NULL, 0, &length); ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, - "GetTokenInformation failed: %u\n", GetLastError()); - ok(length != 0, "Failed to get primary group token information length: %u\n", GetLastError()); + "GetTokenInformation failed: %lu\n", GetLastError()); + ok(length != 0, "Failed to get primary group token information length: %lu\n", GetLastError()); group = HeapAlloc(GetProcessHeap(), 0, length); ret = GetTokenInformation(token, TokenPrimaryGroup, group, length, &length); - ok(ret, "Failed to get primary group token information: %u\n", GetLastError()); + ok(ret, "Failed to get primary group token information: %lu\n", GetLastError()); CloseHandle(token); return group; @@ -1352,7 +2539,7 @@ static SID *well_known_sid(WELL_KNOWN_SID_TYPE sid_type) sid = HeapAlloc(GetProcessHeap(), 0, size); ret = CreateWellKnownSid(sid_type, NULL, sid, &size); - ok(ret, "CreateWellKnownSid failed: %u\n", GetLastError()); + ok(ret, "CreateWellKnownSid failed: %lu\n", GetLastError()); return sid; } @@ -1369,7 +2556,7 @@ static void _test_group(unsigned line, HANDLE handle, SID *expected_sid, BOOL to status = RtlGetGroupSecurityDescriptor(sec_desc, &group_sid, &defaulted); ok_(__FILE__,line)(status == STATUS_SUCCESS, - "Failed to query group from security descriptor: %08x\n", status); + "Failed to query group from security descriptor: %08lx\n", status); todo_wine_if(todo) ok_(__FILE__,line)(EqualSid(group_sid, expected_sid), "SIDs are not equal\n"); @@ -1406,10 +2593,10 @@ static void test_security_info(void) server = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX | WRITE_OWNER, PIPE_TYPE_BYTE, 10, 0x20000, 0x20000, 0, NULL); - ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError()); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError()); client = CreateFileA(PIPENAME, GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL); - ok(client != INVALID_HANDLE_VALUE, "CreateFile failed: %u\n", GetLastError()); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError()); test_group(server, process_group->PrimaryGroup, TRUE); test_group(client, process_group->PrimaryGroup, TRUE); @@ -1418,7 +2605,7 @@ static void test_security_info(void) ret = SetSecurityDescriptorGroup(sec_desc, world_sid, FALSE); ok(ret, "SetSecurityDescriptorGroup failed\n"); status = NtSetSecurityObject(server, GROUP_SECURITY_INFORMATION, sec_desc); - ok(status == STATUS_SUCCESS, "NtSetSecurityObject failed: %08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetSecurityObject failed: %08lx\n", status); test_group(server, world_sid, FALSE); test_group(client, world_sid, FALSE); @@ -1426,14 +2613,14 @@ static void test_security_info(void) /* new instance of pipe server has the same security descriptor */ server2 = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, 10, 0x20000, 0x20000, 0, NULL); - ok(server2 != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError()); + ok(server2 != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError()); test_group(server2, world_sid, FALSE); /* set client group, server changes as well */ ret = SetSecurityDescriptorGroup(sec_desc, local_sid, FALSE); ok(ret, "SetSecurityDescriptorGroup failed\n"); status = NtSetSecurityObject(server, GROUP_SECURITY_INFORMATION, sec_desc); - ok(status == STATUS_SUCCESS, "NtSetSecurityObject failed: %08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetSecurityObject failed: %08lx\n", status); test_group(server, local_sid, FALSE); test_group(client, local_sid, FALSE); @@ -1441,25 +2628,25 @@ static void test_security_info(void) CloseHandle(server); /* SD is preserved after closing server object */ - test_group(client, local_sid, TRUE); + test_group(client, local_sid, FALSE); CloseHandle(client); server = server2; client = CreateFileA(PIPENAME, GENERIC_ALL, 0, NULL, OPEN_EXISTING, 0, NULL); - ok(client != INVALID_HANDLE_VALUE, "CreateFile failed: %u\n", GetLastError()); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError()); test_group(client, local_sid, FALSE); ret = DisconnectNamedPipe(server); - ok(ret, "DisconnectNamedPipe failed: %u\n", GetLastError()); + ok(ret, "DisconnectNamedPipe failed: %lu\n", GetLastError()); /* disconnected server may be queried for security info, but client does not */ test_group(server, local_sid, FALSE); status = NtQuerySecurityObject(client, GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, NULL, 0, &length); - ok(status == STATUS_PIPE_DISCONNECTED, "NtQuerySecurityObject returned %08x\n", status); + ok(status == STATUS_PIPE_DISCONNECTED, "NtQuerySecurityObject returned %08lx\n", status); status = NtSetSecurityObject(client, GROUP_SECURITY_INFORMATION, sec_desc); - ok(status == STATUS_PIPE_DISCONNECTED, "NtQuerySecurityObject returned %08x\n", status); + ok(status == STATUS_PIPE_DISCONNECTED, "NtQuerySecurityObject returned %08lx\n", status); /* attempting to create another pipe instance with specified sd fails */ sec_attr.nLength = sizeof(sec_attr); @@ -1471,7 +2658,7 @@ static void test_security_info(void) 0x20000, 0x20000, 0, &sec_attr); todo_wine ok(server2 == INVALID_HANDLE_VALUE && GetLastError() == ERROR_ACCESS_DENIED, - "CreateNamedPipe failed: %u\n", GetLastError()); + "CreateNamedPipe failed: %lu\n", GetLastError()); if (server2 != INVALID_HANDLE_VALUE) CloseHandle(server2); CloseHandle(server); @@ -1479,7 +2666,7 @@ static void test_security_info(void) server = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX | WRITE_OWNER, PIPE_TYPE_BYTE, 10, 0x20000, 0x20000, 0, &sec_attr); - ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %u\n", GetLastError()); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError()); test_group(server, local_sid, FALSE); CloseHandle(server); @@ -1489,11 +2676,505 @@ static void test_security_info(void) HeapFree(GetProcessHeap(), 0, local_sid); } +static void subtest_empty_name_pipe_operations(HANDLE handle) +{ + static const struct fsctl_test { + const char *name; + ULONG code; + NTSTATUS status; + NTSTATUS status_broken; + } fsctl_tests[] = { +#define FSCTL_TEST(code, ...) { #code, code, __VA_ARGS__ } + FSCTL_TEST(FSCTL_PIPE_ASSIGN_EVENT, STATUS_NOT_SUPPORTED), + FSCTL_TEST(FSCTL_PIPE_DISCONNECT, STATUS_PIPE_DISCONNECTED), + FSCTL_TEST(FSCTL_PIPE_LISTEN, STATUS_ILLEGAL_FUNCTION), + FSCTL_TEST(FSCTL_PIPE_QUERY_EVENT, STATUS_NOT_SUPPORTED), + FSCTL_TEST(FSCTL_PIPE_TRANSCEIVE, STATUS_PIPE_DISCONNECTED), + FSCTL_TEST(FSCTL_PIPE_IMPERSONATE, STATUS_ILLEGAL_FUNCTION), + FSCTL_TEST(FSCTL_PIPE_SET_CLIENT_PROCESS, STATUS_NOT_SUPPORTED), + FSCTL_TEST(FSCTL_PIPE_QUERY_CLIENT_PROCESS, STATUS_INVALID_PARAMETER, /* win10 1507 */ STATUS_PIPE_DISCONNECTED), +#undef FSCTL_TEST + }; + FILE_PIPE_PEEK_BUFFER peek_buf; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + char buffer[1024]; + NTSTATUS status; + ULONG peer_pid; + HANDLE event; + size_t i; + + event = NULL; + InitializeObjectAttributes(&attr, NULL, 0, 0, NULL); + status = NtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, FALSE); + ok(status == STATUS_SUCCESS, "NtCreateEvent returned %#lx\n", status); + + status = NtReadFile(handle, event, NULL, NULL, &io, buffer, sizeof(buffer), NULL, NULL); + todo_wine + ok(status == STATUS_INVALID_PARAMETER, "NtReadFile on \\Device\\NamedPipe: got %#lx\n", status); + + status = NtWriteFile(handle, event, NULL, NULL, &io, buffer, sizeof(buffer), NULL, NULL); + todo_wine + ok(status == STATUS_INVALID_PARAMETER, "NtWriteFile on \\Device\\NamedPipe: got %#lx\n", status); + + status = NtFsControlFile(handle, event, NULL, NULL, &io, FSCTL_PIPE_PEEK, NULL, 0, &peek_buf, sizeof(peek_buf)); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = io.Status; + } + todo_wine + ok(status == STATUS_INVALID_PARAMETER, "FSCTL_PIPE_PEEK on \\Device\\NamedPipe: got %lx\n", status); + + status = NtFsControlFile(handle, event, NULL, NULL, &io, FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE, (void *)"ClientProcessId", sizeof("ClientProcessId"), &peer_pid, sizeof(peer_pid)); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = io.Status; + } + todo_wine + ok(status == STATUS_INVALID_PARAMETER, "FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE ClientProcessId on \\Device\\NamedPipe: got %lx\n", status); + + status = NtFsControlFile(handle, event, NULL, NULL, &io, FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE, (void *)"ServerProcessId", sizeof("ServerProcessId"), &peer_pid, sizeof(peer_pid)); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = io.Status; + } + todo_wine + ok(status == STATUS_INVALID_PARAMETER, "FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE ServerProcessId on \\Device\\NamedPipe: got %lx\n", status); + + for (i = 0; i < ARRAY_SIZE(fsctl_tests); i++) + { + const struct fsctl_test *ft = &fsctl_tests[i]; + + status = NtFsControlFile(handle, event, NULL, NULL, &io, ft->code, 0, 0, 0, 0); + if (status == STATUS_PENDING) + { + WaitForSingleObject(event, INFINITE); + status = io.Status; + } + ok(status == ft->status || (ft->status_broken && broken(status == ft->status_broken)), + "NtFsControlFile(%s) on \\Device\\NamedPipe: expected %#lx, got %#lx\n", + ft->name, ft->status, status); + } + + NtClose(event); +} + +static void test_empty_name(void) +{ + static const LARGE_INTEGER zero_timeout = {{ 0 }}; + HANDLE hdirectory, hpipe, hpipe2, hwrite, hwrite2, handle; + OBJECT_TYPE_INFORMATION *type_info; + OBJECT_NAME_INFORMATION *name_info; + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER timeout; + UNICODE_STRING name; + IO_STATUS_BLOCK io; + DWORD data, length; + char buffer[1024]; + NTSTATUS status; + BOOL ret; + + type_info = (OBJECT_TYPE_INFORMATION *)buffer; + name_info = (OBJECT_NAME_INFORMATION *)buffer; + + hpipe = hwrite = NULL; + + attr.Length = sizeof(attr); + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + pRtlInitUnicodeString(&name, L"\\Device\\NamedPipe"); + attr.RootDirectory = 0; + attr.ObjectName = &name; + + status = NtCreateFile(&hdirectory, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok(!status, "Got unexpected status %#lx.\n", status); + + pRtlInitUnicodeString(&name, L"nonexistent_pipe"); + status = wait_pipe(hdirectory, &name, &zero_timeout); + ok(status == STATUS_ILLEGAL_FUNCTION, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe: %#lx\n", status); + + subtest_empty_name_pipe_operations(hdirectory); + + name.Buffer = NULL; + name.Length = 0; + name.MaximumLength = 0; + attr.RootDirectory = hdirectory; + + timeout.QuadPart = -(LONG64)10000000; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, + &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, + 0, 0, 0, 3, 4096, 4096, &timeout); + todo_wine ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status); + if (!status) + CloseHandle(hpipe); + + pRtlInitUnicodeString(&name, L"test3\\pipe"); + attr.RootDirectory = hdirectory; + attr.ObjectName = &name; + timeout.QuadPart = -(LONG64)10000000; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout); + ok(status == STATUS_OBJECT_NAME_INVALID, "unexpected status from NtCreateNamedPipeFile: %#lx\n", status); + if (!status) + CloseHandle(hpipe); + + CloseHandle(hdirectory); + + pRtlInitUnicodeString(&name, L"\\Device\\NamedPipe\\"); + attr.RootDirectory = 0; + attr.ObjectName = &name; + + status = pNtCreateDirectoryObject(&hdirectory, GENERIC_READ | SYNCHRONIZE, &attr); + todo_wine ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + status = NtCreateFile(&hdirectory, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0 ); + ok(!status, "Got unexpected status %#lx.\n", status); + + pRtlInitUnicodeString(&name, L"nonexistent_pipe"); + status = wait_pipe(hdirectory, &name, &zero_timeout); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "unexpected status for FSCTL_PIPE_WAIT on \\Device\\NamedPipe\\: %#lx\n", status); + + subtest_empty_name_pipe_operations(hdirectory); + + name.Buffer = NULL; + name.Length = 0; + name.MaximumLength = 0; + attr.RootDirectory = hdirectory; + + hpipe = NULL; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ | SYNCHRONIZE, &attr, + &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, + 0, 0, 0, 3, 4096, 4096, &timeout); + ok(!status, "Got unexpected status %#lx.\n", status); + type_info->TypeName.Buffer = NULL; + status = pNtQueryObject(hpipe, ObjectTypeInformation, type_info, sizeof(buffer), NULL); + ok(!status, "Got unexpected status %#lx.\n", status); + ok(type_info->TypeName.Buffer && !wcscmp(type_info->TypeName.Buffer, L"File"), + "Got unexpected type %s.\n", debugstr_w(type_info->TypeName.Buffer)); + status = pNtQueryObject(hpipe, ObjectNameInformation, name_info, sizeof(buffer), NULL); + ok(status == STATUS_OBJECT_PATH_INVALID, "Got unexpected status %#lx.\n", status); + + status = pNtCreateNamedPipeFile(&handle, GENERIC_READ | SYNCHRONIZE, &attr, + &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, + 0, 0, 0, 1, 4096, 4096, &timeout); + todo_wine ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "Got unexpected status %#lx.\n", status); + + status = pNtCreateNamedPipeFile(&hpipe2, GENERIC_READ | SYNCHRONIZE, &attr, + &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, + 0, 0, 0, 3, 4096, 4096, &timeout); + ok(!status, "Got unexpected status %#lx.\n", status); + + attr.RootDirectory = hpipe; + pRtlInitUnicodeString(&name, L"a"); + status = NtCreateFile(&hwrite, GENERIC_WRITE | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + ok(status == STATUS_OBJECT_NAME_INVALID, "Got unexpected status %#lx.\n", status); + + name.Buffer = NULL; + name.Length = 0; + name.MaximumLength = 0; + attr.RootDirectory = hpipe; + status = NtCreateFile(&hwrite, GENERIC_WRITE | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + ok(!status, "Got unexpected status %#lx.\n", status); + + type_info->TypeName.Buffer = NULL; + status = pNtQueryObject(hwrite, ObjectTypeInformation, type_info, sizeof(buffer), NULL); + ok(!status, "Got unexpected status %#lx.\n", status); + ok(type_info->TypeName.Buffer && !wcscmp(type_info->TypeName.Buffer, L"File"), + "Got unexpected type %s.\n", debugstr_w(type_info->TypeName.Buffer)); + status = pNtQueryObject(hwrite, ObjectNameInformation, name_info, sizeof(buffer), NULL); + ok(status == STATUS_OBJECT_PATH_INVALID, "Got unexpected status %#lx.\n", status); + + attr.RootDirectory = hpipe; + status = NtCreateFile(&handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + ok(status == STATUS_PIPE_NOT_AVAILABLE, "Got unexpected status %#lx.\n", status); + + attr.RootDirectory = hpipe; + status = NtCreateFile(&handle, GENERIC_WRITE | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + ok(status == STATUS_PIPE_NOT_AVAILABLE, "Got unexpected status %#lx.\n", status); + + attr.RootDirectory = hpipe2; + status = NtCreateFile(&hwrite2, GENERIC_WRITE | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, &io, NULL, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); + ok(!status, "Got unexpected status %#lx.\n", status); + + data = 0xdeadbeef; + ret = WriteFile(hwrite, &data, sizeof(data), &length, NULL); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + ok(length == sizeof(data), "Got unexpected length %#lx.\n", length); + + data = 0xfeedcafe; + ret = WriteFile(hwrite2, &data, sizeof(data), &length, NULL); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + ok(length == sizeof(data), "Got unexpected length %#lx.\n", length); + + data = 0; + ret = ReadFile(hpipe, &data, sizeof(data), &length, NULL); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + ok(length == sizeof(data), "Got unexpected length %#lx.\n", length); + ok(data == 0xdeadbeef, "Got unexpected data %#lx.\n", data); + + data = 0; + ret = ReadFile(hpipe2, &data, sizeof(data), &length, NULL); + ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError()); + ok(length == sizeof(data), "Got unexpected length %#lx.\n", length); + ok(data == 0xfeedcafe, "Got unexpected data %#lx.\n", data); + + CloseHandle(hwrite); + CloseHandle(hpipe); + CloseHandle(hpipe2); + CloseHandle(hwrite2); + + pRtlInitUnicodeString(&name, L"test3\\pipe"); + attr.RootDirectory = hdirectory; + attr.ObjectName = &name; + timeout.QuadPart = -(LONG64)10000000; + status = pNtCreateNamedPipeFile(&hpipe, GENERIC_READ|GENERIC_WRITE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_CREATE, FILE_PIPE_FULL_DUPLEX, 0, 0, 0, 1, 256, 256, &timeout); + ok(!status, "unexpected failure from NtCreateNamedPipeFile: %#lx\n", status); + + handle = CreateFileA("\\\\.\\pipe\\test3\\pipe", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, 0 ); + ok(handle != INVALID_HANDLE_VALUE, "Failed to open NamedPipe (%lu)\n", GetLastError()); + + CloseHandle(handle); + CloseHandle(hpipe); + CloseHandle(hdirectory); +} + +struct pipe_name_test { + const WCHAR *name; + NTSTATUS status; + BOOL todo; + const WCHAR *no_open_name; +}; + +static void subtest_pipe_name(const struct pipe_name_test *pnt) +{ + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER timeout; + IO_STATUS_BLOCK iosb; + HANDLE pipe, client; + UNICODE_STRING name; + NTSTATUS status; + + pRtlInitUnicodeString(&name, pnt->name); + InitializeObjectAttributes(&attr, &name, OBJ_CASE_INSENSITIVE, NULL, NULL); + timeout.QuadPart = -100000000; + pipe = NULL; + status = pNtCreateNamedPipeFile(&pipe, + GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, + 0, 0, 0, 3, 4096, 4096, &timeout); + todo_wine_if(pnt->todo) + ok(status == pnt->status, "Expected status %#lx, got %#lx\n", pnt->status, status); + + if (!NT_SUCCESS(status)) + { + ok(pipe == NULL, "expected NULL handle, got %p\n", pipe); + return; + } + + ok(pipe != NULL, "expected non-NULL handle\n"); + + client = NULL; + status = NtCreateFile(&client, SYNCHRONIZE, &attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0); + ok(status == STATUS_SUCCESS, "Expected success, got %#lx\n", status); + ok(client != NULL, "expected non-NULL handle\n"); + NtClose(client); + + if (pnt->no_open_name) + { + OBJECT_ATTRIBUTES no_open_attr; + UNICODE_STRING no_open_name; + + pRtlInitUnicodeString(&no_open_name, pnt->no_open_name); + InitializeObjectAttributes(&no_open_attr, &no_open_name, OBJ_CASE_INSENSITIVE, NULL, NULL); + client = NULL; + status = NtCreateFile(&client, SYNCHRONIZE, &no_open_attr, &iosb, NULL, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, + "Expected STATUS_OBJECT_NAME_NOT_FOUND opening %s, got %#lx\n", + debugstr_wn(no_open_name.Buffer, no_open_name.Length / sizeof(WCHAR)), status); + ok(client == NULL, "expected NULL handle, got %p\n", client); + } + + NtClose(pipe); +} + +static void test_pipe_names(void) +{ + static const struct pipe_name_test tests[] = { + { L"\\Device\\NamedPipe" , STATUS_OBJECT_NAME_INVALID, TRUE }, + { L"\\Device\\NamedPipe\\" , STATUS_OBJECT_NAME_INVALID, TRUE }, + { L"\\Device\\NamedPipe\\\\" , STATUS_SUCCESS }, + { L"\\Device\\NamedPipe\\wine-test\\" , STATUS_SUCCESS, 0, L"\\Device\\NamedPipe\\wine-test" }, + { L"\\Device\\NamedPipe\\wine/test" , STATUS_SUCCESS, 0, L"\\Device\\NamedPipe\\wine\\test" }, + { L"\\Device\\NamedPipe\\wine:test" , STATUS_SUCCESS }, + { L"\\Device\\NamedPipe\\wine\\.\\test" , STATUS_SUCCESS, 0, L"\\Device\\NamedPipe\\wine\\test" }, + { L"\\Device\\NamedPipe\\wine\\..\\test" , STATUS_SUCCESS, 0, L"\\Device\\NamedPipe\\test" }, + { L"\\Device\\NamedPipe\\..\\wine-test" , STATUS_SUCCESS }, + { L"\\Device\\NamedPipe\\!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", STATUS_SUCCESS }, + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + const struct pipe_name_test *pnt = &tests[i]; + + winetest_push_context("test %Iu: %s", i, debugstr_w(pnt->name)); + subtest_pipe_name(pnt); + winetest_pop_context(); + } +} + +static void test_async_cancel_on_handle_close(void) +{ + static const struct + { + BOOL event; + PIO_APC_ROUTINE apc; + BOOL apc_context; + } + tests[] = + { + {TRUE, NULL}, + {FALSE, NULL}, + {TRUE, ioapc}, + {FALSE, ioapc}, + {TRUE, NULL, TRUE}, + {FALSE, NULL, TRUE}, + {TRUE, ioapc, TRUE}, + {FALSE, ioapc, TRUE}, + }; + + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + char read_buf[16]; + HANDLE port, write, read, event, handle2, process_handle; + IO_STATUS_BLOCK io; + NTSTATUS status; + unsigned int i, other_process; + DWORD ret; + BOOL bret; + + create_pipe_pair(&read, &write, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096); + + status = pNtQueryInformationFile(read, &io, &info, sizeof(info), + FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS || broken(status == STATUS_INVALID_INFO_CLASS), + "status = %lx\n", status); + CloseHandle(read); + CloseHandle(write); + if (status) + { + win_skip("FileIoCompletionNotificationInformation is not supported.\n"); + return; + } + + process_handle = create_process("sleep"); + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + for (other_process = 0; other_process < 2; ++other_process) + { + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("other_process %u, i %u", other_process, i); + create_pipe_pair(&read, &write, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 4096); + port = CreateIoCompletionPort(read, NULL, 0, 0); + ok(!!port, "got %p.\n", port); + + memset(&io, 0xcc, sizeof(io)); + ResetEvent(event); + status = NtReadFile(read, tests[i].event ? event : NULL, tests[i].apc, tests[i].apc_context ? &io : NULL, &io, + read_buf, 16, NULL, NULL); + if (tests[i].apc) + { + ok(status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status); + CloseHandle(read); + CloseHandle(write); + CloseHandle(port); + winetest_pop_context(); + continue; + } + ok(status == STATUS_PENDING, "got %#lx.\n", status); + ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + + bret = DuplicateHandle(GetCurrentProcess(), read, other_process ? process_handle : GetCurrentProcess(), + &handle2, 0, FALSE, DUPLICATE_SAME_ACCESS); + ok(bret, "failed, error %lu.\n", GetLastError()); + + CloseHandle(read); + /* Canceled asyncs with completion port and no event do not update IOSB before removing completion. */ + todo_wine_if(other_process && tests[i].apc_context && !tests[i].event) + ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + + if (other_process && tests[i].apc_context && !tests[i].event) + test_queued_completion(port, &io, STATUS_CANCELLED, 0); + else + test_no_queued_completion(port); + + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_TIMEOUT, "got %#lx.\n", ret); + + if (other_process) + { + bret = DuplicateHandle(process_handle, handle2, GetCurrentProcess(), &read, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + ok(bret, "failed, error %lu.\n", GetLastError()); + } + else + { + read = handle2; + } + CloseHandle(read); + CloseHandle(write); + CloseHandle(port); + winetest_pop_context(); + } + } + + CloseHandle(event); + TerminateProcess(process_handle, 0); + WaitForSingleObject(process_handle, INFINITE); + CloseHandle(process_handle); +} + START_TEST(pipe) { + char **argv; + int argc; + if (!init_func_ptrs()) return; + if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + + argc = winetest_get_mainargs(&argv); + if (argc >= 3) + { + if (!strcmp(argv[2], "sleep")) + { + Sleep(5000); + return; + } + return; + } + trace("starting invalid create tests\n"); test_create_invalid(); @@ -1506,6 +3187,10 @@ START_TEST(pipe) trace("starting completion tests\n"); test_completion(); + trace("starting blocking tests\n"); + test_blocking(FILE_SYNCHRONOUS_IO_NONALERT); + test_blocking(FILE_SYNCHRONOUS_IO_ALERT); + trace("starting FILE_PIPE_INFORMATION tests\n"); test_filepipeinfo(); @@ -1521,6 +3206,9 @@ START_TEST(pipe) trace("starting cancelio tests\n"); test_cancelio(); + trace("starting cancelsynchronousio tests\n"); + test_cancelsynchronousio(); + trace("starting byte read in byte mode client -> server\n"); read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE); trace("starting byte read in message mode client -> server\n"); @@ -1534,7 +3222,15 @@ START_TEST(pipe) trace("starting message read in message mode server -> client\n"); read_pipe_test(PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE); + test_transceive(); test_volume_info(); test_file_info(); test_security_info(); + test_empty_name(); + test_pipe_names(); + test_async_cancel_on_handle_close(); + + pipe_for_each_state(create_pipe_server, connect_pipe, test_pipe_state); + pipe_for_each_state(create_pipe_server, connect_and_write_pipe, test_pipe_with_data_state); + pipe_for_each_state(create_local_info_test_pipe, connect_pipe_reader, test_pipe_local_info); } diff --git a/modules/rostests/winetests/ntdll/port.c b/modules/rostests/winetests/ntdll/port.c index 9525232ade5..80c60d09683 100644 --- a/modules/rostests/winetests/ntdll/port.c +++ b/modules/rostests/winetests/ntdll/port.c @@ -155,7 +155,7 @@ static BOOL init_function_ptrs(void) !pNtRequestPort || !pNtRegisterThreadTerminatePort || !pNtConnectPort || !pRtlInitUnicodeString) { - win_skip("Needed port functions are not available\n"); + todo_wine win_skip("Needed port functions are not available\n"); FreeLibrary(hntdll); return FALSE; } @@ -183,10 +183,10 @@ static void ProcessConnectionRequest(union lpc_message *LpcMessage, PHANDLE pAcc } status = pNtAcceptConnectPort(pAcceptPortHandle, 0, &LpcMessage->msg, 1, NULL, NULL); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); status = pNtCompleteConnectPort(*pAcceptPortHandle); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); } static void ProcessLpcRequest(HANDLE PortHandle, union lpc_message *LpcMessage) @@ -202,7 +202,7 @@ static void ProcessLpcRequest(HANDLE PortHandle, union lpc_message *LpcMessage) strcpy((LPSTR)LpcMessage->msg64.Data, REPLY); status = pNtReplyPort(PortHandle, &LpcMessage->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(LpcMessage->msg64.MessageType == LPC_REQUEST, "Expected LPC_REQUEST, got %d\n", LpcMessage->msg64.MessageType); ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REPLY), @@ -217,7 +217,7 @@ static void ProcessLpcRequest(HANDLE PortHandle, union lpc_message *LpcMessage) strcpy((LPSTR)LpcMessage->msg.Data, REPLY); status = pNtReplyPort(PortHandle, &LpcMessage->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(LpcMessage->msg.MessageType == LPC_REQUEST, "Expected LPC_REQUEST, got %d\n", LpcMessage->msg.MessageType); ok(!strcmp((LPSTR)LpcMessage->msg.Data, REPLY), @@ -239,11 +239,11 @@ static DWORD WINAPI test_ports_client(LPVOID arg) sqos.EffectiveOnly = TRUE; status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL); - todo_wine ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + todo_wine ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); if (status != STATUS_SUCCESS) return 1; status = pNtRegisterThreadTerminatePort(PortHandle); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); if (is_wow64) { @@ -256,7 +256,7 @@ static DWORD WINAPI test_ports_client(LPVOID arg) strcpy((LPSTR)LpcMessage->msg64.Data, REQUEST1); status = pNtRequestPort(PortHandle, &LpcMessage->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(LpcMessage->msg64.MessageType == 0, "Expected 0, got %d\n", LpcMessage->msg64.MessageType); ok(!strcmp((LPSTR)LpcMessage->msg64.Data, REQUEST1), "Expected %s, got %s\n", REQUEST1, LpcMessage->msg64.Data); @@ -269,7 +269,7 @@ static DWORD WINAPI test_ports_client(LPVOID arg) /* Send the message and wait for the reply */ status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(!strcmp((LPSTR)out->msg64.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg64.Data); ok(out->msg64.MessageType == LPC_REPLY, "Expected LPC_REPLY, got %d\n", out->msg64.MessageType); } @@ -284,7 +284,7 @@ static DWORD WINAPI test_ports_client(LPVOID arg) strcpy((LPSTR)LpcMessage->msg.Data, REQUEST1); status = pNtRequestPort(PortHandle, &LpcMessage->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(LpcMessage->msg.MessageType == 0, "Expected 0, got %d\n", LpcMessage->msg.MessageType); ok(!strcmp((LPSTR)LpcMessage->msg.Data, REQUEST1), "Expected %s, got %s\n", REQUEST1, LpcMessage->msg.Data); @@ -297,7 +297,7 @@ static DWORD WINAPI test_ports_client(LPVOID arg) /* Send the message and wait for the reply */ status = pNtRequestWaitReplyPort(PortHandle, &LpcMessage->msg, &out->msg); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %x\n", status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %lx\n", status); ok(!strcmp((LPSTR)out->msg.Data, REPLY), "Expected %s, got %s\n", REPLY, out->msg.Data); ok(out->msg.MessageType == LPC_REPLY, "Expected LPC_REPLY, got %d\n", out->msg.MessageType); } @@ -324,7 +324,7 @@ static void test_ports_server( HANDLE PortHandle ) status = pNtReplyWaitReceivePort(PortHandle, NULL, NULL, &LpcMessage->msg); todo_wine { - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %d(%x)\n", status, status); + ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld(%lx)\n", status, status); } /* STATUS_INVALID_HANDLE: win2k without admin rights will perform an * endless loop here @@ -384,7 +384,7 @@ START_TEST(port) status = pNtCreatePort(&port_handle, &obj, 100, 100, 0); if (status == STATUS_ACCESS_DENIED) skip("Not enough rights\n"); - else todo_wine ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %d\n", status); + else ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status); if (status == STATUS_SUCCESS) { diff --git a/modules/rostests/winetests/ntdll/process.c b/modules/rostests/winetests/ntdll/process.c deleted file mode 100644 index f10ed527834..00000000000 --- a/modules/rostests/winetests/ntdll/process.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Unit test suite for process functions - * - * Copyright 2017 Michael Müller - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include - -#include "ntdll_test.h" - -#include "windef.h" -#include "winbase.h" - -static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE); -static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE); -static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE,PULONG); -static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE); - -static void test_NtSuspendProcess(char *process_name) -{ - PROCESS_INFORMATION info; - DEBUG_EVENT ev; - STARTUPINFOA startup; - NTSTATUS status; - HANDLE event; - char buffer[MAX_PATH]; - ULONG count; - DWORD ret; - - status = pNtResumeProcess(GetCurrentProcess()); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - event = CreateEventA(NULL, TRUE, FALSE, "wine_suspend_event"); - ok(!!event, "Failed to create event: %u\n", GetLastError()); - - memset(&startup, 0, sizeof(startup)); - startup.cb = sizeof(startup); - - sprintf(buffer, "%s tests/process.c dummy_process wine_suspend_event", process_name); - ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); - ok(ret, "CreateProcess failed with error %u\n", GetLastError()); - - ret = WaitForSingleObject(event, 500); - ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret); - - status = pNtSuspendProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - ResetEvent(event); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = NtResumeThread(info.hThread, &count); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - ok(count == 1, "Expected count 1, got %d\n", count); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret); - - status = pNtResumeProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - status = pNtSuspendThread(info.hThread, &count); - ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status); - ok(count == 0, "Expected count 0, got %d\n", count); - - ResetEvent(event); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = pNtResumeProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret); - - status = pNtSuspendThread(info.hThread, &count); - ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status); - ok(count == 0, "Expected count 0, got %d\n", count); - - status = pNtSuspendThread(info.hThread, &count); - ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status); - ok(count == 1, "Expected count 1, got %d\n", count); - - ResetEvent(event); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = pNtResumeProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = pNtResumeProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret); - - ret = DebugActiveProcess(info.dwProcessId); - ok(ret, "Failed to debug process: %d\n", GetLastError()); - - ResetEvent(event); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - disable_success_count - for (;;) - { - ret = WaitForDebugEvent(&ev, INFINITE); - ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError()); - if (!ret) break; - - if (ev.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) break; - - ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); - if (!ret) break; - } - - ResetEvent(event); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = pNtResumeProcess(info.hProcess); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - status = NtResumeThread(info.hThread, &count); - ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status); - ok(count == 0, "Expected count 0, got %d\n", count); - - ret = WaitForSingleObject(event, 200); - ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret); - - ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); - ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError()); - - ret = WaitForSingleObject(event, 200); - ros_skip_flaky - ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret); - - TerminateProcess(info.hProcess, 0); - - CloseHandle(info.hProcess); - CloseHandle(info.hThread); -} - -static void dummy_process(char *event_name) -{ - HANDLE event = OpenEventA(EVENT_ALL_ACCESS, FALSE, event_name); - - while (TRUE) - { - SetEvent(event); - OutputDebugStringA("test"); - Sleep(5); - } -} - -START_TEST(process) -{ - HMODULE mod; - char **argv; - int argc; - - argc = winetest_get_mainargs(&argv); - if (argc >= 4 && strcmp(argv[2], "dummy_process") == 0) - { - dummy_process(argv[3]); - return; - } - - mod = GetModuleHandleA("ntdll.dll"); - if (!mod) - { - win_skip("Not running on NT, skipping tests\n"); - return; - } - - pNtResumeProcess = (void*)GetProcAddress(mod, "NtResumeProcess"); - pNtSuspendProcess = (void*)GetProcAddress(mod, "NtSuspendProcess"); - pNtResumeThread = (void*)GetProcAddress(mod, "NtResumeThread"); - pNtSuspendThread = (void*)GetProcAddress(mod, "NtSuspendThread"); - - test_NtSuspendProcess(argv[0]); -} diff --git a/modules/rostests/winetests/ntdll/reg.c b/modules/rostests/winetests/ntdll/reg.c index 1ee28ac3b34..83961543513 100644 --- a/modules/rostests/winetests/ntdll/reg.c +++ b/modules/rostests/winetests/ntdll/reg.c @@ -22,12 +22,21 @@ * */ -#include "ntdll_test.h" -#include "winternl.h" -#include "stdio.h" -#include "winnt.h" +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" #include "winnls.h" -#include "stdlib.h" +#include "winternl.h" +#include "wine/test.h" +#ifdef __REACTOS__ +#define REG_APP_HIVE 0x10 +#endif /* A test string */ static const WCHAR stringW[] = {'s', 't', 'r', 'i', 'n', 'g', 'W', 0}; @@ -125,6 +134,8 @@ static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, PHANDLE); static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES); static NTSTATUS (WINAPI * pNtOpenKeyEx)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); static NTSTATUS (WINAPI * pNtClose)(IN HANDLE); +static NTSTATUS (WINAPI * pNtEnumerateKey)(HANDLE, ULONG, KEY_INFORMATION_CLASS, void *, DWORD, DWORD *); +static NTSTATUS (WINAPI * pNtEnumerateValueKey)(HANDLE, ULONG, KEY_VALUE_INFORMATION_CLASS, void *, DWORD, DWORD *); static NTSTATUS (WINAPI * pNtFlushKey)(HANDLE); static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE); static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, @@ -132,10 +143,10 @@ static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, con PULONG dispos ); static NTSTATUS (WINAPI * pNtQueryKey)(HANDLE,KEY_INFORMATION_CLASS,PVOID,ULONG,PULONG); static NTSTATUS (WINAPI * pNtQueryLicenseValue)(const UNICODE_STRING *,ULONG *,PVOID,ULONG,ULONG *); +static NTSTATUS (WINAPI * pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, void *, ULONG, ULONG *); static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *); static NTSTATUS (WINAPI * pNtSetValueKey)(HANDLE, const PUNICODE_STRING, ULONG, ULONG, const void*, ULONG ); -static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG); static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING); static LONG (WINAPI * pRtlCompareUnicodeString)(const PUNICODE_STRING,const PUNICODE_STRING,BOOLEAN); static BOOLEAN (WINAPI * pRtlCreateUnicodeString)(PUNICODE_STRING, LPCWSTR); @@ -145,14 +156,15 @@ static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRIN static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID); static LPVOID (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG); static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG); +static NTSTATUS (WINAPI * pRtlCreateRegistryKey)(ULONG, PWSTR); static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*,void *); static NTSTATUS (WINAPI * pNtNotifyChangeKey)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,ULONG,BOOLEAN,PVOID,ULONG,BOOLEAN); static NTSTATUS (WINAPI * pNtNotifyChangeMultipleKeys)(HANDLE,ULONG,OBJECT_ATTRIBUTES*,HANDLE,PIO_APC_ROUTINE, void*,IO_STATUS_BLOCK*,ULONG,BOOLEAN,void*,ULONG,BOOLEAN); static NTSTATUS (WINAPI * pNtWaitForSingleObject)(HANDLE,BOOLEAN,const LARGE_INTEGER*); +static NTSTATUS (WINAPI * pNtLoadKeyEx)(const OBJECT_ATTRIBUTES*,OBJECT_ATTRIBUTES*,ULONG,HANDLE,HANDLE,ACCESS_MASK,HANDLE*,IO_STATUS_BLOCK*); static HMODULE hntdll = 0; -static int CurrentTest = 0; static UNICODE_STRING winetestpath; #define NTDLL_GET_PROC(func) \ @@ -180,11 +192,13 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(NtClose) NTDLL_GET_PROC(NtDeleteValueKey) NTDLL_GET_PROC(NtCreateKey) + NTDLL_GET_PROC(NtEnumerateKey) + NTDLL_GET_PROC(NtEnumerateValueKey) NTDLL_GET_PROC(NtFlushKey) NTDLL_GET_PROC(NtDeleteKey) NTDLL_GET_PROC(NtQueryKey) + NTDLL_GET_PROC(NtQueryObject) NTDLL_GET_PROC(NtQueryValueKey) - NTDLL_GET_PROC(NtQueryInformationProcess) NTDLL_GET_PROC(NtSetValueKey) NTDLL_GET_PROC(NtOpenKey) NTDLL_GET_PROC(NtNotifyChangeKey) @@ -196,9 +210,11 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(RtlFreeHeap) NTDLL_GET_PROC(RtlAllocateHeap) NTDLL_GET_PROC(RtlZeroMemory) + NTDLL_GET_PROC(RtlCreateRegistryKey) NTDLL_GET_PROC(RtlpNtQueryValueKey) NTDLL_GET_PROC(RtlOpenCurrentUser) NTDLL_GET_PROC(NtWaitForSingleObject) + NTDLL_GET_PROC(NtLoadKeyEx); /* optional functions */ pNtQueryLicenseValue = (void *)GetProcAddress(hntdll, "NtQueryLicenseValue"); @@ -209,134 +225,9 @@ static BOOL InitFunctionPtrs(void) } #undef NTDLL_GET_PROC -static NTSTATUS WINAPI QueryRoutine (IN PCWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, - IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext) -{ - NTSTATUS ret = STATUS_SUCCESS; - - trace("**Test %d**\n", CurrentTest); - trace("ValueName: %s\n", wine_dbgstr_w(ValueName)); - - switch(ValueType) - { - case REG_NONE: - trace("ValueType: REG_NONE\n"); - trace("ValueData: %p\n", ValueData); - break; - - case REG_BINARY: - trace("ValueType: REG_BINARY\n"); - trace("ValueData: %p\n", ValueData); - break; - - case REG_SZ: - trace("ValueType: REG_SZ\n"); - trace("ValueData: %s\n", (char*)ValueData); - break; - - case REG_MULTI_SZ: - trace("ValueType: REG_MULTI_SZ\n"); - trace("ValueData: %s\n", (char*)ValueData); - break; - - case REG_EXPAND_SZ: - trace("ValueType: REG_EXPAND_SZ\n"); - trace("ValueData: %s\n", (char*)ValueData); - break; - - case REG_DWORD: - trace("ValueType: REG_DWORD\n"); - trace("ValueData: %p\n", ValueData); - break; - }; - trace("ValueLength: %d\n", (int)ValueLength); - - if(CurrentTest == 0) - ok(1, "\n"); /*checks that QueryRoutine is called*/ - if(CurrentTest > 7) - ok(!1, "Invalid Test Specified!\n"); - - CurrentTest++; - - return ret; -} - -static void test_RtlQueryRegistryValues(void) -{ - - /* - ****************************** - * QueryTable Flags * - ****************************** - *RTL_QUERY_REGISTRY_SUBKEY * Name is the name of a subkey relative to Path - *RTL_QUERY_REGISTRY_TOPKEY * Resets location to original RelativeTo and Path - *RTL_QUERY_REGISTRY_REQUIRED * Key required. returns STATUS_OBJECT_NAME_NOT_FOUND if not present - *RTL_QUERY_REGISTRY_NOVALUE * We just want a call-back - *RTL_QUERY_REGISTRY_NOEXPAND * Don't expand the variables! - *RTL_QUERY_REGISTRY_DIRECT * Results of query will be stored in EntryContext(QueryRoutine ignored) - *RTL_QUERY_REGISTRY_DELETE * Delete value key after query - ****************************** - - - **Test layout(numbered according to CurrentTest value)** - 0)NOVALUE Just make sure call-back works - 1)Null Name See if QueryRoutine is called for every value in current key - 2)SUBKEY See if we can use SUBKEY to change the current path on the fly - 3)REQUIRED Test for value that's not there - 4)NOEXPAND See if it will return multiple strings(no expand should split strings up) - 5)DIRECT Make it store data directly in EntryContext and not call QueryRoutine - 6)DefaultType Test return values when key isn't present - 7)DefaultValue Test Default Value returned with key isn't present(and no REQUIRED flag set) - 8)DefaultLength Test Default Length with DefaultType = REG_SZ - 9)DefaultLength Test Default Length with DefaultType = REG_MULTI_SZ - 10)DefaultLength Test Default Length with DefaultType = REG_EXPAND_SZ - 11)DefaultData Test whether DefaultData is used while DefaultType = REG_NONE(shouldn't be) - 12)Delete Try to delete value key - - */ - NTSTATUS status; - ULONG RelativeTo; - - PRTL_QUERY_REGISTRY_TABLE QueryTable = NULL; - RelativeTo = RTL_REGISTRY_ABSOLUTE;/*Only using absolute - no need to test all relativeto variables*/ - - QueryTable = pRtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_QUERY_REGISTRY_TABLE)*26); - - pRtlZeroMemory( QueryTable, sizeof(RTL_QUERY_REGISTRY_TABLE) * 26); - - QueryTable[0].QueryRoutine = QueryRoutine; - QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOVALUE; - QueryTable[0].Name = NULL; - QueryTable[0].EntryContext = NULL; - QueryTable[0].DefaultType = REG_BINARY; - QueryTable[0].DefaultData = NULL; - QueryTable[0].DefaultLength = 100; - - QueryTable[1].QueryRoutine = QueryRoutine; - QueryTable[1].Flags = 0; - QueryTable[1].Name = NULL; - QueryTable[1].EntryContext = 0; - QueryTable[1].DefaultType = REG_NONE; - QueryTable[1].DefaultData = NULL; - QueryTable[1].DefaultLength = 0; - - QueryTable[2].QueryRoutine = NULL; - QueryTable[2].Flags = 0; - QueryTable[2].Name = NULL; - QueryTable[2].EntryContext = 0; - QueryTable[2].DefaultType = REG_NONE; - QueryTable[2].DefaultData = NULL; - QueryTable[2].DefaultLength = 0; - - status = pRtlQueryRegistryValues(RelativeTo, winetestpath.Buffer, QueryTable, 0, 0); - ok(status == STATUS_SUCCESS, "RtlQueryRegistryValues return: 0x%08x\n", status); - - pRtlFreeHeap(GetProcessHeap(), 0, QueryTable); -} - static void test_NtOpenKey(void) { - HANDLE key; + HANDLE key, subkey; NTSTATUS status; OBJECT_ATTRIBUTES attr; ACCESS_MASK am = KEY_READ; @@ -344,110 +235,134 @@ static void test_NtOpenKey(void) /* All NULL */ status = pNtOpenKey(NULL, 0, NULL); - ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08lx\n", status); /* NULL attributes */ status = pNtOpenKey(&key, 0, NULL); - ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08lx\n", status); InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); /* NULL key */ status = pNtOpenKey(NULL, am, &attr); - ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08lx\n", status); /* Length > sizeof(OBJECT_ATTRIBUTES) */ attr.Length *= 2; status = pNtOpenKey(&key, am, &attr); - ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08lx\n", status); /* Zero accessmask */ attr.Length = sizeof(attr); key = (HANDLE)0xdeadbeef; status = pNtOpenKey(&key, 0, &attr); -todo_wine - ok(status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got: 0x%08x\n", status); -todo_wine + todo_wine + ok(status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got: 0x%08lx\n", status); + todo_wine ok(!key, "key = %p\n", key); if (status == STATUS_SUCCESS) NtClose(key); - /* Calling without parent key requres full registry path. */ + /* Calling without parent key requires full registry path. */ pRtlCreateUnicodeStringFromAsciiz( &str, "Machine" ); InitializeObjectAttributes(&attr, &str, 0, 0, 0); key = (HANDLE)0xdeadbeef; status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey Failed: 0x%08x\n", status); -todo_wine + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey Failed: 0x%08lx\n", status); ok(!key, "key = %p\n", key); pRtlFreeUnicodeString( &str ); /* Open is case sensitive unless OBJ_CASE_INSENSITIVE is specified. */ pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine ok(status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_SUCCESS /* Win10 1607+ */, + "NtOpenKey Failed: 0x%08lx\n", status); + if (!status) pNtClose( key ); attr.Attributes = OBJ_CASE_INSENSITIVE; status = pNtOpenKey(&key, KEY_READ, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); pNtClose(key); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08lx\n", status ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\\\\\" ); + status = pNtOpenKey(&key, KEY_READ, &attr); + ok( status == STATUS_OBJECT_NAME_INVALID, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); pNtClose( key ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\" ); status = pNtOpenKey(&key, KEY_READ, &attr); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\\\" ); + status = pNtOpenKey(&key, KEY_READ, &attr); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); pNtClose( key ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar\\Machine" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Machine\\Software\\Classes" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "Machine\\Software\\Classes" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Device\\Null" ); status = pNtOpenKey(&key, KEY_READ, &attr); - todo_wine - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); + InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); + status = pNtOpenKey(&key, KEY_WRITE|KEY_READ, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status); + + /* keys are case insensitive even without OBJ_CASE_INSENSITIVE */ + InitializeObjectAttributes( &attr, &str, 0, key, 0 ); + pRtlInitUnicodeString( &str, L"\xf6\xf3\x14d\x371\xd801\xdc00" ); + status = pNtCreateKey( &subkey, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0); + ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status); + pNtClose( subkey ); + pRtlInitUnicodeString( &str, L"\xd6\xd3\x14c\x370\xd801\xdc28" ); /* surrogates not supported */ + status = pNtOpenKeyEx(&subkey, KEY_ALL_ACCESS, &attr, 0); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKeyEx failed: 0x%08lx\n", status); + pRtlInitUnicodeString( &str, L"\xd6\xd3\x14c\x370\xd801\xdc00" ); + status = pNtOpenKeyEx(&subkey, KEY_ALL_ACCESS, &attr, 0); + ok(status == STATUS_SUCCESS, "NtOpenKeyEx failed: 0x%08lx\n", status); + + pNtDeleteKey( subkey ); + pNtClose( subkey ); + pNtClose( key ); + if (!pNtOpenKeyEx) { win_skip("NtOpenKeyEx not available\n"); @@ -456,7 +371,7 @@ todo_wine InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKeyEx(&key, KEY_WRITE|KEY_READ, &attr, 0); - ok(status == STATUS_SUCCESS, "NtOpenKeyEx Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKeyEx Failed: 0x%08lx\n", status); pNtClose(key); } @@ -465,7 +380,7 @@ static void test_NtCreateKey(void) { /*Create WineTest*/ OBJECT_ATTRIBUTES attr; - HANDLE key, subkey; + HANDLE key, subkey, subkey2; ACCESS_MASK am = GENERIC_ALL; NTSTATUS status; UNICODE_STRING str; @@ -473,71 +388,86 @@ static void test_NtCreateKey(void) /* All NULL */ status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0); ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08lx\n", status); /* Only the key */ status = pNtCreateKey(&key, 0, NULL, 0, 0, 0, 0); - ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08lx\n", status); /* Only accessmask */ status = pNtCreateKey(NULL, am, NULL, 0, 0, 0, 0); ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08lx\n", status); /* Key and accessmask */ status = pNtCreateKey(&key, am, NULL, 0, 0, 0, 0); - ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */, - "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08lx\n", status); InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); /* Only attributes */ status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0); ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_ACCESS_DENIED /* Win7 */, - "Expected STATUS_ACCESS_VIOLATION or STATUS_ACCESS_DENIED, got: 0x%08x\n", status); + "Expected STATUS_ACCESS_VIOLATION or STATUS_ACCESS_DENIED, got: 0x%08lx\n", status); /* Length > sizeof(OBJECT_ATTRIBUTES) */ attr.Length *= 2; status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0); - ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08lx\n", status); attr.Length = sizeof(attr); status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0); - ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08lx\n", status); attr.RootDirectory = key; attr.ObjectName = &str; pRtlCreateUnicodeStringFromAsciiz( &str, "test\\sub\\key" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "test\\\\subkey" ); + status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey\\" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey\\" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS || broken(status == STATUS_OBJECT_NAME_NOT_FOUND), /* nt4 */ - "NtCreateKey failed: 0x%08x\n", status ); - if (status == STATUS_SUCCESS) - { - pNtDeleteKey( subkey ); - pNtClose( subkey ); - } + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey\\" ); + status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + pNtDeleteKey( subkey ); + pNtClose( subkey ); pRtlFreeUnicodeString( &str ); + pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey2\\\\" ); + status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey2\\\\test\\\\" ); + status = pNtCreateKey( &subkey2, am, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + pRtlFreeUnicodeString( &str ); + pNtDeleteKey( subkey2 ); + pNtClose( subkey2 ); + pNtDeleteKey( subkey ); + pNtClose( subkey ); + pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pNtDeleteKey( subkey ); pNtClose( subkey ); @@ -547,87 +477,88 @@ static void test_NtCreateKey(void) pRtlCreateUnicodeStringFromAsciiz( &str, "" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed: 0x%08x\n", status ); + "NtCreateKey failed: 0x%08lx\n", status ); if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed: 0x%08x\n", status ); + "NtCreateKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( subkey ); + pRtlFreeUnicodeString( &str ); + + pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\\\" ); + status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, + "NtCreateKey failed: 0x%08lx\n", status ); if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar\\Machine" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Machine\\Software\\Classes" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "Machine\\Software\\Classes" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Device\\Null" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine\\Software\\Classes" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed: 0x%08x\n", status ); + "NtCreateKey failed: 0x%08lx\n", status ); if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); /* the REGISTRY part is case-sensitive unless OBJ_CASE_INSENSITIVE is specified */ + am = GENERIC_READ; attr.Attributes = 0; pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine\\Software\\Classes" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - todo_wine - ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status ); + ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_SUCCESS /* Win10 1607+ */, + "NtCreateKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\REGISTRY\\Machine\\Software\\Classes" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, + "NtCreateKey failed: 0x%08lx\n", status ); if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); pRtlCreateUnicodeStringFromAsciiz( &str, "\\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES" ); status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED, - "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, + "NtCreateKey failed: 0x%08lx\n", status ); if (!status) pNtClose( subkey ); pRtlFreeUnicodeString( &str ); @@ -645,16 +576,16 @@ static void test_NtSetValueKey(void) InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, am, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest"); status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &data, sizeof(data)); - ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&ValName); pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest"); status = pNtSetValueKey(key, &ValName, 0, REG_SZ, (VOID*)stringW, STR_TRUNC_SIZE); - ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&ValName); pNtClose(key); @@ -665,19 +596,32 @@ static void test_RtlOpenCurrentUser(void) NTSTATUS status; HANDLE handle; status=pRtlOpenCurrentUser(KEY_READ, &handle); - ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08lx\n", status); pNtClose(handle); } static void test_RtlCheckRegistryKey(void) { + static WCHAR empty[] = {0}; NTSTATUS status; status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer); - ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08lx\n", status); status = pRtlCheckRegistryKey((RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL), winetestpath.Buffer); - ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08lx\n", status); + + status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, NULL); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and Path being NULL: 0x%08lx\n", status); + + status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, empty); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and Path being empty: 0x%08lx\n", status); + + status = pRtlCheckRegistryKey(RTL_REGISTRY_USER, NULL); + ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_USER and Path being NULL: 0x%08lx\n", status); + + status = pRtlCheckRegistryKey(RTL_REGISTRY_USER, empty); + ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_USER and Path being empty: 0x%08lx\n", status); } static void test_NtFlushKey(void) @@ -688,13 +632,13 @@ static void test_NtFlushKey(void) ACCESS_MASK am = KEY_ALL_ACCESS; status = pNtFlushKey(NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08lx\n", status); InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); pNtOpenKey(&hkey, am, &attr); status = pNtFlushKey(hkey); - ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08lx\n", status); pNtClose(hkey); } @@ -707,6 +651,7 @@ static void test_NtQueryValueKey(void) UNICODE_STRING ValName; KEY_VALUE_BASIC_INFORMATION *basic_info; KEY_VALUE_PARTIAL_INFORMATION *partial_info, pi; + KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *aligned_info; KEY_VALUE_FULL_INFORMATION *full_info; DWORD len, expected; @@ -714,67 +659,103 @@ static void test_NtQueryValueKey(void) InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, KEY_READ|KEY_SET_VALUE, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); len = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]); - basic_info = HeapAlloc(GetProcessHeap(), 0, len); + basic_info = HeapAlloc(GetProcessHeap(), 0, sizeof(*basic_info)); status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len); - ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status); - ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex); - ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type); - ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength); - ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08lx\n", status); + ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", basic_info->TitleIndex); + ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", basic_info->Type); + ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %ld\n", basic_info->NameLength); + ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %ld\n", len); basic_info = HeapReAlloc(GetProcessHeap(), 0, basic_info, len); status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len); - ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status); - ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex); - ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type); - ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength); - ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", basic_info->TitleIndex); + ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", basic_info->Type); + ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %ld\n", basic_info->NameLength); + ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %ld\n", len); ok(!memcmp(basic_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n"); HeapFree(GetProcessHeap(), 0, basic_info); len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]); - partial_info = HeapAlloc(GetProcessHeap(), 0, len); + partial_info = HeapAlloc(GetProcessHeap(), 0, sizeof(*partial_info)); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len); - ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status); - ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex); - ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type); - ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength); - ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08lx\n", status); + ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", partial_info->TitleIndex); + ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", partial_info->Type); + ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", partial_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); partial_info = HeapReAlloc(GetProcessHeap(), 0, partial_info, len); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len); - ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status); - ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex); - ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type); - ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength); - ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len); - ok(*(DWORD *)partial_info->Data == 711, "incorrect Data returned: 0x%x\n", *(DWORD *)partial_info->Data); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", partial_info->TitleIndex); + ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", partial_info->Type); + ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", partial_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); + ok(*(DWORD *)partial_info->Data == 711, "incorrect Data returned: 0x%lx\n", *(DWORD *)partial_info->Data); HeapFree(GetProcessHeap(), 0, partial_info); + len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[0]); + aligned_info = HeapAlloc(GetProcessHeap(), 0, sizeof(*aligned_info) + 4); + + aligned_info = (KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *)((char *)aligned_info + 4); + status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformationAlign64, aligned_info, len, &len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08lx\n", status); + ok(aligned_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", aligned_info->Type); + ok(aligned_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", aligned_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[aligned_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); + + len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[0]); + aligned_info = (KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *)((char *)aligned_info - 4); + status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformationAlign64, aligned_info, len, &len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08lx\n", status); + ok(aligned_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", aligned_info->Type); + ok(aligned_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", aligned_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[aligned_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); + + aligned_info = HeapReAlloc(GetProcessHeap(), 0, aligned_info, len + 4); + aligned_info = (KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *)((char *)aligned_info + 4); + status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformationAlign64, aligned_info, len, &len); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(aligned_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", aligned_info->Type); + ok(aligned_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", aligned_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[aligned_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); + ok(*(DWORD *)aligned_info->Data == 711, "incorrect Data returned: 0x%lx\n", *(DWORD *)aligned_info->Data); + + aligned_info = (KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 *)((char *)aligned_info - 4); + status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformationAlign64, aligned_info, len, &len); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(aligned_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", aligned_info->Type); + ok(aligned_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", aligned_info->DataLength); + ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data[aligned_info->DataLength]), "NtQueryValueKey returned wrong len %ld\n", len); + ok(*(DWORD *)aligned_info->Data == 711, "incorrect Data returned: 0x%lx\n", *(DWORD *)aligned_info->Data); + HeapFree(GetProcessHeap(), 0, aligned_info); + len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]); - full_info = HeapAlloc(GetProcessHeap(), 0, len); + full_info = HeapAlloc(GetProcessHeap(), 0, sizeof(*full_info)); status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len); - ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status); - ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex); - ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type); - ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength); - ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08lx\n", status); + ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", full_info->TitleIndex); + ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", full_info->Type); + ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", full_info->DataLength); + ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %ld\n", full_info->NameLength); ok(len == FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength, - "NtQueryValueKey returned wrong len %d\n", len); + "NtQueryValueKey returned wrong len %ld\n", len); len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength; full_info = HeapReAlloc(GetProcessHeap(), 0, full_info, len); status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len); - ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status); - ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex); - ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type); - ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength); - ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", full_info->TitleIndex); + ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %ld\n", full_info->Type); + ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %ld\n", full_info->DataLength); + ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %ld\n", full_info->NameLength); ok(!memcmp(full_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n"); - ok(*(DWORD *)((char *)full_info + full_info->DataOffset) == 711, "incorrect Data returned: 0x%x\n", + ok(*(DWORD *)((char *)full_info + full_info->DataOffset) == 711, "incorrect Data returned: 0x%lx\n", *(DWORD *)((char *)full_info + full_info->DataOffset)); HeapFree(GetProcessHeap(), 0, full_info); @@ -782,42 +763,42 @@ static void test_NtQueryValueKey(void) pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest"); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08lx\n", status); partial_info = HeapAlloc(GetProcessHeap(), 0, len+1); memset(partial_info, 0xbd, len+1); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len); - ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status); - ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex); - ok(partial_info->Type == REG_SZ, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type); - ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %ld\n", partial_info->TitleIndex); + ok(partial_info->Type == REG_SZ, "NtQueryValueKey returned wrong Type %ld\n", partial_info->Type); + ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %ld\n", partial_info->DataLength); ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n"); ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE)); expected = len; status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status); - ok(len == expected, "NtQueryValueKey wrong len %u\n", len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08lx\n", status); + ok(len == expected, "NtQueryValueKey wrong len %lu\n", len); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status); - ok(len == expected, "NtQueryValueKey wrong len %u\n", len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08lx\n", status); + ok(len == expected, "NtQueryValueKey wrong len %lu\n", len); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status); - ok(len == expected, "NtQueryValueKey wrong len %u\n", len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08lx\n", status); + ok(len == expected, "NtQueryValueKey wrong len %lu\n", len); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len); - ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status); - ok(len == expected, "NtQueryValueKey wrong len %u\n", len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08lx\n", status); + ok(len == expected, "NtQueryValueKey wrong len %lu\n", len); HeapFree(GetProcessHeap(), 0, partial_info); pRtlFreeUnicodeString(&ValName); pRtlCreateUnicodeStringFromAsciiz(&ValName, "custtest"); status = pNtSetValueKey(key, &ValName, 0, 0xff00ff00, NULL, 0); - ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08lx\n", status); status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, &pi, sizeof(pi), &len); - ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status); - ok(pi.Type == 0xff00ff00, "Type=%x\n", pi.Type); - ok(pi.DataLength == 0, "DataLength=%u\n", pi.DataLength); + ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08lx\n", status); + ok(pi.Type == 0xff00ff00, "Type=%lx\n", pi.Type); + ok(pi.DataLength == 0, "DataLength=%lu\n", pi.DataLength); pRtlFreeUnicodeString(&ValName); pNtClose(key); @@ -825,20 +806,61 @@ static void test_NtQueryValueKey(void) static void test_NtDeleteKey(void) { + UNICODE_STRING string; + char buffer[200]; NTSTATUS status; - HANDLE hkey; + HANDLE hkey, hkey2; OBJECT_ATTRIBUTES attr; - ACCESS_MASK am = KEY_ALL_ACCESS; + DWORD size; status = pNtDeleteKey(NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08lx\n", status); InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); - status = pNtOpenKey(&hkey, am, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + status = pNtOpenKey(&hkey, KEY_ALL_ACCESS, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); status = pNtDeleteKey(hkey); - ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08lx\n", status); + + status = pNtQueryKey(hkey, KeyNameInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtEnumerateKey(hkey, 0, KeyFullInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + pRtlInitUnicodeString(&string, L"value"); + status = pNtQueryValueKey(hkey, &string, KeyValueBasicInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtEnumerateValueKey(hkey, 0, KeyValuePartialInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtSetValueKey(hkey, &string, 0, REG_SZ, "test", 5); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtDeleteValueKey(hkey, &string); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtDeleteKey(hkey); + ok(!status, "got %#lx\n", status); + + RtlInitUnicodeString(&string, L"subkey"); + InitializeObjectAttributes(&attr, &string, OBJ_CASE_INSENSITIVE, hkey, NULL); + status = pNtOpenKey(&hkey2, KEY_READ, &attr); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtCreateKey(&hkey2, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtQueryObject(hkey, ObjectNameInformation, buffer, sizeof(buffer), &size); + ok(status == STATUS_KEY_DELETED, "got %#lx\n", status); + + status = pNtQueryObject(hkey, ObjectBasicInformation, buffer, sizeof(OBJECT_BASIC_INFORMATION), &size); + ok(!status, "got %#lx\n", status); + + status = pNtClose(hkey); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); } static void test_NtQueryLicenseKey(void) @@ -860,9 +882,9 @@ static void test_NtQueryLicenseKey(void) len = 0xbeef; memset(&name, 0, sizeof(name)); status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); /* test with empty key */ pRtlCreateUnicodeStringFromAsciiz(&name, ""); @@ -870,26 +892,26 @@ static void test_NtQueryLicenseKey(void) type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); type = 0xdead; status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); len = 0xbeef; status = pNtQueryLicenseValue(&name, NULL, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); pRtlFreeUnicodeString(&name); @@ -899,26 +921,26 @@ static void test_NtQueryLicenseKey(void) type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); type = 0xdead; status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); len = 0xbeef; status = pNtQueryLicenseValue(&name, NULL, buffer, sizeof(buffer), &len); - ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue returned %08x, expected STATUS_OBJECT_NAME_NOT_FOUND\n", status); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue returned %08lx, expected STATUS_OBJECT_NAME_NOT_FOUND\n", status); + ok(len == 0xbeef || broken(!len) /* Win10 1607 */, "expected unmodified value for len, got %lu\n", len); type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len); ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue unexpected succeeded\n"); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef || broken(!len) /* Win10 1607 */, "expected unmodified value for len, got %lu\n", len); pRtlFreeUnicodeString(&name); @@ -928,43 +950,43 @@ static void test_NtQueryLicenseKey(void) type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); type = 0xdead; status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); type = 0xdead; len = 0; status = pNtQueryLicenseValue(&name, &type, buffer, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(type == REG_SZ, "expected type = REG_SZ, got %u\n", type); - ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(type == REG_SZ, "expected type = REG_SZ, got %lu\n", type); + ok(len == sizeof(emptyW), "expected len = %lu, got %lu\n", (DWORD)sizeof(emptyW), len); len = 0; status = pNtQueryLicenseValue(&name, NULL, buffer, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(len == sizeof(emptyW), "expected len = %lu, got %lu\n", (DWORD)sizeof(emptyW), len); type = 0xdead; len = 0; memset(buffer, 0x11, sizeof(buffer)); status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len); - ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08x, expected STATUS_SUCCESS\n", status); - ok(type == REG_SZ, "expected type = REG_SZ, got %u\n", type); - ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len); + ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08lx, expected STATUS_SUCCESS\n", status); + ok(type == REG_SZ, "expected type = REG_SZ, got %lu\n", type); + ok(len == sizeof(emptyW), "expected len = %lu, got %lu\n", (DWORD)sizeof(emptyW), len); ok(!memcmp(buffer, emptyW, sizeof(emptyW)), "unexpected buffer content\n"); type = 0xdead; len = 0; memset(buffer, 0x11, sizeof(buffer)); status = pNtQueryLicenseValue(&name, &type, buffer, 2, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(type == REG_SZ, "expected type REG_SZ, got %u\n", type); - ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(type == REG_SZ, "expected type REG_SZ, got %lu\n", type); + ok(len == sizeof(emptyW), "expected len = %lu, got %lu\n", (DWORD)sizeof(emptyW), len); ok(buffer[0] == 0x1111, "expected buffer[0] = 0x1111, got %u\n", buffer[0]); pRtlFreeUnicodeString(&name); @@ -975,42 +997,42 @@ static void test_NtQueryLicenseKey(void) type = 0xdead; len = 0xbeef; status = pNtQueryLicenseValue(NULL, &type, &value, sizeof(value), &len); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); - ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); + ok(len == 0xbeef, "expected unmodified value for len, got %lu\n", len); type = 0xdead; status = pNtQueryLicenseValue(&name, &type, &value, sizeof(value), NULL); - ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status); - ok(type == 0xdead, "expected unmodified value for type, got %u\n", type); + ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08lx, expected STATUS_INVALID_PARAMETER\n", status); + ok(type == 0xdead, "expected unmodified value for type, got %lu\n", type); type = 0xdead; len = 0; status = pNtQueryLicenseValue(&name, &type, &value, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(type == REG_DWORD, "expected type = REG_DWORD, got %u\n", type); - ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(type == REG_DWORD, "expected type = REG_DWORD, got %lu\n", type); + ok(len == sizeof(value), "expected len = %lu, got %lu\n", (DWORD)sizeof(value), len); len = 0; status = pNtQueryLicenseValue(&name, NULL, &value, 0, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(len == sizeof(value), "expected len = %lu, got %lu\n", (DWORD)sizeof(value), len); type = 0xdead; len = 0; value = 0xdeadbeef; status = pNtQueryLicenseValue(&name, &type, &value, sizeof(value), &len); - ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08x, expected STATUS_SUCCESS\n", status); - ok(type == REG_DWORD, "expected type = REG_DWORD, got %u\n", type); - ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len); + ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08lx, expected STATUS_SUCCESS\n", status); + ok(type == REG_DWORD, "expected type = REG_DWORD, got %lu\n", type); + ok(len == sizeof(value), "expected len = %lu, got %lu\n", (DWORD)sizeof(value), len); ok(value != 0xdeadbeef, "expected value != 0xdeadbeef\n"); type = 0xdead; len = 0; status = pNtQueryLicenseValue(&name, &type, &value, 2, &len); - ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status); - ok(type == REG_DWORD, "expected type REG_DWORD, got %u\n", type); - ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08lx, expected STATUS_BUFFER_TOO_SMALL\n", status); + ok(type == REG_DWORD, "expected type REG_DWORD, got %lu\n", type); + ok(len == sizeof(value), "expected len = %lu, got %lu\n", (DWORD)sizeof(value), len); pRtlFreeUnicodeString(&name); } @@ -1020,7 +1042,7 @@ static void test_RtlpNtQueryValueKey(void) NTSTATUS status; status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL); - ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status); + ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08lx\n", status); } static void test_symlinks(void) @@ -1057,75 +1079,75 @@ static void test_symlinks(void) attr.SecurityQualityOfService = NULL; status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); attr.RootDirectory = root; attr.ObjectName = &link_str; status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); /* REG_SZ is not allowed */ status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len ); - ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status ); + ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08lx\n", status ); status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); /* other values are not allowed */ status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) ); - ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status ); + ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08lx\n", status ); /* try opening the target through the link */ attr.ObjectName = &link_str; key = (HANDLE)0xdeadbeef; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08lx\n", status ); ok( !key, "key = %p\n", key ); attr.ObjectName = &target_str; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); dw = 0xbeef; status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); pNtClose( key ); attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); - ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); + ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %lu\n", len ); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08lx\n", status ); /* REG_LINK can be created in non-link keys */ status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); status = pNtDeleteValueKey( key, &symlink_str ); - ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08lx\n", status ); pNtClose( key ); attr.Attributes = 0; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); - ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); + ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %lu\n", len ); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08lx\n", status ); pNtClose( key ); /* now open the symlink itself */ @@ -1134,87 +1156,79 @@ static void test_symlinks(void) attr.Attributes = OBJ_OPENLINK; attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); if (pNtOpenKeyEx) { /* REG_OPTION_OPEN_LINK flag doesn't matter */ status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, 0 ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); attr.Attributes = 0; status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08lx\n", status ); pNtClose( key ); } attr.Attributes = OBJ_OPENLINK; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); /* delete target and create by NtCreateKey on link */ attr.ObjectName = &target_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); status = pNtDeleteKey( key ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( key ); attr.ObjectName = &link_str; attr.Attributes = 0; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08lx\n", status ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - todo_wine ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); pNtClose( key ); - if (status) /* can be removed once todo_wine above is fixed */ - { - attr.ObjectName = &target_str; - attr.Attributes = OBJ_OPENLINK; - status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - pNtClose( key ); - } attr.ObjectName = &target_str; attr.Attributes = OBJ_OPENLINK; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey wrong status 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey wrong status 0x%08lx\n", status ); if (0) /* crashes the Windows kernel on some Vista systems */ { @@ -1224,21 +1238,21 @@ static void test_symlinks(void) attr.Attributes = OBJ_OPENLINK; attr.ObjectName = &null_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR), - "wrong len %u\n", len ); + "wrong len %lu\n", len ); pNtClose( key ); } @@ -1248,77 +1262,77 @@ static void test_symlinks(void) attr.Attributes = 0; attr.ObjectName = &null_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08lx\n", status ); pNtClose( key ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08lx\n", status ); pNtClose( key ); } /* target with terminating null doesn't work */ status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); attr.RootDirectory = root; attr.Attributes = 0; attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08lx\n", status ); /* relative symlink, works only on win2k */ status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); attr.ObjectName = &link_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND, - "NtOpenKey wrong status 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_NAME_INVALID /* Win10 1607+ */, + "NtOpenKey wrong status 0x%08lx\n", status ); key = (HKEY)0xdeadbeef; status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, NULL ); - ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08lx\n", status ); ok( !key, "key = %p\n", key ); status = pNtDeleteKey( link ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( link ); attr.ObjectName = &target_str; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); status = pNtDeleteKey( key ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( key ); /* symlink loop */ status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) ); status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len + sizeof(targetW) - sizeof(WCHAR) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG, - "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_NAME_TOO_LONG || status == STATUS_INVALID_PARAMETER /* Win10 1607+ */, + "NtOpenKey failed: 0x%08lx\n", status ); attr.Attributes = OBJ_OPENLINK; status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); - ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); pNtClose( key ); status = pNtDeleteKey( link ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( link ); status = pNtDeleteKey( root ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( root ); pRtlFreeHeap(GetProcessHeap(), 0, target); @@ -1346,16 +1360,16 @@ static DWORD get_key_value( HANDLE root, const char *name, DWORD flags ) attr.SecurityQualityOfService = NULL; pRtlCreateUnicodeStringFromAsciiz( &str, name ); - status = pNtCreateKey( &key, flags | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + status = pNtOpenKey( &key, flags | KEY_ALL_ACCESS, &attr ); if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0; - ok( status == STATUS_SUCCESS, "%08x: NtCreateKey failed: 0x%08x\n", flags, status ); + ok( status == STATUS_SUCCESS, "%08lx: NtCreateKey failed: 0x%08lx\n", flags, status ); status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len ); if (status == STATUS_OBJECT_NAME_NOT_FOUND) dw = 0; else { - ok( status == STATUS_SUCCESS, "%08x: NtQueryValueKey failed: 0x%08x\n", flags, status ); + ok( status == STATUS_SUCCESS, "%08lx: NtQueryValueKey failed: 0x%08lx\n", flags, status ); dw = *(DWORD *)info->Data; } pNtClose( key ); @@ -1366,61 +1380,90 @@ static DWORD get_key_value( HANDLE root, const char *name, DWORD flags ) static void _check_key_value( int line, HANDLE root, const char *name, DWORD flags, DWORD expect ) { DWORD dw = get_key_value( root, name, flags ); - ok_(__FILE__,line)( dw == expect, "%08x: wrong value %u/%u\n", flags, dw, expect ); + ok_(__FILE__,line)( dw == expect, "%08lx: wrong value %lu/%lu\n", flags, dw, expect ); } #define check_key_value(root,name,flags,expect) _check_key_value( __LINE__, root, name, flags, expect ) +static void _check_enum_value( int line, const WCHAR *name, DWORD flags, int subkeys, BOOL present) +{ + static const WCHAR wineW[] = {'W','i','n','e'}; + char buffer[1024]; + KEY_BASIC_INFORMATION *basic_info = (KEY_BASIC_INFORMATION *)buffer; + KEY_FULL_INFORMATION *full_info = (KEY_FULL_INFORMATION *)buffer; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + BOOL found; + HANDLE key; + DWORD len; + int i; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &str; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + pRtlInitUnicodeString( &str, name ); + status = pNtOpenKey( &key, flags, &attr ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtQueryKey( key, KeyFullInformation, full_info, sizeof(buffer), &len ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtQueryKey failed: 0x%08lx\n", status ); + ok_( __FILE__, line )( full_info->SubKeys == subkeys, "wrong number of subkeys: %lu\n", full_info->SubKeys ); + subkeys = full_info->SubKeys; + + found = FALSE; + for (i = 0; i < subkeys; i++) + { + status = pNtEnumerateKey( key, i, KeyBasicInformation, basic_info, sizeof(buffer), &len ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtEnumerateKey failed: 0x%08lx\n", status ); + + if (basic_info->NameLength == sizeof(wineW) && !memcmp(basic_info->Name, wineW, sizeof(wineW) )) + found = TRUE; + } + ok_( __FILE__, line )( found == present, "found equals %d\n", found ); + pNtClose( key ); + + status = pNtCreateKey( &key, flags, &attr, 0, 0, 0, 0 ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + status = pNtQueryKey( key, KeyFullInformation, full_info, sizeof(buffer), &len ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtQueryKey failed: 0x%08lx\n", status ); + ok_( __FILE__, line )( full_info->SubKeys == subkeys, "wrong number of subkeys: %lu\n", full_info->SubKeys ); + subkeys = full_info->SubKeys; + + found = FALSE; + for (i = 0; i < subkeys; i++) + { + status = pNtEnumerateKey( key, i, KeyBasicInformation, basic_info, sizeof(buffer), &len ); + ok_( __FILE__, line )( status == STATUS_SUCCESS, "NtEnumerateKey failed: 0x%08lx\n", status ); + + if (basic_info->NameLength == sizeof(wineW) && !memcmp(basic_info->Name, wineW, sizeof(wineW) )) + found = TRUE; + } + ok_( __FILE__, line )( found == present, "found equals %d\n", found ); + pNtClose( key ); +} +#define check_enum_value(name, flags, subkeys, present) _check_enum_value( __LINE__, name, flags, subkeys, present ) + static void test_redirection(void) { - static const WCHAR softwareW[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e',0}; - static const WCHAR wownodeW[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'W','o','w','6','4','3','2','N','o','d','e',0}; - static const WCHAR wine64W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'W','i','n','e',0}; - static const WCHAR wine32W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'W','o','w','6','4','3','2','N','o','d','e','\\', - 'W','i','n','e',0}; - static const WCHAR key64W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'W','i','n','e','\\','W','i','n','e','t','e','s','t',0}; - static const WCHAR key32W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'W','o','w','6','4','3','2','N','o','d','e','\\', - 'W','i','n','e','\\', 'W','i','n','e','t','e','s','t',0}; - static const WCHAR classes64W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'C','l','a','s','s','e','s','\\', - 'W','i','n','e',0}; - static const WCHAR classes32W[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','o','f','t','w','a','r','e','\\', - 'C','l','a','s','s','e','s','\\', - 'W','o','w','6','4','3','2','N','o','d','e','\\', - 'W','i','n','e',0}; NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING str; char buffer[1024]; KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + KEY_FULL_INFORMATION *full_info = (KEY_FULL_INFORMATION *)buffer; DWORD dw, len; - HANDLE key, root32, root64, key32, key64; - BOOL is_vista = FALSE; + HANDLE key, key32, key64, root, root32, root64; + int subkeys64, subkeys32; if (ptr_size != 64) { ULONG is_wow64, len; - if (pNtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information, + if (NtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information, &is_wow64, sizeof(is_wow64), &len ) || !is_wow64) { @@ -1436,199 +1479,177 @@ static void test_redirection(void) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; - pRtlInitUnicodeString( &str, wine64W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wine" ); status = pNtCreateKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + if (status == STATUS_ACCESS_DENIED) + { + skip("Not authorized to modify KEY_WOW64_64KEY, no redirection\n"); + return; + } + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); - pRtlInitUnicodeString( &str, wine32W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wow6432Node\\Wine" ); status = pNtCreateKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); - pRtlInitUnicodeString( &str, key64W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wine\\Winetest" ); status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); - pRtlInitUnicodeString( &str, key32W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest" ); status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); dw = 64; status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); dw = 32; status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); len = sizeof(buffer); status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); dw = *(DWORD *)info->Data; - ok( dw == 32, "wrong value %u\n", dw ); + ok( dw == 32, "wrong value %lu\n", dw ); len = sizeof(buffer); status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08lx\n", status ); dw = *(DWORD *)info->Data; - ok( dw == 64, "wrong value %u\n", dw ); + ok( dw == 64, "wrong value %lu\n", dw ); - pRtlInitUnicodeString( &str, softwareW ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software" ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); - if (ptr_size == 32) - { - /* the Vista mechanism allows opening Wow6432Node from a 32-bit key too */ - /* the new (and simpler) Win7 mechanism doesn't */ - if (get_key_value( key, "Wow6432Node\\Wine\\Winetest", 0 ) == 32) - { - trace( "using Vista-style Wow6432Node handling\n" ); - is_vista = TRUE; - } - check_key_value( key, "Wine\\Winetest", 0, 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 ); - } - else - { - check_key_value( key, "Wine\\Winetest", 0, 64 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 ); - } + check_key_value( key, "Wine\\Winetest", 0, ptr_size ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, ptr_size ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, ptr_size ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, ptr_size == 32 ? 0 : 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, ptr_size == 32 ? 0 : 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, ptr_size == 32 ? 0 : 32 ); pNtClose( key ); - if (ptr_size == 32) - { - status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - dw = get_key_value( key, "Wine\\Winetest", 0 ); - ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 ); - dw = get_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY ); - ok( dw == 32 || broken(dw == 64) /* xp64 */, "wrong value %u\n", dw ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); + status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + dw = get_key_value( key, "Wine\\Winetest", 0 ); + ok( dw == 64 || broken(dw == 32) /* win7 */, "wrong value %lu\n", dw ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, ptr_size ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); + pNtClose( key ); - status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - check_key_value( key, "Wine\\Winetest", 0, 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 ); - check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 ); - pNtClose( key ); - } + status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Wine\\Winetest", 0, ptr_size ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, ptr_size ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, ptr_size ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, ptr_size == 32 ? 0 : 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, ptr_size == 32 ? 0 : 32 ); + check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, ptr_size == 32 ? 0 : 32 ); + pNtClose( key ); check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", 0, ptr_size ); check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", 0, 32 ); - if (ptr_size == 64) - { - /* KEY_WOW64 flags have no effect on 64-bit */ - check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 64 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - } - else - { - check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - } + check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, ptr_size ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - pRtlInitUnicodeString( &str, wownodeW ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wow6432Node" ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); check_key_value( key, "Wine\\Winetest", 0, 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, (ptr_size == 64) ? 32 : (is_vista ? 64 : 32) ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 32 ); check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); pNtClose( key ); - if (ptr_size == 32) - { - status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - dw = get_key_value( key, "Wine\\Winetest", 0 ); - ok( dw == (is_vista ? 64 : 32) || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); + status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Wine\\Winetest", 0, 32 ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); + pNtClose( key ); - status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - check_key_value( key, "Wine\\Winetest", 0, 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); - } + status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Wine\\Winetest", 0, 32 ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 ); + pNtClose( key ); - pRtlInitUnicodeString( &str, wine32W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wow6432Node\\Wine" ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); check_key_value( key, "Winetest", 0, 32 ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, (ptr_size == 32 && is_vista) ? 64 : 32 ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, 32 ); check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); pNtClose( key ); - if (ptr_size == 32) - { - status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - dw = get_key_value( key, "Winetest", 0 ); - ok( dw == 32 || (is_vista && dw == 64), "wrong value %u\n", dw ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); + status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Winetest", 0, 32 ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); + pNtClose( key ); - status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - check_key_value( key, "Winetest", 0, 32 ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); - } + status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Winetest", 0, 32 ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, 32 ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); + pNtClose( key ); - pRtlInitUnicodeString( &str, wine64W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wine" ); status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); check_key_value( key, "Winetest", 0, ptr_size ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : ptr_size ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, ptr_size ); check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size ); pNtClose( key ); - if (ptr_size == 32) - { - status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - dw = get_key_value( key, "Winetest", 0 ); - ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 ); - dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY ); - todo_wine ok( dw == 32, "wrong value %u\n", dw ); - pNtClose( key ); + status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Winetest", 0, 64 ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size ); + pNtClose( key ); - status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - check_key_value( key, "Winetest", 0, 32 ); - check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 ); - check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); - pNtClose( key ); - } + status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + check_key_value( key, "Winetest", 0, ptr_size ); + check_key_value( key, "Winetest", KEY_WOW64_64KEY, ptr_size ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size ); + pNtClose( key ); status = pNtDeleteKey( key32 ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( key32 ); status = pNtDeleteKey( key64 ); - ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); pNtClose( key64 ); + pRtlInitUnicodeString( &str, L"Winetest" ); + attr.RootDirectory = root64; + status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest" ); + attr.RootDirectory = 0; + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + status = pNtDeleteKey( key32 ); + ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08lx\n", status ); + pNtClose( key32 ); + pNtDeleteKey( root32 ); pNtClose( root32 ); pNtDeleteKey( root64 ); @@ -1636,64 +1657,377 @@ static void test_redirection(void) /* Software\Classes is shared/reflected so behavior is different */ - pRtlInitUnicodeString( &str, classes64W ); + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Wine" ); status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); if (status == STATUS_ACCESS_DENIED) { skip("Not authorized to modify the Classes key\n"); return; } - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine" ); + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); - pRtlInitUnicodeString( &str, classes32W ); status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - - dw = 64; - status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); - pNtClose( key64 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); dw = 32; status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); - pNtClose( key32 ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); - pRtlInitUnicodeString( &str, classes64W ); - status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - len = sizeof(buffer); - status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); - dw = *(DWORD *)info->Data; - ok( dw == ptr_size, "wrong value %u\n", dw ); + dw = 64; + status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); - pRtlInitUnicodeString( &str, classes32W ); - status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); - ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status ); - len = sizeof(buffer); - status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len ); - ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status ); - dw = *(DWORD *)info->Data; - ok( dw == 32, "wrong value %u\n", dw ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", 0, 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", KEY_WOW64_32KEY, 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", 0, ptr_size == 64 ? 32 : 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", KEY_WOW64_64KEY, ptr_size == 64 ? 32 : 0 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", KEY_WOW64_32KEY, ptr_size == 64 ? 32 : 64 ); pNtDeleteKey( key32 ); pNtClose( key32 ); + + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", 0, ptr_size == 32 ? 0 : 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", KEY_WOW64_64KEY, ptr_size == 32 ? 0 : 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wine", KEY_WOW64_32KEY, ptr_size == 32 ? 0 : 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", 0, 0 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", KEY_WOW64_64KEY, 0 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine", KEY_WOW64_32KEY, 0 ); + pNtDeleteKey( key64 ); pNtClose( key64 ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes" ); + status = pNtOpenKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtOpenKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + pRtlInitUnicodeString( &str, L"Wine" ); + attr.RootDirectory = root64; + status = pNtCreateKey( &key64, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = key64; + status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + pNtDeleteKey( key ); + pNtClose( key ); + + attr.RootDirectory = root32; + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + status = pNtCreateKey( &key32, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + dw = 32; + status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + dw = 64; + status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + check_key_value( root64, "Wine", 0, 64 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, 64 ); + check_key_value( root32, "Wine", 0, 64 ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, 64 ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); + + check_key_value( root64, "Wine", 0, 0 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, 0 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, 0 ); + check_key_value( root32, "Wine", 0, 0 ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, 0 ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, 0 ); + + pNtDeleteKey( key64 ); + pNtClose( key64 ); + + attr.RootDirectory = root32; + status = pNtCreateKey( &key32, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + dw = 32; + status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + check_key_value( root64, "Wine", 0, 32 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, 32 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, 32 ); + check_key_value( root32, "Wine", 0, 32 ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, 32 ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, 32 ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); + + pNtClose( root64 ); + pNtClose( root32 ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes" ); + attr.RootDirectory = 0; + status = pNtOpenKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtOpenKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtOpenKey( &root, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + pRtlInitUnicodeString( &str, L"Interface" ); + attr.RootDirectory = root64; + status = pNtOpenKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = root32; + status = pNtOpenKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = root; + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + pNtClose( root64 ); + pNtClose( root32 ); + pNtClose( root ); + + root64 = key64; + root32 = key32; + root = key; + + pRtlInitUnicodeString( &str, L"Wine" ); + attr.RootDirectory = root32; + status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = root; + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); + + attr.RootDirectory = root64; + status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = root; + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 32 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + pNtDeleteKey( key64 ); + pNtClose( key64 ); + + pNtDeleteKey( root ); + pNtClose( root ); + + attr.RootDirectory = root64; + status = pNtCreateKey( &key64, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + attr.RootDirectory = root32; + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 32 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 32 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + status = pNtCreateKey( &key32, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + dw = 32; + status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + dw = 64; + status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + check_key_value( root64, "Wine", 0, 64 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, ptr_size ); + check_key_value( root32, "Wine", 0, ptr_size ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, ptr_size ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, ptr_size ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Interface" ); + attr.RootDirectory = 0; + status = pNtOpenKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + check_key_value( key, "Wine", 0, 64 ); + check_key_value( key, "Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( key, "Wine", KEY_WOW64_32KEY, ptr_size ); + pNtClose( key ); + + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + check_key_value( key, "Wine", 0, ptr_size ); + check_key_value( key, "Wine", KEY_WOW64_64KEY, ptr_size ); + check_key_value( key, "Wine", KEY_WOW64_32KEY, ptr_size ); + pNtClose( key ); + + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", 0, ptr_size ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", KEY_WOW64_64KEY, 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", KEY_WOW64_32KEY, ptr_size ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); + + check_key_value( root64, "Wine", 0, ptr_size == 64 ? 0 : 64 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, ptr_size == 64 ? 0 : 64 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, 0 ); + check_key_value( root32, "Wine", 0, 0 ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, 0 ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, 0 ); + + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", 0, 0 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", KEY_WOW64_64KEY, ptr_size == 64 ? 0 : 64 ); + check_key_value( 0, "\\Registry\\Machine\\Software\\Classes\\Interface\\Wine", KEY_WOW64_32KEY, 0 ); + + pNtDeleteKey( key64 ); + pNtClose( key64 ); + + pRtlInitUnicodeString( &str, L"Wine" ); + attr.RootDirectory = root32; + status = pNtCreateKey( &key32, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + dw = 32; + status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); + + check_key_value( root64, "Wine", 0, ptr_size == 64 ? 32 : 0 ); + check_key_value( root64, "Wine", KEY_WOW64_64KEY, ptr_size == 64 ? 32 : 0 ); + check_key_value( root64, "Wine", KEY_WOW64_32KEY, 32 ); + check_key_value( root32, "Wine", 0, 32 ); + check_key_value( root32, "Wine", KEY_WOW64_64KEY, 32 ); + check_key_value( root32, "Wine", KEY_WOW64_32KEY, 32 ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); + + pNtClose( root64 ); + pNtClose( root32 ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node\\Wine" ); + attr.RootDirectory = 0; + status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); + ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status ); + + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + pNtClose( key ); + + status = pNtOpenKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 32 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Wine" ); + status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + status = pNtOpenKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + status = pNtOpenKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == (ptr_size == 64 ? STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS), + "NtOpenKey failed: 0x%08lx\n", status ); + if (!status) pNtClose( key ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node" ); + status = pNtOpenKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtQueryKey( root32, KeyFullInformation, full_info, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS, "NtQueryKey failed: 0x%08lx\n", status ); + ok( full_info->SubKeys > 0, "wrong number of subkeys: %lu\n", full_info->SubKeys ); + subkeys32 = full_info->SubKeys; + pNtClose( root32 ); + + pRtlInitUnicodeString( &str, L"\\Registry\\Machine\\Software\\Classes" ); + status = pNtOpenKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr ); + ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08lx\n", status ); + + status = pNtQueryKey( root64, KeyFullInformation, full_info, sizeof(buffer), &len ); + ok( status == STATUS_SUCCESS, "NtQueryKey failed: 0x%08lx\n", status ); + ok( full_info->SubKeys > subkeys32, "wrong number of subkeys: %lu\n", full_info->SubKeys ); + subkeys64 = full_info->SubKeys; + pNtClose( root64 ); + + check_enum_value( L"\\Registry\\Machine\\Software\\Classes", + KEY_WOW64_32KEY | KEY_ALL_ACCESS, subkeys64, ptr_size == 32 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Classes", + KEY_WOW64_64KEY | KEY_ALL_ACCESS, subkeys64, ptr_size == 32 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Classes", + KEY_ALL_ACCESS, subkeys64, ptr_size == 32 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node", + KEY_WOW64_32KEY | KEY_ALL_ACCESS, subkeys32, ptr_size == 64 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node", + KEY_WOW64_64KEY | KEY_ALL_ACCESS, subkeys32, ptr_size == 64 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Classes\\Wow6432Node", + KEY_ALL_ACCESS, subkeys32, ptr_size == 64 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Wow6432Node\\Classes", + KEY_WOW64_32KEY | KEY_ALL_ACCESS, ptr_size == 32 ? subkeys64 : subkeys32, TRUE ); + check_enum_value( L"\\Registry\\Machine\\Software\\Wow6432Node\\Classes", + KEY_WOW64_64KEY | KEY_ALL_ACCESS, subkeys32, ptr_size == 64 ); + check_enum_value( L"\\Registry\\Machine\\Software\\Wow6432Node\\Classes", + KEY_ALL_ACCESS, ptr_size == 32 ? subkeys64 : subkeys32, TRUE ); + + pNtDeleteKey( key32 ); + pNtClose( key32 ); } static void test_long_value_name(void) { HANDLE key; - NTSTATUS status, expected; + NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING ValName; DWORD i; InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, KEY_WRITE|KEY_READ, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); ValName.MaximumLength = 0xfffc; ValName.Length = ValName.MaximumLength - sizeof(WCHAR); @@ -1703,16 +2037,12 @@ static void test_long_value_name(void) ValName.Buffer[i] = 0; status = pNtDeleteValueKey(key, &ValName); - ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtDeleteValueKey with nonexistent long value name returned 0x%08x\n", status); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtDeleteValueKey with nonexistent long value name returned 0x%08lx\n", status); status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &i, sizeof(i)); - ok(status == STATUS_INVALID_PARAMETER || broken(status == STATUS_SUCCESS) /* nt4 */, - "NtSetValueKey with long value name returned 0x%08x\n", status); - expected = (status == STATUS_SUCCESS) ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND; - status = pNtDeleteValueKey(key, &ValName); - ok(status == expected, "NtDeleteValueKey with long value name returned 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "NtSetValueKey with long value name returned 0x%08lx\n", status); status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, NULL, 0, &i); - ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey with nonexistent long value name returned 0x%08x\n", status); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey with nonexistent long value name returned 0x%08lx\n", status); pRtlFreeUnicodeString(&ValName); pNtClose(key); @@ -1731,7 +2061,7 @@ static void test_NtQueryKey(void) InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, KEY_READ, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); status = pNtQueryKey(key, KeyNameInformation, NULL, 0, &length); if (status == STATUS_INVALID_PARAMETER) { @@ -1739,20 +2069,34 @@ static void test_NtQueryKey(void) pNtClose(key); return; } - todo_wine ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08x\n", status); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08lx\n", status); info = HeapAlloc(GetProcessHeap(), 0, length); /* non-zero buffer size, but insufficient */ + len = 0; + status = pNtQueryKey(key, KeyNameInformation, info, 1, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08lx\n", status); + ok(length == len, "got %ld, expected %ld\n", len, length); + len = 0; + status = pNtQueryKey(key, KeyNameInformation, info, offsetof( KEY_NAME_INFORMATION, Name ) - 1, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08lx\n", status); + ok(length == len, "got %ld, expected %ld\n", len, length); + len = 0; + status = pNtQueryKey(key, KeyNameInformation, info, offsetof( KEY_NAME_INFORMATION, Name ), &len); + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryKey Failed: 0x%08lx\n", status); + ok(length == len, "got %ld, expected %ld\n", len, length); + len = 0; status = pNtQueryKey(key, KeyNameInformation, info, sizeof(*info), &len); - ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryKey Failed: 0x%08x\n", status); - ok(length == len, "got %d, expected %d\n", len, length); - ok(info->NameLength == winetestpath.Length, "got %d, expected %d\n", + ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryKey Failed: 0x%08lx\n", status); + ok(length == len, "got %ld, expected %ld\n", len, length); + ok(info->NameLength == winetestpath.Length, "got %ld, expected %d\n", info->NameLength, winetestpath.Length); /* correct buffer size */ + len = 0; status = pNtQueryKey(key, KeyNameInformation, info, length, &len); - ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status); - ok(length == len, "got %d, expected %d\n", len, length); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); + ok(length == len, "got %ld, expected %ld\n", len, length); str.Buffer = info->Name; str.Length = info->NameLength; @@ -1767,54 +2111,54 @@ static void test_NtQueryKey(void) attr.ObjectName = &str; pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey"); status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0); - ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&str); status = pNtQueryKey(subkey, KeyCachedInformation, &cached_info, sizeof(cached_info), &len); - ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); if (status == STATUS_SUCCESS) { - ok(len == sizeof(cached_info), "got unexpected length %d\n", len); - ok(cached_info.SubKeys == 0, "cached_info.SubKeys = %u\n", cached_info.SubKeys); - ok(cached_info.MaxNameLen == 0, "cached_info.MaxNameLen = %u\n", cached_info.MaxNameLen); - ok(cached_info.Values == 0, "cached_info.Values = %u\n", cached_info.Values); - ok(cached_info.MaxValueNameLen == 0, "cached_info.MaxValueNameLen = %u\n", cached_info.MaxValueNameLen); - ok(cached_info.MaxValueDataLen == 0, "cached_info.MaxValueDataLen = %u\n", cached_info.MaxValueDataLen); - ok(cached_info.NameLength == 22, "cached_info.NameLength = %u\n", cached_info.NameLength); + ok(len == sizeof(cached_info), "got unexpected length %ld\n", len); + ok(cached_info.SubKeys == 0, "cached_info.SubKeys = %lu\n", cached_info.SubKeys); + ok(cached_info.MaxNameLen == 0, "cached_info.MaxNameLen = %lu\n", cached_info.MaxNameLen); + ok(cached_info.Values == 0, "cached_info.Values = %lu\n", cached_info.Values); + ok(cached_info.MaxValueNameLen == 0, "cached_info.MaxValueNameLen = %lu\n", cached_info.MaxValueNameLen); + ok(cached_info.MaxValueDataLen == 0, "cached_info.MaxValueDataLen = %lu\n", cached_info.MaxValueDataLen); + ok(cached_info.NameLength == 22, "cached_info.NameLength = %lu\n", cached_info.NameLength); } attr.RootDirectory = subkey; attr.ObjectName = &str; pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey2"); status = pNtCreateKey(&subkey2, GENERIC_ALL, &attr, 0, 0, 0, 0); - ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&str); pRtlCreateUnicodeStringFromAsciiz(&str, "val"); dw = 64; status = pNtSetValueKey( subkey, &str, 0, REG_DWORD, &dw, sizeof(dw) ); - ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status ); + ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08lx\n", status ); pRtlFreeUnicodeString(&str); status = pNtQueryKey(subkey, KeyCachedInformation, &cached_info, sizeof(cached_info), &len); - ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); if (status == STATUS_SUCCESS) { - ok(len == sizeof(cached_info), "got unexpected length %d\n", len); - ok(cached_info.SubKeys == 1, "cached_info.SubKeys = %u\n", cached_info.SubKeys); - ok(cached_info.MaxNameLen == 24, "cached_info.MaxNameLen = %u\n", cached_info.MaxNameLen); - ok(cached_info.Values == 1, "cached_info.Values = %u\n", cached_info.Values); - ok(cached_info.MaxValueNameLen == 6, "cached_info.MaxValueNameLen = %u\n", cached_info.MaxValueNameLen); - ok(cached_info.MaxValueDataLen == 4, "cached_info.MaxValueDataLen = %u\n", cached_info.MaxValueDataLen); - ok(cached_info.NameLength == 22, "cached_info.NameLength = %u\n", cached_info.NameLength); + ok(len == sizeof(cached_info), "got unexpected length %ld\n", len); + ok(cached_info.SubKeys == 1, "cached_info.SubKeys = %lu\n", cached_info.SubKeys); + ok(cached_info.MaxNameLen == 24, "cached_info.MaxNameLen = %lu\n", cached_info.MaxNameLen); + ok(cached_info.Values == 1, "cached_info.Values = %lu\n", cached_info.Values); + ok(cached_info.MaxValueNameLen == 6, "cached_info.MaxValueNameLen = %lu\n", cached_info.MaxValueNameLen); + ok(cached_info.MaxValueDataLen == 4, "cached_info.MaxValueDataLen = %lu\n", cached_info.MaxValueDataLen); + ok(cached_info.NameLength == 22, "cached_info.NameLength = %lu\n", cached_info.NameLength); } status = pNtDeleteKey(subkey2); - ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status); + ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %lx\n", status); status = pNtDeleteKey(subkey); - ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status); + ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %lx\n", status); pNtClose(subkey2); pNtClose(subkey); @@ -1824,97 +2168,123 @@ static void test_NtQueryKey(void) static void test_notify(void) { OBJECT_ATTRIBUTES attr; - LARGE_INTEGER timeout; + static const LARGE_INTEGER timeout; IO_STATUS_BLOCK iosb; UNICODE_STRING str; - HANDLE key, events[2], subkey; + HANDLE key, key2, events[4], subkey; NTSTATUS status; + unsigned int i; InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, KEY_ALL_ACCESS, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); + status = pNtOpenKey(&key2, KEY_ALL_ACCESS, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); - events[0] = CreateEventW(NULL, FALSE, TRUE, NULL); - ok(events[0] != NULL, "CreateEvent failed: %u\n", GetLastError()); - events[1] = CreateEventW(NULL, FALSE, TRUE, NULL); - ok(events[1] != NULL, "CreateEvent failed: %u\n", GetLastError()); + for (i = 0; i < ARRAY_SIZE(events); ++i) + events[i] = CreateEventW(NULL, TRUE, TRUE, NULL); status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); - status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); + status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); + status = pNtNotifyChangeKey(key2, events[2], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); + status = pNtNotifyChangeKey(key2, events[3], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); - timeout.QuadPart = 0; - status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status); - status = pNtWaitForSingleObject(events[1], FALSE, &timeout); - ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status); + status = WaitForMultipleObjects(4, events, FALSE, 0); + ok(status == WAIT_TIMEOUT, "got %ld\n", status); attr.RootDirectory = key; attr.ObjectName = &str; pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey"); status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0); - ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&str); status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(!status, "got %#lx\n", status); status = pNtWaitForSingleObject(events[1], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(!status, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[2], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[3], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); - status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); - - status = pNtDeleteKey(subkey); - ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); status = pNtWaitForSingleObject(events[1], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(!status, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[2], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[3], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); + + status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); + + status = WaitForMultipleObjects(4, events, FALSE, 0); + ok(status == WAIT_TIMEOUT, "got %ld\n", status); + + status = pNtDeleteKey(subkey); + ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %lx\n", status); + + status = pNtWaitForSingleObject(events[0], FALSE, &timeout); + ok(!status, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[1], FALSE, &timeout); + ok(!status, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[2], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[3], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); pNtClose(subkey); status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); pNtClose(key); status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(!status, "got %#lx\n", status); status = pNtWaitForSingleObject(events[1], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(!status, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[2], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); + status = pNtWaitForSingleObject(events[3], FALSE, &timeout); + ok(status == STATUS_TIMEOUT, "got %#lx\n", status); if (pNtNotifyChangeMultipleKeys) { InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); status = pNtOpenKey(&key, KEY_ALL_ACCESS, &attr); - ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); status = pNtNotifyChangeMultipleKeys(key, 0, NULL, events[0], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE); - ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status); + ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %lx\n", status); - timeout.QuadPart = 0; status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status); + ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %lx\n", status); attr.RootDirectory = key; attr.ObjectName = &str; pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey"); status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0); - ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status); + ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08lx\n", status); pRtlFreeUnicodeString(&str); status = pNtWaitForSingleObject(events[0], FALSE, &timeout); - ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status); + ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %lx\n", status); status = pNtDeleteKey(subkey); - ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status); + ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %lx\n", status); pNtClose(subkey); pNtClose(key); } @@ -1927,24 +2297,808 @@ static void test_notify(void) pNtClose(events[1]); } +static void test_RtlCreateRegistryKey(void) +{ + static WCHAR empty[] = {0}; + static const WCHAR key1[] = {'\\','R','t','l','C','r','e','a','t','e','R','e','g','i','s','t','r','y','K','e','y',0}; + UNICODE_STRING str; + SIZE_T size; + NTSTATUS status; + + RtlDuplicateUnicodeString(1, &winetestpath, &str); + size = str.MaximumLength + sizeof(key1)* sizeof(WCHAR) * 2; + str.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, str.Buffer, size); + str.MaximumLength = size; + pRtlAppendUnicodeToString(&str, key1); + pRtlAppendUnicodeToString(&str, key1); + + /* should work */ + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, winetestpath.Buffer); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER, NULL); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER | RTL_REGISTRY_OPTIONAL, NULL); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER, empty); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER | RTL_REGISTRY_OPTIONAL, empty); + ok(status == STATUS_SUCCESS, "RtlCreateRegistryKey failed: %08lx\n", status); + + /* invalid first parameter */ + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER+1, winetestpath.Buffer); + ok(status == STATUS_INVALID_PARAMETER, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_INVALID_PARAMETER); + + status = pRtlCreateRegistryKey((RTL_REGISTRY_USER+1) | RTL_REGISTRY_OPTIONAL, winetestpath.Buffer); + ok(status == STATUS_INVALID_PARAMETER, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_INVALID_PARAMETER); + + /* invalid second parameter */ + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, NULL); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_PATH_SYNTAX_BAD); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, NULL); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_PATH_SYNTAX_BAD); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, empty); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_PATH_SYNTAX_BAD); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, empty); + ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_PATH_SYNTAX_BAD); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, str.Buffer); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_NAME_NOT_FOUND); + + status = pRtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, str.Buffer); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_OBJECT_NAME_NOT_FOUND); + + /* both parameters invalid */ + status = pRtlCreateRegistryKey(RTL_REGISTRY_USER+1, NULL); + ok(status == STATUS_INVALID_PARAMETER, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_INVALID_PARAMETER); + + status = pRtlCreateRegistryKey((RTL_REGISTRY_USER+1) | RTL_REGISTRY_OPTIONAL, NULL); + ok(status == STATUS_INVALID_PARAMETER, "RtlCreateRegistryKey unexpected return value: %08lx, expected %08lx\n", status, STATUS_INVALID_PARAMETER); + + pRtlFreeUnicodeString(&str); +} + +static void test_NtRenameKey(void) +{ + KEY_NAME_INFORMATION *info = NULL; + UNICODE_STRING str, str2; + OBJECT_ATTRIBUTES attr; + HANDLE key, subkey; + char buffer[200]; + NTSTATUS status; + DWORD size; + + status = NtRenameKey(NULL, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#lx.\n", status); + + InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); + status = pNtCreateKey(&key, KEY_READ|DELETE, &attr, 0, 0, 0, 0); + ok(!status, "Unexpected status %#lx.\n", status); + + attr.RootDirectory = key; + attr.ObjectName = &str; + + pRtlCreateUnicodeStringFromAsciiz(&str, "rename_subkey"); + status = pNtCreateKey(&subkey, KEY_READ|DELETE, &attr, 0, 0, 0, 0); + ok(!status, "Unexpected status %#lx.\n", status); + + memset(&str2, 0, sizeof(str2)); + status = NtRenameKey(subkey, &str2); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); + + pRtlCreateUnicodeStringFromAsciiz(&str2, "renamed_subkey"); + + status = NtRenameKey(subkey, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#lx.\n", status); + status = NtRenameKey(NULL, &str); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#lx.\n", status); + + status = NtRenameKey(subkey, &str2); + ok(status == STATUS_ACCESS_DENIED, "Unexpected status %#lx.\n", status); + pNtClose(subkey); + + status = pNtCreateKey(&subkey, KEY_WRITE|DELETE, &attr, 0, 0, 0, 0); + ok(!status, "Unexpected status %#lx.\n", status); + /* Rename to itself. */ + status = NtRenameKey(subkey, &str); + ok(status == STATUS_CANNOT_DELETE, "Unexpected status %#lx.\n", status); + status = NtRenameKey(subkey, &str2); + ok(!status, "Unexpected status %#lx.\n", status); + + pRtlFreeUnicodeString(&str2); + pRtlFreeUnicodeString(&str); + + info = (KEY_NAME_INFORMATION *)buffer; + status = pNtQueryKey(subkey, KeyNameInformation, info, sizeof(buffer), &size); + ok(!status, "Unexpected status %#lx.\n", status); + if (status == STATUS_SUCCESS) + { + info->Name[info->NameLength/sizeof(WCHAR)] = 0; + ok(!!wcsstr(info->Name, L"renamed_subkey"), "Unexpected subkey name %s.\n", wine_dbgstr_w(info->Name)); + } + + pNtDeleteKey(subkey); + pNtDeleteKey(key); + pNtClose(subkey); + pNtClose(key); +} + +static BOOL set_privileges(LPCSTR privilege, BOOL set) +{ + TOKEN_PRIVILEGES tp; + HANDLE hToken; + LUID luid; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + return FALSE; + + if(!LookupPrivilegeValueA(NULL, privilege, &luid)) + { + CloseHandle(hToken); + return FALSE; + } + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + + if (set) + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + if (GetLastError() != ERROR_SUCCESS) + { + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + return TRUE; +} + +static void test_NtRegLoadKeyEx(void) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES file_attr, key_attr; + WCHAR temp_path[MAX_PATH], hivefile_path[MAX_PATH]; + UNICODE_STRING hivefile_pathW, key_pathW; + HANDLE key = 0; + + GetTempPathW(ARRAY_SIZE(temp_path), temp_path); + GetTempFileNameW(temp_path, L"key", 0, hivefile_path); + DeleteFileW(hivefile_path); + RtlDosPathNameToNtPathName_U(hivefile_path, &hivefile_pathW, NULL, NULL); + + if (!set_privileges(SE_RESTORE_NAME, TRUE) || + !set_privileges(SE_BACKUP_NAME, TRUE)) + { + win_skip("Failed to set SE_RESTORE_NAME and SE_BACKUP_NAME privileges, skipping tests\n"); + RtlFreeUnicodeString(&hivefile_pathW); + return; + } + + /* Generate hive file */ + InitializeObjectAttributes(&key_attr, &winetestpath, 0, NULL, NULL); + status = pNtCreateKey(&key, KEY_ALL_ACCESS, &key_attr, 0, 0, 0, 0); + ok(status == ERROR_SUCCESS, "couldn't create key 0x%lx\n", status); + status = RegSaveKeyW(key, hivefile_path, NULL); + ok(status == ERROR_SUCCESS, "couldn't save key %ld\n", status); + status = pNtDeleteKey(key); + ok(status == ERROR_SUCCESS, "couldn't delete key 0x%lx\n", status); + key = 0; + + /* Test for roothandle parameter with no flags */ + pRtlFormatCurrentUserKeyPath(&key_pathW); + key_pathW.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, key_pathW.Buffer, + key_pathW.MaximumLength + sizeof(key_pathW)*sizeof(WCHAR)); + key_pathW.MaximumLength = key_pathW.MaximumLength + sizeof(key_pathW)*sizeof(WCHAR); + pRtlAppendUnicodeToString(&key_pathW, L"TestKey"); + + InitializeObjectAttributes(&file_attr, &hivefile_pathW, 0, NULL, NULL); + key_attr.ObjectName = &key_pathW; + status = pNtLoadKeyEx(&key_attr, &file_attr, 0, NULL, NULL, KEY_READ, &key, NULL); + todo_wine ok(status == STATUS_INVALID_PARAMETER_7 || broken(status == STATUS_INVALID_PARAMETER_6) /* win7 */, "got 0x%lx\n", status); + if (status == STATUS_INVALID_PARAMETER_6) + { + win_skip("NtLoadKeyEx has a different order of parameters in this windows version\n"); + RtlFreeUnicodeString(&hivefile_pathW); + RtlFreeUnicodeString(&key_pathW); + DeleteFileW(hivefile_path); + return; + } + ok(!key, "key is expected to be null\n"); + if (key) pNtClose(key); + RtlFreeUnicodeString(&key_pathW); + + /* Test for roothandle parameter with REG_APP_HIVE */ + RtlCreateUnicodeString(&key_pathW, L"\\REGISTRY\\A\\TestKey"); + status = pNtLoadKeyEx(&key_attr, &file_attr, REG_APP_HIVE, NULL, NULL, KEY_READ, &key, NULL); + todo_wine ok(status == STATUS_SUCCESS, "got 0x%lx\n", status); + todo_wine ok(key != NULL, "key is null\n"); + if (key) pNtClose(key); + RtlFreeUnicodeString(&key_pathW); + + set_privileges(SE_RESTORE_NAME, FALSE); + set_privileges(SE_BACKUP_NAME, FALSE); + RtlFreeUnicodeString(&hivefile_pathW); + DeleteFileW(hivefile_path); +} + +struct query_reg_values_test +{ + RTL_QUERY_REGISTRY_TABLE query_table[3]; + NTSTATUS expected_ret; + unsigned int expected_calls; + enum + { + SKIP_NAME_CHECK = 0x1, + SKIP_DATA_CHECK = 0x2, + EXPECT_DEFAULT_DATA = 0x4, + SPLIT_MULTI = 0x8, + WINE_TODO_RET = 0x10, + WINE_TODO_CALLS = 0x20, + WINE_TODO_NAME = 0x40, + WINE_TODO_TYPE = 0x80, + WINE_TODO_SIZE = 0x100, + } + flags; + ULONG expected_type; + const WCHAR *expected_data; + ULONG expected_data_size; + ULONG size_limit; +}; + +#if !defined(__REACTOS__) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_WIN7) +static unsigned int query_routine_calls; + +static NTSTATUS WINAPI query_routine(const WCHAR *value_name, ULONG value_type, void *value_data, ULONG value_data_size, + void *context, void *entry_context) +{ + struct query_reg_values_test *test = context; + RTL_QUERY_REGISTRY_TABLE *query = entry_context; + const WCHAR *expected_data; + ULONG expected_size; + ULONG expected_type; + + trace("Value name: %s\n", debugstr_w(value_name)); + trace("Value data: %s\n", debugstr_w(value_data)); + + if (!(test->flags & SKIP_NAME_CHECK)) + { + todo_wine_if(test->flags & WINE_TODO_NAME) + if (query->Name) + ok(!wcscmp(value_name, query->Name), "Expected name %s, got %s\n", debugstr_w(query->Name), debugstr_w(value_name)); + else + ok(!value_name, "Expected null name\n"); + } + + if (!(test->flags & SKIP_DATA_CHECK) && query_routine_calls < test->expected_calls) + { + if (test->flags & EXPECT_DEFAULT_DATA) + { + expected_type = query->DefaultType; + expected_data = query->DefaultData; + expected_size = query->DefaultLength; + } + else + { + expected_type = test->expected_type; + expected_data = test->expected_data; + expected_size = test->expected_data_size; + } + + if (test->flags & SPLIT_MULTI) + { + expected_type = REG_SZ; + for (int i = 0; i < query_routine_calls; i++) + expected_data = wcschr(expected_data, '\0') + 1; + expected_size = 0; + } + + if (!expected_size && expected_data && (expected_type == REG_SZ || expected_type == REG_EXPAND_SZ)) + expected_size = (wcslen(expected_data) + 1) * sizeof(WCHAR); + + todo_wine_if(test->flags & WINE_TODO_TYPE) + ok(value_type == expected_type, "Expected type %lu, got %lu\n", expected_type, value_type); + + todo_wine_if(test->flags & WINE_TODO_SIZE) + ok(value_data_size == expected_size, "Expected size %lu, got %lu\n", expected_size, value_data_size); + + if (expected_data == query->DefaultData || expected_data == NULL) + ok(value_data == expected_data, "Expected data %p, got %p\n", expected_data, value_data); + else + ok(!memcmp(value_data, expected_data, value_data_size), + "Expected data %s, got %s\n", debugstr_w(expected_data), debugstr_w(value_data)); + } + + query_routine_calls++; + + return STATUS_SUCCESS; +} + +static WCHAR query_reg_values_direct_str_buf[32]; +static UNICODE_STRING query_reg_values_direct_str = {0, 0, query_reg_values_direct_str_buf}; + +static ULONG query_reg_values_direct_int; + +static union +{ + ULONG size; + char data[32]; +} +query_reg_values_direct_sized; + +static struct +{ + ULONG size; + ULONG type; + char data[32]; +} +query_reg_values_direct_typed; + +static struct query_reg_values_test query_reg_values_tests[] = +{ + /* Empty table */ + { + {{ NULL }}, + STATUS_SUCCESS, 0 + }, + /* Name without query routine or DIRECT */ + { + {{ NULL, 0, (WCHAR*)L"WindowsDrive" }}, + STATUS_SUCCESS, 0 + }, + { + {{ NULL, 0, (WCHAR*)L"I don't exist", NULL, REG_SZ, (WCHAR*)L"Some default" }}, + STATUS_SUCCESS, 0 + }, + /* The query routine is called for every value in current key */ + { + {{ query_routine }}, + STATUS_SUCCESS, 6, SKIP_NAME_CHECK | SKIP_DATA_CHECK + }, + /* NOVALUE is ignored when the name is not null */ + { + {{ query_routine, RTL_QUERY_REGISTRY_NOVALUE, (WCHAR*)L"WindowsDrive" }}, + STATUS_SUCCESS, 1, WINE_TODO_TYPE | WINE_TODO_SIZE, REG_SZ, L"C:" + }, + /* NOVALUE calls the callback without enumerating any values */ + { + { + { query_routine, RTL_QUERY_REGISTRY_NOVALUE }, + { query_routine, RTL_QUERY_REGISTRY_NOVALUE, NULL, NULL, REG_SZ, (WCHAR*)L"Some default" }, + }, + STATUS_SUCCESS, 2 + }, + /* DIRECT doesn't call the query routine and reads directly into a buffer */ + { + {{ query_routine, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"WindowsDrive", &query_reg_values_direct_str }}, + STATUS_INVALID_PARAMETER, 0, 0, REG_NONE, NULL, -1 + }, + { + {{ query_routine, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_SZ, (WCHAR*)L"Some default" }}, + STATUS_INVALID_PARAMETER, 0, 0, REG_NONE, NULL, -1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"WindowsDrive", &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"C:" + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"WindowsDrive", &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2323", 0, 2 * sizeof(WCHAR) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"WindowsDrive", + &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"%SYSTEMDRIVE%" + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"WindowsDrive", + &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2323", 0, 2 * sizeof(WCHAR) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"MeaningOfLife32", &query_reg_values_direct_int }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2a" + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"MeaningOfLife64", &query_reg_values_direct_sized }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2a\0\0", sizeof(UINT64) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"MeaningOfLife64", &query_reg_values_direct_sized }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\xff", 1, 1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"MeaningOfLife64", &query_reg_values_direct_typed }}, + STATUS_SUCCESS, 0, 0, REG_QWORD, L"\x2a\0\0", sizeof(UINT64) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"MeaningOfLife64", &query_reg_values_direct_typed }}, + STATUS_SUCCESS, 0, 0, 0x23, L"\x23", 1, 1 + }, + /* DIRECT on a multi-string crashes on Windows without NOEXPAND */ + /* { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"CapitalsOfEurope", &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, WINE_TODO_RET + }, */ + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"CapitalsOfEurope", + &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"Brussels\0Paris\0%PATH%\0", sizeof(L"Brussels\0Paris\0%PATH%\0") + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"CapitalsOfEurope", + &query_reg_values_direct_str }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2323", 0, 2 * sizeof(WCHAR) + }, + /* DIRECT with a null buffer crashes on Windows */ + /* { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"WindowsDrive", NULL }}, + STATUS_INVALID_PARAMETER + }, */ + /* SUBKEY changes the current path on the fly */ + { + {{ query_routine, RTL_QUERY_REGISTRY_SUBKEY, (WCHAR*)L"subkey" }}, + STATUS_SUCCESS, 1, SKIP_NAME_CHECK | WINE_TODO_RET | WINE_TODO_CALLS, REG_SZ, L"Yellow" + }, + { + { + { NULL, RTL_QUERY_REGISTRY_SUBKEY, (WCHAR*)L"subkey" }, + { query_routine, 0, (WCHAR*)L"Color" }, + }, + STATUS_SUCCESS, 1, WINE_TODO_RET | WINE_TODO_CALLS, REG_SZ, L"Yellow" + }, + /* NOEXPAND disables variable expansion */ + { + {{ query_routine, RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"WindowsDrive" }}, + STATUS_SUCCESS, 1, 0, REG_EXPAND_SZ, L"%SYSTEMDRIVE%" + }, + /* NOEXPAND calls the query routine only once instead of once for each string in a multi-string */ + { + {{ query_routine, 0, (WCHAR*)L"CapitalsOfEurope" }}, + STATUS_SUCCESS, 3, SPLIT_MULTI, REG_SZ, L"Brussels\0Paris\0%PATH%\0" + }, + { + {{ query_routine, RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"CapitalsOfEurope" }}, + STATUS_SUCCESS, 1, 0, REG_MULTI_SZ, L"Brussels\0Paris\0%PATH%\0", sizeof(L"Brussels\0Paris\0%PATH%\0") + }, + /* The default value is used if the registry value does not exist */ + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_SZ, (WCHAR*)L"Some default", 4 * sizeof (WCHAR) }}, + STATUS_SUCCESS, 1, 0, REG_SZ, L"Some", 4 * sizeof(WCHAR) + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 1, EXPECT_DEFAULT_DATA + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_EXPAND_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 1, 0, REG_SZ, L"C:" + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_MULTI_SZ, (WCHAR*)L"Brussels\0Paris\0%PATH%\0" }}, + STATUS_SUCCESS, 3, EXPECT_DEFAULT_DATA | SPLIT_MULTI + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", + NULL, REG_MULTI_SZ, (WCHAR*)L"A\0B\0C", sizeof(L"A\0B\0C") }}, + STATUS_SUCCESS, 2, EXPECT_DEFAULT_DATA | SPLIT_MULTI + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", + NULL, REG_MULTI_SZ, (WCHAR*)L"A\0B\0C", sizeof(L"A\0B\0C") - sizeof(L'\0') }}, + STATUS_SUCCESS, 2, EXPECT_DEFAULT_DATA | SPLIT_MULTI + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_DWORD, (WCHAR*)0xdeadbeef }}, + STATUS_SUCCESS, 1, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_SZ, (WCHAR*)L"Some default", 4 * sizeof(WCHAR) }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2323", 0, 2 * sizeof(WCHAR) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_EXPAND_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"C:" + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_EXPAND_SZ, (WCHAR*)L"%SYSTEMDRIVE%" }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\x2323", 0, 2 * sizeof(WCHAR) + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_int, REG_DWORD, (WCHAR*)0xdeadbeef }}, + STATUS_SUCCESS, 0 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_int, REG_DWORD, (WCHAR*)L"\x2a", sizeof(DWORD) }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_sized, REG_DWORD, (WCHAR*)L"Some default", sizeof(L"Some default") }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_sized, REG_DWORD, (WCHAR*)L"Some default", sizeof(L"Some default") }}, + STATUS_SUCCESS, 0, 0, REG_NONE, L"\xff", 1, 1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_typed, REG_NONE, (WCHAR*)L"Some default", sizeof(L"Some default") }}, + STATUS_SUCCESS, 0, WINE_TODO_TYPE | WINE_TODO_SIZE, 0x23, NULL, -1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_typed, REG_QWORD, (WCHAR*)L"Some default", sizeof(L"Some default") }}, + STATUS_SUCCESS, 0, WINE_TODO_TYPE | WINE_TODO_SIZE, 0x23, NULL, -1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_typed, REG_QWORD, (WCHAR*)L"\x2a\0\0", sizeof(UINT64) }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_typed, REG_QWORD, (WCHAR*)L"\x2a\0\0", sizeof(UINT64) }}, + STATUS_SUCCESS, 0, 0, 0x23, L"\x23", 1, 1 + }, + /* DIRECT with a multi-string default value crashes on Windows without NOEXPAND */ + /* { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_MULTI_SZ, (WCHAR*)L"A\0B\0C\0", sizeof(L"A\0B\0C\0") }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, */ + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_MULTI_SZ, (WCHAR*)L"A\0B\0C", sizeof(L"A\0B\0C") - sizeof(L'\0') }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA + }, + /* The default value is not used if it is not valid */ + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_SZ }}, + STATUS_DATA_OVERRUN, 0, EXPECT_DEFAULT_DATA + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_NONE, (WCHAR*)L"Some default" }}, + STATUS_SUCCESS, 0, EXPECT_DEFAULT_DATA | WINE_TODO_CALLS | WINE_TODO_SIZE + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_SZ }}, + STATUS_DATA_OVERRUN, 0, 0, REG_NONE, NULL, -1 + }, + { + {{ NULL, RTL_QUERY_REGISTRY_DIRECT, (WCHAR*)L"I don't exist", + &query_reg_values_direct_str, REG_NONE, (WCHAR*)L"Some default" }}, + STATUS_SUCCESS, 0, 0, REG_NONE, NULL, -1 + }, + /* REQUIRED fails if the value doesn't exist and there is no default */ + { + {{ query_routine, RTL_QUERY_REGISTRY_REQUIRED, (WCHAR*)L"I don't exist", + NULL, REG_SZ, (WCHAR*)L"Some default" }}, + STATUS_SUCCESS, 1, EXPECT_DEFAULT_DATA | WINE_TODO_RET | WINE_TODO_CALLS + }, + { + {{ query_routine, RTL_QUERY_REGISTRY_REQUIRED, (WCHAR*)L"I don't exist", + NULL, REG_NONE, (WCHAR*)L"Some default" }}, + STATUS_OBJECT_NAME_NOT_FOUND + }, + /* DELETE deletes the value after reading it */ + { + {{ query_routine, RTL_QUERY_REGISTRY_DELETE, (WCHAR*)L"WindowsDrive" }}, + STATUS_SUCCESS, 1, 0, REG_SZ, L"C:" + }, + { + {{ query_routine, 0, (WCHAR*)L"I don't exist", NULL, REG_SZ, (WCHAR*)L"Some default" }}, + STATUS_SUCCESS, 1, EXPECT_DEFAULT_DATA + }, +}; + +static void test_RtlQueryRegistryValues(void) +{ + NTSTATUS status; + unsigned int i; + + status = RegSetKeyValueW(HKEY_CURRENT_USER, L"WineTest", L"WindowsDrive", REG_EXPAND_SZ, + L"%SYSTEMDRIVE%", sizeof(L"%SYSTEMDRIVE%")); + ok(status == ERROR_SUCCESS, "Failed to create registry value WindowsDrive: %lu\n", status); + + status = RegSetKeyValueW(HKEY_CURRENT_USER, L"WineTest", L"CapitalsOfEurope", REG_MULTI_SZ, + L"Brussels\0Paris\0%PATH%", sizeof(L"Brussels\0Paris\0%PATH%") - sizeof(L'\0')); + ok(status == ERROR_SUCCESS, "Failed to create registry value CapitalsOfEurope: %lu\n", status); + + status = RegSetKeyValueW(HKEY_CURRENT_USER, L"WineTest", L"MeaningOfLife32", REG_DWORD, + L"\x2a", sizeof(DWORD)); + ok(status == ERROR_SUCCESS, "Failed to create registry value MeaningOfLife32: %lu\n", status); + + status = RegSetKeyValueW(HKEY_CURRENT_USER, L"WineTest", L"MeaningOfLife64", REG_QWORD, + L"\x2a\0\0", sizeof(UINT64)); + ok(status == ERROR_SUCCESS, "Failed to create registry value MeaningOfLife64: %lu\n", status); + + status = RegSetKeyValueW(HKEY_CURRENT_USER, L"WineTest\\subkey", L"Color", REG_SZ, + L"Yellow", sizeof(L"Yellow")); + ok(status == ERROR_SUCCESS, "Failed to create registry value Color: %lu\n", status); + + for (i = 0; i < ARRAY_SIZE(query_reg_values_tests); i++) + { + struct query_reg_values_test *test = &query_reg_values_tests[i]; + RTL_QUERY_REGISTRY_TABLE *query; + const WCHAR *expected_data; + ULONG expected_size; + ULONG expected_type; + + winetest_push_context("%u/%Iu", i, ARRAY_SIZE(query_reg_values_tests) - 1); + + for (query = test->query_table; query->QueryRoutine || query->Name; query++) + { + if (!(query->Flags & RTL_QUERY_REGISTRY_DIRECT)) + query->EntryContext = query; + } + + query_routine_calls = 0; + + query_reg_values_direct_str.MaximumLength = test->size_limit ? test->size_limit + : sizeof(query_reg_values_direct_str_buf); + if (query_reg_values_direct_str.MaximumLength >= sizeof(WCHAR)) + query_reg_values_direct_str.Length = query_reg_values_direct_str.MaximumLength - sizeof(WCHAR); + else + query_reg_values_direct_str.Length = 0; + memset(query_reg_values_direct_str.Buffer, 0x23, query_reg_values_direct_str.Length); + query_reg_values_direct_str.Buffer[query_reg_values_direct_str.Length / sizeof(WCHAR)] = 0; + + query_reg_values_direct_int = 1; + + memset(query_reg_values_direct_sized.data, 0x23, sizeof(query_reg_values_direct_sized)); + query_reg_values_direct_sized.size = test->size_limit ? -test->size_limit + : -(LONG)sizeof(query_reg_values_direct_sized); + + query_reg_values_direct_typed.size = test->size_limit ? test->size_limit + : sizeof(query_reg_values_direct_typed.data); + query_reg_values_direct_typed.type = 0x23; + memset(query_reg_values_direct_typed.data, 0x23, sizeof(query_reg_values_direct_typed.data)); + + status = pRtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer, test->query_table, test, NULL); + + todo_wine_if(test->flags & WINE_TODO_RET) + ok(status == test->expected_ret, "Expected RtlQueryRegistryValues to return 0x%08lx, got 0x%08lx\n", + test->expected_ret, status); + + todo_wine_if(test->flags & WINE_TODO_CALLS) + ok(query_routine_calls == test->expected_calls, "Expected %u calls to QueryRoutine, got %u\n", + test->expected_calls, query_routine_calls); + + for (query = test->query_table; query->QueryRoutine || query->Name; query++) + { + if ((query->Flags & RTL_QUERY_REGISTRY_DIRECT) && query->EntryContext) + { + if (test->flags & EXPECT_DEFAULT_DATA) + { + expected_type = query->DefaultType; + expected_data = query->DefaultData; + expected_size = query->DefaultLength; + } + else + { + expected_type = test->expected_type; + expected_data = test->expected_data; + expected_size = test->expected_data_size; + } + + if (query->EntryContext == &query_reg_values_direct_str) + { + if (!expected_size && expected_data) + expected_size = (wcslen(expected_data) + 1) * sizeof(WCHAR); + else if (expected_size == -1) + expected_size = query_reg_values_direct_str.MaximumLength; + + todo_wine_if(test->flags & WINE_TODO_SIZE) + ok(query_reg_values_direct_str.Length + sizeof(WCHAR) == expected_size, + "Expected size %lu, got %Iu\n", expected_size, + query_reg_values_direct_str.Length + sizeof(WCHAR)); + + if (expected_data) + { + ok(!memcmp(query_reg_values_direct_str.Buffer, expected_data, expected_size), + "Expected data %s, got %s\n", debugstr_w(expected_data), + debugstr_w(query_reg_values_direct_str.Buffer)); + } + } + else if (query->EntryContext == &query_reg_values_direct_int) + { + if (expected_data) + { + ok(!memcmp(&query_reg_values_direct_int, expected_data, expected_size), + "Data does not match\n"); + } + else + { + ok(query_reg_values_direct_int == 1, + "Expected data to not change, got %lu\n", query_reg_values_direct_int); + } + } + else if (query->EntryContext == &query_reg_values_direct_sized) + { + ok(!memcmp(query_reg_values_direct_sized.data, expected_data, expected_size), + "Data does not match\n"); + } + else if (query->EntryContext == &query_reg_values_direct_typed) + { + if (expected_size == -1) + expected_size = sizeof(query_reg_values_direct_typed.data); + + todo_wine_if(test->flags & WINE_TODO_SIZE) + ok(query_reg_values_direct_typed.size == expected_size, + "Expected size %lu, got %lu\n", expected_size, query_reg_values_direct_typed.size); + + todo_wine_if(test->flags & WINE_TODO_TYPE) + ok(query_reg_values_direct_typed.type == expected_type, + "Expected type %lu, got %lu\n", expected_type, query_reg_values_direct_typed.type); + + if (expected_data) + { + ok(!memcmp(query_reg_values_direct_typed.data, expected_data, expected_size), + "Data does not match\n"); + } + } + } + } + + winetest_pop_context(); + } + + status = RegDeleteKeyValueW(HKEY_CURRENT_USER, L"WineTest", L"WindowsDrive"); + ok(status == ERROR_FILE_NOT_FOUND, "Registry value WindowsDrive should have been deleted already\n"); +} +#endif + START_TEST(reg) { - static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0}; +#if !defined(__REACTOS__) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_WIN7) + LSTATUS status; +#endif + if(!InitFunctionPtrs()) return; - pRtlFormatCurrentUserKeyPath(&winetestpath); - winetestpath.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer, - winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR)); - winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR); - pRtlAppendUnicodeToString(&winetestpath, winetest); + pRtlFormatCurrentUserKeyPath(&winetestpath); + winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(L"\\WineTest"); + winetestpath.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer, + winetestpath.MaximumLength); + pRtlAppendUnicodeToString(&winetestpath, L"\\WineTest"); test_NtCreateKey(); test_NtOpenKey(); test_NtSetValueKey(); test_RtlCheckRegistryKey(); test_RtlOpenCurrentUser(); - test_RtlQueryRegistryValues(); test_RtlpNtQueryValueKey(); test_NtFlushKey(); test_NtQueryKey(); @@ -1952,9 +3106,18 @@ START_TEST(reg) test_NtQueryValueKey(); test_long_value_name(); test_notify(); + test_RtlCreateRegistryKey(); test_NtDeleteKey(); test_symlinks(); test_redirection(); + test_NtRenameKey(); + test_NtRegLoadKeyEx(); +#if !defined(__REACTOS__) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_WIN7) + test_RtlQueryRegistryValues(); + + status = RegDeleteTreeW(HKEY_CURRENT_USER, L"WineTest"); + ok(status == ERROR_SUCCESS, "Failed to delete the WineTest registry key: %lu\n", status); +#endif pRtlFreeUnicodeString(&winetestpath); diff --git a/modules/rostests/winetests/ntdll/ros_diff.patch b/modules/rostests/winetests/ntdll/ros_diff.patch deleted file mode 100644 index a46b1c3d9c3..00000000000 --- a/modules/rostests/winetests/ntdll/ros_diff.patch +++ /dev/null @@ -1,69 +0,0 @@ -Index: generated.c -=================================================================== ---- generated.c (Revision 48052) -+++ generated.c (Arbeitskopie) -@@ -442,6 +442,7 @@ - - static void test_pack_FLOATING_SAVE_AREA(void) - { -+#if 0 - /* FLOATING_SAVE_AREA */ - TEST_TYPE_SIZE (FLOATING_SAVE_AREA, 112) - TEST_TYPE_ALIGN (FLOATING_SAVE_AREA, 4) -@@ -472,6 +473,7 @@ - TEST_FIELD_SIZE (FLOATING_SAVE_AREA, Cr0NpxState, 4) - TEST_FIELD_ALIGN (FLOATING_SAVE_AREA, Cr0NpxState, 4) - TEST_FIELD_OFFSET(FLOATING_SAVE_AREA, Cr0NpxState, 108) -+#endif - } - - static void test_pack_FPO_DATA(void) -@@ -875,7 +877,7 @@ - static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY(void) - { - /* IMAGE_LOAD_CONFIG_DIRECTORY */ -- TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 88) -+// TEST_TYPE_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, 88) - TEST_TYPE_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, 8) - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, Size, 4) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, Size, 4) -@@ -898,6 +900,7 @@ - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, CriticalSectionDefaultTimeout, 4) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, CriticalSectionDefaultTimeout, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY, CriticalSectionDefaultTimeout, 20) -+#if 0 // wine headers are broken - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, DeCommitFreeBlockThreshold, 4) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, DeCommitFreeBlockThreshold, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY, DeCommitFreeBlockThreshold, 24) -@@ -937,6 +940,7 @@ - TEST_FIELD_SIZE (IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerCount, 4) - TEST_FIELD_ALIGN (IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerCount, 4) - TEST_FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerCount, 80) -+#endif - } - - static void test_pack_IMAGE_NT_HEADERS(void) -@@ -1980,11 +1984,13 @@ - - static void test_pack_PFLOATING_SAVE_AREA(void) - { -+#if 0 - /* PFLOATING_SAVE_AREA */ - TEST_TYPE_SIZE (PFLOATING_SAVE_AREA, 8) - TEST_TYPE_ALIGN (PFLOATING_SAVE_AREA, 8) - TEST_TARGET_SIZE (PFLOATING_SAVE_AREA, 112) - TEST_TARGET_ALIGN(PFLOATING_SAVE_AREA, 4) -+#endif - } - - static void test_pack_PFPO_DATA(void) -@@ -2157,7 +2163,9 @@ - /* PIMAGE_LOAD_CONFIG_DIRECTORY */ - TEST_TYPE_SIZE (PIMAGE_LOAD_CONFIG_DIRECTORY, 8) - TEST_TYPE_ALIGN (PIMAGE_LOAD_CONFIG_DIRECTORY, 8) -+#if 0// wine headers are broken - TEST_TARGET_SIZE (PIMAGE_LOAD_CONFIG_DIRECTORY, 88) -+#endif - TEST_TARGET_ALIGN(PIMAGE_LOAD_CONFIG_DIRECTORY, 8) - } - diff --git a/modules/rostests/winetests/ntdll/rtl.c b/modules/rostests/winetests/ntdll/rtl.c index 347474cac50..29e3d464685 100644 --- a/modules/rostests/winetests/ntdll/rtl.c +++ b/modules/rostests/winetests/ntdll/rtl.c @@ -22,16 +22,33 @@ */ #include +#include -#include "ntdll_test.h" -#include "inaddr.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" #include "in6addr.h" -#include "initguid.h" -#define COBJMACROS -#ifdef __REACTOS__ -#include +#include "inaddr.h" +#include "ip2string.h" +#ifndef __REACTOS__ +#include "ddk/ntifs.h" +#else +#define FASTCALL __fastcall +NTSYSAPI +SIZE_T +NTAPI +RtlCompareMemoryUlong( + _In_ PVOID Source, + _In_ SIZE_T Length, + _In_ ULONG Pattern +); #endif -#include "shobjidl.h" +#include "wine/test.h" +#include "wine/asm.h" +#include "wine/rbtree.h" #ifndef __WINE_WINTERNL_H @@ -53,6 +70,8 @@ typedef struct _RTL_HANDLE_TABLE #endif +static BOOL is_win64 = (sizeof(void *) > sizeof(int)); + /* avoid #include */ #undef htons #ifdef WORDS_BIGENDIAN @@ -66,59 +85,53 @@ static inline USHORT __my_ushort_swap(USHORT s) #endif /* WORDS_BIGENDIAN */ +#ifdef __ASM_USE_FASTCALL_WRAPPER +extern ULONG WINAPI wrap_fastcall_func1( void *func, ULONG a ); +__ASM_STDCALL_FUNC( wrap_fastcall_func1, 8, + "popl %ecx\n\t" + "popl %eax\n\t" + "xchgl (%esp),%ecx\n\t" + "jmp *%eax" ) +#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a) +#else +#define call_fastcall_func1(func,a) func(a) +#endif + /* Function ptrs for ntdll calls */ static HMODULE hntdll = 0; -static SIZE_T (WINAPI *pRtlCompareMemory)(LPCVOID,LPCVOID,SIZE_T); -static SIZE_T (WINAPI *pRtlCompareMemoryUlong)(PULONG, SIZE_T, ULONG); -static NTSTATUS (WINAPI *pRtlDeleteTimer)(HANDLE, HANDLE, HANDLE); static VOID (WINAPI *pRtlMoveMemory)(LPVOID,LPCVOID,SIZE_T); static VOID (WINAPI *pRtlFillMemory)(LPVOID,SIZE_T,BYTE); static VOID (WINAPI *pRtlFillMemoryUlong)(LPVOID,SIZE_T,ULONG); static VOID (WINAPI *pRtlZeroMemory)(LPVOID,SIZE_T); -static ULONGLONG (WINAPIV *pRtlUlonglongByteSwap)(ULONGLONG source); -static ULONG (WINAPI *pRtlUniform)(PULONG); -static ULONG (WINAPI *pRtlRandom)(PULONG); -static BOOLEAN (WINAPI *pRtlAreAllAccessesGranted)(ACCESS_MASK, ACCESS_MASK); -static BOOLEAN (WINAPI *pRtlAreAnyAccessesGranted)(ACCESS_MASK, ACCESS_MASK); -static DWORD (WINAPI *pRtlComputeCrc32)(DWORD,const BYTE*,INT); -static void (WINAPI * pRtlInitializeHandleTable)(ULONG, ULONG, RTL_HANDLE_TABLE *); -static BOOLEAN (WINAPI * pRtlIsValidIndexHandle)(const RTL_HANDLE_TABLE *, ULONG, RTL_HANDLE **); -static NTSTATUS (WINAPI * pRtlDestroyHandleTable)(RTL_HANDLE_TABLE *); -static RTL_HANDLE * (WINAPI * pRtlAllocateHandle)(RTL_HANDLE_TABLE *, ULONG *); -static BOOLEAN (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *); -static NTSTATUS (WINAPI *pRtlAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*); -static NTSTATUS (WINAPI *pRtlFreeSid)(PSID); +static USHORT (FASTCALL *pRtlUshortByteSwap)(USHORT source); +static ULONG (FASTCALL *pRtlUlongByteSwap)(ULONG source); +static ULONGLONG (FASTCALL *pRtlUlonglongByteSwap)(ULONGLONG source); static DWORD (WINAPI *pRtlGetThreadErrorMode)(void); static NTSTATUS (WINAPI *pRtlSetThreadErrorMode)(DWORD, LPDWORD); -static IMAGE_BASE_RELOCATION *(WINAPI *pLdrProcessRelocationBlock)(void*,UINT,USHORT*,INT_PTR); -static CHAR * (WINAPI *pRtlIpv4AddressToStringA)(const IN_ADDR *, LPSTR); static NTSTATUS (WINAPI *pRtlIpv4AddressToStringExA)(const IN_ADDR *, USHORT, LPSTR, PULONG); -static NTSTATUS (WINAPI *pRtlIpv4StringToAddressA)(PCSTR, BOOLEAN, PCSTR *, IN_ADDR *); static NTSTATUS (WINAPI *pRtlIpv4StringToAddressExA)(PCSTR, BOOLEAN, IN_ADDR *, PUSHORT); -static CHAR * (WINAPI *pRtlIpv6AddressToStringA)(struct in6_addr *, PSTR); static NTSTATUS (WINAPI *pRtlIpv6AddressToStringExA)(struct in6_addr *, ULONG, USHORT, PCHAR, PULONG); -static NTSTATUS (WINAPI *pRtlIpv6StringToAddressA)(PCSTR, PCSTR *, struct in6_addr *); -static NTSTATUS (WINAPI *pRtlIpv6StringToAddressW)(PCWSTR, PCWSTR *, struct in6_addr *); static NTSTATUS (WINAPI *pRtlIpv6StringToAddressExA)(PCSTR, struct in6_addr *, PULONG, PUSHORT); static NTSTATUS (WINAPI *pRtlIpv6StringToAddressExW)(PCWSTR, struct in6_addr *, PULONG, PUSHORT); -static NTSTATUS (WINAPI *pLdrAddRefDll)(ULONG, HMODULE); -static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG*, ULONG_PTR*); -static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR); -static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)(LPWSTR, DWORD, LPDWORD, LPCSTR, DWORD); -static NTSTATUS (WINAPI *pRtlGetCompressionWorkSpaceSize)(USHORT, PULONG, PULONG); -static NTSTATUS (WINAPI *pRtlDecompressBuffer)(USHORT, PUCHAR, ULONG, const UCHAR*, ULONG, PULONG); -static NTSTATUS (WINAPI *pRtlDecompressFragment)(USHORT, PUCHAR, ULONG, const UCHAR*, ULONG, ULONG, PULONG, PVOID); -static NTSTATUS (WINAPI *pRtlCompressBuffer)(USHORT, const UCHAR*, ULONG, PUCHAR, ULONG, ULONG, PULONG, PVOID); static BOOL (WINAPI *pRtlIsCriticalSectionLocked)(CRITICAL_SECTION *); static BOOL (WINAPI *pRtlIsCriticalSectionLockedByThread)(CRITICAL_SECTION *); static NTSTATUS (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, ULONG, ULONG); +static void * (WINAPI *pRtlFindExportedRoutineByName)(HMODULE,const char *); static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *); -static NTSTATUS (WINAPI *pRtlQueryPackageIdentity)(HANDLE, WCHAR*, SIZE_T*, WCHAR*, SIZE_T*, BOOLEAN*); -static NTSTATUS (WINAPI *pRtlMakeSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,LPDWORD); -static NTSTATUS (WINAPI *pRtlAbsoluteToSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PULONG); static NTSTATUS (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **); static NTSTATUS (WINAPI *pLdrUnregisterDllNotification)(void *); +static VOID (WINAPI *pRtlGetDeviceFamilyInfoEnum)(ULONGLONG *,DWORD *,DWORD *); +static void (WINAPI *pRtlRbInsertNodeEx)(RTL_RB_TREE *, RTL_BALANCED_NODE *, BOOLEAN, RTL_BALANCED_NODE *); +static void (WINAPI *pRtlRbRemoveNode)(RTL_RB_TREE *, RTL_BALANCED_NODE *); +static DWORD (WINAPI *pRtlConvertDeviceFamilyInfoToString)(DWORD *, DWORD *, WCHAR *, WCHAR *); +static NTSTATUS (WINAPI *pRtlInitializeNtUserPfn)( const UINT64 *client_procsA, ULONG procsA_size, + const UINT64 *client_procsW, ULONG procsW_size, + const void *client_workers, ULONG workers_size ); +static NTSTATUS (WINAPI *pRtlRetrieveNtUserPfn)( const UINT64 **client_procsA, + const UINT64 **client_procsW, + const UINT64 **client_workers ); +static NTSTATUS (WINAPI *pRtlResetNtUserPfn)(void); static HMODULE hkernel32 = 0; static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); @@ -127,68 +140,48 @@ static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); #define LEN 16 static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */ static WCHAR ws2_32dllW[] = {'w','s','2','_','3','2','.','d','l','l',0}; +static WCHAR nsidllW[] = {'n','s','i','.','d','l','l',0}; static WCHAR wintrustdllW[] = {'w','i','n','t','r','u','s','t','.','d','l','l',0}; static WCHAR crypt32dllW[] = {'c','r','y','p','t','3','2','.','d','l','l',0}; static ULONG src_aligned_block[4]; static ULONG dest_aligned_block[32]; static const char *src = (const char*)src_aligned_block; static char* dest = (char*)dest_aligned_block; +const WCHAR *expected_dll = nsidllW; static void InitFunctionPtrs(void) { hntdll = LoadLibraryA("ntdll.dll"); ok(hntdll != 0, "LoadLibrary failed\n"); if (hntdll) { - pRtlCompareMemory = (void *)GetProcAddress(hntdll, "RtlCompareMemory"); - pRtlCompareMemoryUlong = (void *)GetProcAddress(hntdll, "RtlCompareMemoryUlong"); - pRtlDeleteTimer = (void *)GetProcAddress(hntdll, "RtlDeleteTimer"); pRtlMoveMemory = (void *)GetProcAddress(hntdll, "RtlMoveMemory"); pRtlFillMemory = (void *)GetProcAddress(hntdll, "RtlFillMemory"); pRtlFillMemoryUlong = (void *)GetProcAddress(hntdll, "RtlFillMemoryUlong"); pRtlZeroMemory = (void *)GetProcAddress(hntdll, "RtlZeroMemory"); - pRtlUlonglongByteSwap = (void *)GetProcAddress(hntdll, "RtlUlonglongByteSwap"); - pRtlUniform = (void *)GetProcAddress(hntdll, "RtlUniform"); - pRtlRandom = (void *)GetProcAddress(hntdll, "RtlRandom"); - pRtlAreAllAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAllAccessesGranted"); - pRtlAreAnyAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAnyAccessesGranted"); - pRtlComputeCrc32 = (void *)GetProcAddress(hntdll, "RtlComputeCrc32"); - pRtlInitializeHandleTable = (void *)GetProcAddress(hntdll, "RtlInitializeHandleTable"); - pRtlIsValidIndexHandle = (void *)GetProcAddress(hntdll, "RtlIsValidIndexHandle"); - pRtlDestroyHandleTable = (void *)GetProcAddress(hntdll, "RtlDestroyHandleTable"); - pRtlAllocateHandle = (void *)GetProcAddress(hntdll, "RtlAllocateHandle"); - pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle"); - pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid"); - pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid"); + pRtlUshortByteSwap = (void *)GetProcAddress(hntdll, "RtlUshortByteSwap"); + pRtlUlongByteSwap = (void *)GetProcAddress(hntdll, "RtlUlongByteSwap"); + pRtlUlonglongByteSwap = (void *)GetProcAddress(hntdll, "RtlUlonglongByteSwap"); pRtlGetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlGetThreadErrorMode"); pRtlSetThreadErrorMode = (void *)GetProcAddress(hntdll, "RtlSetThreadErrorMode"); - pLdrProcessRelocationBlock = (void *)GetProcAddress(hntdll, "LdrProcessRelocationBlock"); - pRtlIpv4AddressToStringA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringA"); pRtlIpv4AddressToStringExA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringExA"); - pRtlIpv4StringToAddressA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressA"); pRtlIpv4StringToAddressExA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressExA"); - pRtlIpv6AddressToStringA = (void *)GetProcAddress(hntdll, "RtlIpv6AddressToStringA"); pRtlIpv6AddressToStringExA = (void *)GetProcAddress(hntdll, "RtlIpv6AddressToStringExA"); - pRtlIpv6StringToAddressA = (void *)GetProcAddress(hntdll, "RtlIpv6StringToAddressA"); - pRtlIpv6StringToAddressW = (void *)GetProcAddress(hntdll, "RtlIpv6StringToAddressW"); pRtlIpv6StringToAddressExA = (void *)GetProcAddress(hntdll, "RtlIpv6StringToAddressExA"); pRtlIpv6StringToAddressExW = (void *)GetProcAddress(hntdll, "RtlIpv6StringToAddressExW"); - pLdrAddRefDll = (void *)GetProcAddress(hntdll, "LdrAddRefDll"); - pLdrLockLoaderLock = (void *)GetProcAddress(hntdll, "LdrLockLoaderLock"); - pLdrUnlockLoaderLock = (void *)GetProcAddress(hntdll, "LdrUnlockLoaderLock"); - pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll, "RtlMultiByteToUnicodeN"); - pRtlGetCompressionWorkSpaceSize = (void *)GetProcAddress(hntdll, "RtlGetCompressionWorkSpaceSize"); - pRtlDecompressBuffer = (void *)GetProcAddress(hntdll, "RtlDecompressBuffer"); - pRtlDecompressFragment = (void *)GetProcAddress(hntdll, "RtlDecompressFragment"); - pRtlCompressBuffer = (void *)GetProcAddress(hntdll, "RtlCompressBuffer"); pRtlIsCriticalSectionLocked = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLocked"); pRtlIsCriticalSectionLockedByThread = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLockedByThread"); pRtlInitializeCriticalSectionEx = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSectionEx"); + pRtlFindExportedRoutineByName = (void *)GetProcAddress(hntdll, "RtlFindExportedRoutineByName"); pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules"); - pRtlQueryPackageIdentity = (void *)GetProcAddress(hntdll, "RtlQueryPackageIdentity"); - pRtlMakeSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlMakeSelfRelativeSD"); - pRtlAbsoluteToSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlAbsoluteToSelfRelativeSD"); pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification"); pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification"); + pRtlGetDeviceFamilyInfoEnum = (void *)GetProcAddress(hntdll, "RtlGetDeviceFamilyInfoEnum"); + pRtlRbInsertNodeEx = (void *)GetProcAddress(hntdll, "RtlRbInsertNodeEx"); + pRtlRbRemoveNode = (void *)GetProcAddress(hntdll, "RtlRbRemoveNode"); + pRtlConvertDeviceFamilyInfoToString = (void *)GetProcAddress(hntdll, "RtlConvertDeviceFamilyInfoToString"); + pRtlInitializeNtUserPfn = (void *)GetProcAddress(hntdll, "RtlInitializeNtUserPfn"); + pRtlRetrieveNtUserPfn = (void *)GetProcAddress(hntdll, "RtlRetrieveNtUserPfn"); + pRtlResetNtUserPfn = (void *)GetProcAddress(hntdll, "RtlResetNtUserPfn"); } hkernel32 = LoadLibraryA("kernel32.dll"); ok(hkernel32 != 0, "LoadLibrary failed\n"); @@ -199,19 +192,46 @@ static void InitFunctionPtrs(void) ok(strlen(src) == 15, "Source must be 16 bytes long!\n"); } -#define COMP(str1,str2,cmplen,len) size = pRtlCompareMemory(str1, str2, cmplen); \ - ok(size == len, "Expected %ld, got %ld\n", size, (SIZE_T)len) +static void test_RtlQueryProcessDebugInformation(void) +{ + DEBUG_BUFFER *buffer; + NTSTATUS status; + + /* PDI_HEAPS | PDI_HEAP_BLOCKS */ + buffer = RtlCreateQueryDebugBuffer( 0, 0 ); + ok( buffer != NULL, "RtlCreateQueryDebugBuffer returned NULL" ); + + status = RtlQueryProcessDebugInformation( GetCurrentThreadId(), PDI_HEAPS | PDI_HEAP_BLOCKS, buffer ); + ok( status == STATUS_INVALID_CID, "RtlQueryProcessDebugInformation returned %lx\n", status ); + + status = RtlQueryProcessDebugInformation( GetCurrentProcessId(), PDI_HEAPS | PDI_HEAP_BLOCKS, buffer ); + ok( !status, "RtlQueryProcessDebugInformation returned %lx\n", status ); + ok( buffer->InfoClassMask == (PDI_HEAPS | PDI_HEAP_BLOCKS), "unexpected InfoClassMask %ld\n", buffer->InfoClassMask); + ok( buffer->HeapInformation != NULL, "unexpected HeapInformation %p\n", buffer->HeapInformation); + + status = RtlDestroyQueryDebugBuffer( buffer ); + ok( !status, "RtlDestroyQueryDebugBuffer returned %lx\n", status ); + + /* PDI_MODULES */ + buffer = RtlCreateQueryDebugBuffer( 0, 0 ); + ok( buffer != NULL, "RtlCreateQueryDebugBuffer returned NULL" ); + + status = RtlQueryProcessDebugInformation( GetCurrentProcessId(), PDI_MODULES, buffer ); + ok( !status, "RtlQueryProcessDebugInformation returned %lx\n", status ); + ok( buffer->InfoClassMask == PDI_MODULES, "unexpected InfoClassMask %ld\n", buffer->InfoClassMask); + ok( buffer->ModuleInformation != NULL, "unexpected ModuleInformation %p\n", buffer->ModuleInformation); + + status = RtlDestroyQueryDebugBuffer( buffer ); + ok( !status, "RtlDestroyQueryDebugBuffer returned %lx\n", status ); +} + +#define COMP(str1,str2,cmplen,len) size = RtlCompareMemory(str1, str2, cmplen); \ + ok(size == len, "Expected %Id, got %Id\n", size, (SIZE_T)len) static void test_RtlCompareMemory(void) { SIZE_T size; - if (!pRtlCompareMemory) - { - win_skip("RtlCompareMemory is not available\n"); - return; - } - strcpy(dest, src); COMP(src,src,0,0); @@ -225,50 +245,44 @@ static void test_RtlCompareMemoryUlong(void) ULONG a[10]; ULONG result; - if (!pRtlCompareMemoryUlong) - { - win_skip("RtlCompareMemoryUlong is not available\n"); - return; - } - a[0]= 0x0123; a[1]= 0x4567; a[2]= 0x89ab; a[3]= 0xcdef; - result = pRtlCompareMemoryUlong(a, 0, 0x0123); - ok(result == 0, "RtlCompareMemoryUlong(%p, 0, 0x0123) returns %u, expected 0\n", a, result); - result = pRtlCompareMemoryUlong(a, 3, 0x0123); - ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %u, expected 0\n", a, result); - result = pRtlCompareMemoryUlong(a, 4, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 5, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 7, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 8, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 9, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 4, 0x0127); - ok(result == 0, "RtlCompareMemoryUlong(%p, 4, 0x0127) returns %u, expected 0\n", a, result); - result = pRtlCompareMemoryUlong(a, 4, 0x7123); - ok(result == 0, "RtlCompareMemoryUlong(%p, 4, 0x7123) returns %u, expected 0\n", a, result); - result = pRtlCompareMemoryUlong(a, 16, 0x4567); - ok(result == 0, "RtlCompareMemoryUlong(%p, 16, 0x4567) returns %u, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 0, 0x0123); + ok(result == 0, "RtlCompareMemoryUlong(%p, 0, 0x0123) returns %lu, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 3, 0x0123); + ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %lu, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 4, 0x0123); + ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 5, 0x0123); + ok(result == 4 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 7, 0x0123); + ok(result == 4 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 8, 0x0123); + ok(result == 4, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 9, 0x0123); + ok(result == 4 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 4, 0x0127); + ok(result == 0, "RtlCompareMemoryUlong(%p, 4, 0x0127) returns %lu, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 4, 0x7123); + ok(result == 0 || result == 1 /* arm64 */, "RtlCompareMemoryUlong(%p, 4, 0x7123) returns %lu, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 16, 0x4567); + ok(result == 0, "RtlCompareMemoryUlong(%p, 16, 0x4567) returns %lu, expected 0\n", a, result); a[1]= 0x0123; - result = pRtlCompareMemoryUlong(a, 3, 0x0123); - ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %u, expected 0\n", a, result); - result = pRtlCompareMemoryUlong(a, 4, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 5, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 7, 0x0123); - ok(result == 4, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %u, expected 4\n", a, result); - result = pRtlCompareMemoryUlong(a, 8, 0x0123); - ok(result == 8, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %u, expected 8\n", a, result); - result = pRtlCompareMemoryUlong(a, 9, 0x0123); - ok(result == 8, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %u, expected 8\n", a, result); + result = RtlCompareMemoryUlong(a, 3, 0x0123); + ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %lu, expected 0\n", a, result); + result = RtlCompareMemoryUlong(a, 4, 0x0123); + ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 5, 0x0123); + ok(result == 4 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 7, 0x0123); + ok(result == 4 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %lu, expected 4\n", a, result); + result = RtlCompareMemoryUlong(a, 8, 0x0123); + ok(result == 8, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %lu, expected 8\n", a, result); + result = RtlCompareMemoryUlong(a, 9, 0x0123); + ok(result == 8 || !result /* arm64 */, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %lu, expected 8\n", a, result); } #define COPY(len) memset(dest,0,sizeof(dest_aligned_block)); pRtlMoveMemory(dest, src, len) @@ -378,324 +392,160 @@ static void test_RtlZeroMemory(void) ZERO(9); MCMP("\0\0\0\0\0\0\0\0\0 test!"); } -static void test_RtlUlonglongByteSwap(void) +static void test_RtlByteSwap(void) { - ULONGLONG result; + ULONGLONG llresult; + ULONG lresult; + USHORT sresult; - if ( !pRtlUlonglongByteSwap ) +#ifdef _WIN64 + /* the Rtl*ByteSwap() are always inlined and not exported from ntdll on 64bit */ + sresult = RtlUshortByteSwap( 0x1234 ); + ok( 0x3412 == sresult, + "inlined RtlUshortByteSwap() returns 0x%x\n", sresult ); + lresult = RtlUlongByteSwap( 0x87654321 ); + ok( 0x21436587 == lresult, + "inlined RtlUlongByteSwap() returns 0x%lx\n", lresult ); + llresult = RtlUlonglongByteSwap( 0x7654321087654321ull ); + ok( 0x2143658710325476 == llresult, + "inlined RtlUlonglongByteSwap() returns %#I64x\n", llresult ); +#else + ok( pRtlUshortByteSwap != NULL, "RtlUshortByteSwap is not available\n" ); + if ( pRtlUshortByteSwap ) { - win_skip("RtlUlonglongByteSwap is not available\n"); - return; + sresult = call_fastcall_func1( pRtlUshortByteSwap, 0x1234u ); + ok( 0x3412u == sresult, + "ntdll.RtlUshortByteSwap() returns %#x\n", sresult ); } - if ( pRtlUlonglongByteSwap( 0 ) != 0 ) + ok( pRtlUlongByteSwap != NULL, "RtlUlongByteSwap is not available\n" ); + if ( pRtlUlongByteSwap ) { - win_skip("Broken RtlUlonglongByteSwap in win2k\n"); - return; + lresult = call_fastcall_func1( pRtlUlongByteSwap, 0x87654321ul ); + ok( 0x21436587ul == lresult, + "ntdll.RtlUlongByteSwap() returns %#lx\n", lresult ); } - result = pRtlUlonglongByteSwap( ((ULONGLONG)0x76543210 << 32) | 0x87654321 ); - ok( (((ULONGLONG)0x21436587 << 32) | 0x10325476) == result, - "RtlUlonglongByteSwap(0x7654321087654321) returns 0x%s, expected 0x2143658710325476\n", - wine_dbgstr_longlong(result)); + ok( pRtlUlonglongByteSwap != NULL, "RtlUlonglongByteSwap is not available\n"); + if ( pRtlUlonglongByteSwap ) + { + llresult = pRtlUlonglongByteSwap( 0x7654321087654321ull ); + ok( 0x2143658710325476ull == llresult, + "ntdll.RtlUlonglongByteSwap() returns %#I64x\n", llresult ); + } +#endif } static void test_RtlUniform(void) { - ULONGLONG num; + const ULONG step = 0x7fff; + ULONG num; ULONG seed; ULONG seed_bak; ULONG expected; ULONG result; - if (!pRtlUniform) - { - win_skip("RtlUniform is not available\n"); - return; - } - -/* - * According to the documentation RtlUniform is using D.H. Lehmer's 1948 - * algorithm. This algorithm is: - * - * seed = (seed * const_1 + const_2) % const_3; - * - * According to the documentation the random number is distributed over - * [0..MAXLONG]. Therefore const_3 is MAXLONG + 1: - * - * seed = (seed * const_1 + const_2) % (MAXLONG + 1); - * - * Because MAXLONG is 0x7fffffff (and MAXLONG + 1 is 0x80000000) the - * algorithm can be expressed without division as: - * - * seed = (seed * const_1 + const_2) & MAXLONG; - * - * To find out const_2 we just call RtlUniform with seed set to 0: - */ + /* + * According to the documentation RtlUniform is using D.H. Lehmer's 1948 + * algorithm. We assume a more generic version of this algorithm, + * which is the linear congruential generator (LCG). Its formula is: + * + * X_(n+1) = (a * X_n + c) % m + * + * where a is the multiplier, c is the increment, and m is the modulus. + * + * According to the documentation, the random numbers are distributed over + * [0..MAXLONG]. Therefore, the modulus is MAXLONG + 1: + * + * X_(n+1) = (a * X_n + c) % (MAXLONG + 1) + * + * To find out the increment, we just call RtlUniform with seed set to 0. + * This reveals c = 0x7fffffc3. + */ seed = 0; expected = 0x7fffffc3; - result = pRtlUniform(&seed); + result = RtlUniform(&seed); ok(result == expected, - "RtlUniform(&seed (seed == 0)) returns %x, expected %x\n", + "RtlUniform(&seed (seed == 0)) returns %lx, expected %lx\n", result, expected); -/* - * The algorithm is now: - * - * seed = (seed * const_1 + 0x7fffffc3) & MAXLONG; - * - * To find out const_1 we can use: - * - * const_1 = RtlUniform(1) - 0x7fffffc3; - * - * If that does not work a search loop can try all possible values of - * const_1 and compare to the result to RtlUniform(1). - * This way we find out that const_1 is 0xffffffed. - * - * For seed = 1 the const_2 is 0x7fffffc4: - */ + + /* + * The formula is now: + * + * X_(n+1) = (a * X_n + 0x7fffffc3) % (MAXLONG + 1) + * + * If the modulus is correct, RtlUniform(0) shall equal RtlUniform(MAXLONG + 1). + * However, testing reveals that this is not the case. + * That is, the modulus in the documentation is incorrect. + */ + seed = 0x80000000U; + expected = 0x7fffffb1; + result = RtlUniform(&seed); + + ok(result == expected, + "RtlUniform(&seed (seed == 0x80000000)) returns %lx, expected %lx\n", + result, expected); + + /* + * We try another value for modulus, say MAXLONG. + * We discover that RtlUniform(0) equals RtlUniform(MAXLONG), which means + * the correct value for the modulus is actually MAXLONG. + */ + seed = 0x7fffffff; + expected = 0x7fffffc3; + result = RtlUniform(&seed); + ok(result == expected, + "RtlUniform(&seed (seed == 0x7fffffff)) returns %lx, expected %lx\n", + result, expected); + + /* + * The formula is now: + * + * X_(n+1) = (a * X_n + 0x7fffffc3) % MAXLONG + * + * To find out the multiplier we can use: + * + * a = RtlUniform(1) - 0x7fffffc3 (mod MAXLONG) + * + * This way, we find out that a = -18 (mod MAXLONG), + * which is congruent to 0x7fffffed (MAXLONG - 18). + */ seed = 1; - expected = seed * 0xffffffed + 0x7fffffc3 + 1; - result = pRtlUniform(&seed); + expected = ((ULONGLONG)seed * 0x7fffffed + 0x7fffffc3) % MAXLONG; + result = RtlUniform(&seed); ok(result == expected, - "RtlUniform(&seed (seed == 1)) returns %x, expected %x\n", - result, expected); -/* - * For seed = 2 the const_2 is 0x7fffffc3: - */ - seed = 2; - expected = seed * 0xffffffed + 0x7fffffc3; - result = pRtlUniform(&seed); - -/* - * Windows Vista uses different algorithms, so skip the rest of the tests - * until that is figured out. Trace output for the failures is about 10.5 MB! - */ - - if (result == 0x7fffff9f) { - skip("Most likely running on Windows Vista which uses a different algorithm\n"); - return; - } - - ok(result == expected, - "RtlUniform(&seed (seed == 2)) returns %x, expected %x\n", + "RtlUniform(&seed (seed == 1)) returns %lx, expected %lx\n", result, expected); -/* - * More tests show that if seed is odd the result must be incremented by 1: - */ - seed = 3; - expected = seed * 0xffffffed + 0x7fffffc3 + (seed & 1); - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 3)) returns %x, expected %x\n", - result, expected); + num = 2; + do + { + seed = num; + expected = ((ULONGLONG)seed * 0x7fffffed + 0x7fffffc3) % 0x7fffffff; + result = RtlUniform(&seed); + ok(result == expected, + "test: RtlUniform(&seed (seed == %lx)) returns %lx, expected %lx\n", + num, result, expected); + ok(seed == expected, + "test: RtlUniform(&seed (seed == %lx)) sets seed to %lx, expected %lx\n", + num, result, expected); - seed = 0x6bca1aa; - expected = seed * 0xffffffed + 0x7fffffc3; - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 0x6bca1aa)) returns %x, expected %x\n", - result, expected); + num += step; + } while (num >= 2 + step); - seed = 0x6bca1ab; - expected = seed * 0xffffffed + 0x7fffffc3 + 1; - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 0x6bca1ab)) returns %x, expected %x\n", - result, expected); -/* - * When seed is 0x6bca1ac there is an exception: - */ - seed = 0x6bca1ac; - expected = seed * 0xffffffed + 0x7fffffc3 + 2; - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 0x6bca1ac)) returns %x, expected %x\n", - result, expected); -/* - * Note that up to here const_3 is not used - * (the highest bit of the result is not set). - * - * Starting with 0x6bca1ad: If seed is even the result must be incremented by 1: - */ - seed = 0x6bca1ad; - expected = (seed * 0xffffffed + 0x7fffffc3) & MAXLONG; - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 0x6bca1ad)) returns %x, expected %x\n", - result, expected); - - seed = 0x6bca1ae; - expected = (seed * 0xffffffed + 0x7fffffc3 + 1) & MAXLONG; - result = pRtlUniform(&seed); - ok(result == expected, - "RtlUniform(&seed (seed == 0x6bca1ae)) returns %x, expected %x\n", - result, expected); -/* - * There are several ranges where for odd or even seed the result must be - * incremented by 1. You can see this ranges in the following test. - * - * For a full test use one of the following loop heads: - * - * for (num = 0; num <= 0xffffffff; num++) { - * seed = num; - * ... - * - * seed = 0; - * for (num = 0; num <= 0xffffffff; num++) { - * ... - */ seed = 0; for (num = 0; num <= 100000; num++) { - - expected = seed * 0xffffffed + 0x7fffffc3; - if (seed < 0x6bca1ac) { - expected = expected + (seed & 1); - } else if (seed == 0x6bca1ac) { - expected = (expected + 2) & MAXLONG; - } else if (seed < 0xd79435c) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x1435e50b) { - expected = expected + (seed & 1); - } else if (seed < 0x1af286ba) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x21af2869) { - expected = expected + (seed & 1); - } else if (seed < 0x286bca18) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x2f286bc7) { - expected = expected + (seed & 1); - } else if (seed < 0x35e50d77) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x3ca1af26) { - expected = expected + (seed & 1); - } else if (seed < 0x435e50d5) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x4a1af284) { - expected = expected + (seed & 1); - } else if (seed < 0x50d79433) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x579435e2) { - expected = expected + (seed & 1); - } else if (seed < 0x5e50d792) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x650d7941) { - expected = expected + (seed & 1); - } else if (seed < 0x6bca1af0) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x7286bc9f) { - expected = expected + (seed & 1); - } else if (seed < 0x79435e4e) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x7ffffffd) { - expected = expected + (seed & 1); - } else if (seed < 0x86bca1ac) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed == 0x86bca1ac) { - expected = (expected + 1) & MAXLONG; - } else if (seed < 0x8d79435c) { - expected = expected + (seed & 1); - } else if (seed < 0x9435e50b) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0x9af286ba) { - expected = expected + (seed & 1); - } else if (seed < 0xa1af2869) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xa86bca18) { - expected = expected + (seed & 1); - } else if (seed < 0xaf286bc7) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed == 0xaf286bc7) { - expected = (expected + 2) & MAXLONG; - } else if (seed < 0xb5e50d77) { - expected = expected + (seed & 1); - } else if (seed < 0xbca1af26) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xc35e50d5) { - expected = expected + (seed & 1); - } else if (seed < 0xca1af284) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xd0d79433) { - expected = expected + (seed & 1); - } else if (seed < 0xd79435e2) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xde50d792) { - expected = expected + (seed & 1); - } else if (seed < 0xe50d7941) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xebca1af0) { - expected = expected + (seed & 1); - } else if (seed < 0xf286bc9f) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else if (seed < 0xf9435e4e) { - expected = expected + (seed & 1); - } else if (seed < 0xfffffffd) { - expected = (expected + (~seed & 1)) & MAXLONG; - } else { - expected = expected + (seed & 1); - } /* if */ + expected = ((ULONGLONG)seed * 0x7fffffed + 0x7fffffc3) % 0x7fffffff; seed_bak = seed; - result = pRtlUniform(&seed); + result = RtlUniform(&seed); ok(result == expected, - "test: 0x%s RtlUniform(&seed (seed == %x)) returns %x, expected %x\n", - wine_dbgstr_longlong(num), seed_bak, result, expected); + "test: %ld RtlUniform(&seed (seed == %lx)) returns %lx, expected %lx\n", + num, seed_bak, result, expected); ok(seed == expected, - "test: 0x%s RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n", - wine_dbgstr_longlong(num), seed_bak, result, expected); + "test: %ld RtlUniform(&seed (seed == %lx)) sets seed to %lx, expected %lx\n", + num, seed_bak, result, expected); } /* for */ -/* - * Further investigation shows: In the different regions the highest bit - * is set or cleared when even or odd seeds need an increment by 1. - * This leads to a simplified algorithm: - * - * seed = seed * 0xffffffed + 0x7fffffc3; - * if (seed == 0xffffffff || seed == 0x7ffffffe) { - * seed = (seed + 2) & MAXLONG; - * } else if (seed == 0x7fffffff) { - * seed = 0; - * } else if ((seed & 0x80000000) == 0) { - * seed = seed + (~seed & 1); - * } else { - * seed = (seed + (seed & 1)) & MAXLONG; - * } - * - * This is also the algorithm used for RtlUniform of wine (see dlls/ntdll/rtl.c). - * - * Now comes the funny part: - * It took me one weekend, to find the complicated algorithm and one day more, - * to find the simplified algorithm. Several weeks later I found out: The value - * MAXLONG (=0x7fffffff) is never returned, neither with the native function - * nor with the simplified algorithm. In reality the native function and our - * function return a random number distributed over [0..MAXLONG-1]. Note - * that this is different from what native documentation states [0..MAXLONG]. - * Expressed with D.H. Lehmer's 1948 algorithm it looks like: - * - * seed = (seed * const_1 + const_2) % MAXLONG; - * - * Further investigations show that the real algorithm is: - * - * seed = (seed * 0x7fffffed + 0x7fffffc3) % MAXLONG; - * - * This is checked with the test below: - */ - seed = 0; - for (num = 0; num <= 100000; num++) { - expected = (seed * 0x7fffffed + 0x7fffffc3) % 0x7fffffff; - seed_bak = seed; - result = pRtlUniform(&seed); - ok(result == expected, - "test: 0x%s RtlUniform(&seed (seed == %x)) returns %x, expected %x\n", - wine_dbgstr_longlong(num), seed_bak, result, expected); - ok(seed == expected, - "test: 0x%s RtlUniform(&seed (seed == %x)) sets seed to %x, expected %x\n", - wine_dbgstr_longlong(num), seed_bak, result, expected); - } /* for */ -/* - * More tests show that RtlUniform does not return 0x7ffffffd for seed values - * in the range [0..MAXLONG-1]. Additionally 2 is returned twice. This shows - * that there is more than one cycle of generated randon numbers ... - */ } @@ -705,19 +555,13 @@ static void test_RtlRandom(void) ULONG seed; ULONG res[512]; - if (!pRtlRandom) - { - win_skip("RtlRandom is not available\n"); - return; - } - seed = 0; - for (i = 0; i < sizeof(res) / sizeof(res[0]); i++) + for (i = 0; i < ARRAY_SIZE(res); i++) { - res[i] = pRtlRandom(&seed); - ok(seed != res[i], "%i: seed is same as res %x\n", i, seed); + res[i] = RtlRandom(&seed); + ok(seed != res[i], "%i: seed is same as res %lx\n", i, seed); for (j = 0; j < i; j++) - ok(res[i] != res[j], "res[%i] (%x) is same as res[%i] (%x)\n", j, res[j], i, res[i]); + ok(res[i] != res[j], "res[%i] (%lx) is same as res[%i] (%lx)\n", j, res[j], i, res[i]); } } @@ -740,7 +584,6 @@ static const all_accesses_t all_accesses[] = { {0xFEDCBA76, 0xC8C4B242, 1}, {0xC8C4B242, 0xFEDCBA76, 0}, }; -#define NB_ALL_ACCESSES (sizeof(all_accesses)/sizeof(*all_accesses)) static void test_RtlAreAllAccessesGranted(void) @@ -748,17 +591,11 @@ static void test_RtlAreAllAccessesGranted(void) unsigned int test_num; BOOLEAN result; - if (!pRtlAreAllAccessesGranted) - { - win_skip("RtlAreAllAccessesGranted is not available\n"); - return; - } - - for (test_num = 0; test_num < NB_ALL_ACCESSES; test_num++) { - result = pRtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess, - all_accesses[test_num].DesiredAccess); + for (test_num = 0; test_num < ARRAY_SIZE(all_accesses); test_num++) { + result = RtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess, + all_accesses[test_num].DesiredAccess); ok(all_accesses[test_num].result == result, - "(test %d): RtlAreAllAccessesGranted(%08x, %08x) returns %d, expected %d\n", + "(test %d): RtlAreAllAccessesGranted(%08lx, %08lx) returns %d, expected %d\n", test_num, all_accesses[test_num].GrantedAccess, all_accesses[test_num].DesiredAccess, result, all_accesses[test_num].result); @@ -783,7 +620,6 @@ static const any_accesses_t any_accesses[] = { {0xFEDCBA76, 0x00040000, 1}, {0xFED8BA76, 0x00040000, 0}, }; -#define NB_ANY_ACCESSES (sizeof(any_accesses)/sizeof(*any_accesses)) static void test_RtlAreAnyAccessesGranted(void) @@ -791,17 +627,11 @@ static void test_RtlAreAnyAccessesGranted(void) unsigned int test_num; BOOLEAN result; - if (!pRtlAreAnyAccessesGranted) - { - win_skip("RtlAreAnyAccessesGranted is not available\n"); - return; - } - - for (test_num = 0; test_num < NB_ANY_ACCESSES; test_num++) { - result = pRtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess, - any_accesses[test_num].DesiredAccess); + for (test_num = 0; test_num < ARRAY_SIZE(any_accesses); test_num++) { + result = RtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess, + any_accesses[test_num].DesiredAccess); ok(any_accesses[test_num].result == result, - "(test %d): RtlAreAnyAccessesGranted(%08x, %08x) returns %d, expected %d\n", + "(test %d): RtlAreAnyAccessesGranted(%08lx, %08lx) returns %d, expected %d\n", test_num, any_accesses[test_num].GrantedAccess, any_accesses[test_num].DesiredAccess, result, any_accesses[test_num].result); @@ -812,14 +642,8 @@ static void test_RtlComputeCrc32(void) { DWORD crc = 0; - if (!pRtlComputeCrc32) - { - win_skip("RtlComputeCrc32 is not available\n"); - return; - } - - crc = pRtlComputeCrc32(crc, (const BYTE *)src, LEN); - ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8x\n", crc); + crc = RtlComputeCrc32(crc, (const BYTE *)src, LEN); + ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8lx\n", crc); } @@ -843,23 +667,17 @@ static void test_HandleTables(void) MY_HANDLE * MyHandle; RTL_HANDLE_TABLE HandleTable; - if (!pRtlInitializeHandleTable) - { - win_skip("RtlInitializeHandleTable is not available\n"); - return; - } - - pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable); - MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index); + RtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable); + MyHandle = (MY_HANDLE *)RtlAllocateHandle(&HandleTable, &Index); ok(MyHandle != NULL, "RtlAllocateHandle failed\n"); RtlpMakeHandleAllocated(&MyHandle->RtlHandle); MyHandle = NULL; - result = pRtlIsValidIndexHandle(&HandleTable, Index, (RTL_HANDLE **)&MyHandle); + result = RtlIsValidIndexHandle(&HandleTable, Index, (RTL_HANDLE **)&MyHandle); ok(result, "Handle %p wasn't valid\n", MyHandle); - result = pRtlFreeHandle(&HandleTable, &MyHandle->RtlHandle); + result = RtlFreeHandle(&HandleTable, &MyHandle->RtlHandle); ok(result, "Couldn't free handle %p\n", MyHandle); - status = pRtlDestroyHandleTable(&HandleTable); - ok(status == STATUS_SUCCESS, "RtlDestroyHandleTable failed with error 0x%08x\n", status); + status = RtlDestroyHandleTable(&HandleTable); + ok(status == STATUS_SUCCESS, "RtlDestroyHandleTable failed with error 0x%08lx\n", status); } static void test_RtlAllocateAndInitializeSid(void) @@ -868,42 +686,30 @@ static void test_RtlAllocateAndInitializeSid(void) SID_IDENTIFIER_AUTHORITY sia = {{ 1, 2, 3, 4, 5, 6 }}; PSID psid; - if (!pRtlAllocateAndInitializeSid) - { - win_skip("RtlAllocateAndInitializeSid is not available\n"); - return; - } - - ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid); - ok(!ret, "RtlAllocateAndInitializeSid error %08x\n", ret); - ret = pRtlFreeSid(psid); - ok(!ret, "RtlFreeSid error %08x\n", ret); + ret = RtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid); + ok(!ret, "RtlAllocateAndInitializeSid error %08lx\n", ret); + ret = RtlFreeSid(psid); + ok(!ret, "RtlFreeSid error %08lx\n", ret); /* these tests crash on XP */ if (0) { - pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid); - pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL); + RtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid); + RtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL); } - ret = pRtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid); - ok(ret == STATUS_INVALID_SID, "wrong error %08x\n", ret); + ret = RtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid); + ok(ret == STATUS_INVALID_SID, "wrong error %08lx\n", ret); } static void test_RtlDeleteTimer(void) { NTSTATUS ret; - if (!pRtlDeleteTimer) - { - win_skip("RtlDeleteTimer is not available\n"); - return; - } - - ret = pRtlDeleteTimer(NULL, NULL, NULL); + ret = RtlDeleteTimer(NULL, NULL, NULL); ok(ret == STATUS_INVALID_PARAMETER_1 || ret == STATUS_INVALID_PARAMETER, /* W2K */ - "expected STATUS_INVALID_PARAMETER_1 or STATUS_INVALID_PARAMETER, got %x\n", ret); + "expected STATUS_INVALID_PARAMETER_1 or STATUS_INVALID_PARAMETER, got %lx\n", ret); } static void test_RtlThreadErrorMode(void) @@ -927,32 +733,32 @@ static void test_RtlThreadErrorMode(void) status = pRtlSetThreadErrorMode(0x70, &mode); ok(status == STATUS_SUCCESS || status == STATUS_WAIT_1, /* Vista */ - "RtlSetThreadErrorMode failed with error 0x%08x\n", status); + "RtlSetThreadErrorMode failed with error 0x%08lx\n", status); ok(mode == oldmode, - "RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n", + "RtlSetThreadErrorMode returned mode 0x%lx, expected 0x%lx\n", mode, oldmode); ok(pRtlGetThreadErrorMode() == 0x70, - "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0x70); + "RtlGetThreadErrorMode returned 0x%lx, expected 0x%x\n", mode, 0x70); if (!is_wow64) { ok(NtCurrentTeb()->HardErrorMode == 0x70, - "The TEB contains 0x%x, expected 0x%x\n", + "The TEB contains 0x%lx, expected 0x%x\n", NtCurrentTeb()->HardErrorMode, 0x70); } status = pRtlSetThreadErrorMode(0, &mode); ok(status == STATUS_SUCCESS || status == STATUS_WAIT_1, /* Vista */ - "RtlSetThreadErrorMode failed with error 0x%08x\n", status); + "RtlSetThreadErrorMode failed with error 0x%08lx\n", status); ok(mode == 0x70, - "RtlSetThreadErrorMode returned mode 0x%x, expected 0x%x\n", + "RtlSetThreadErrorMode returned mode 0x%lx, expected 0x%x\n", mode, 0x70); ok(pRtlGetThreadErrorMode() == 0, - "RtlGetThreadErrorMode returned 0x%x, expected 0x%x\n", mode, 0); + "RtlGetThreadErrorMode returned 0x%lx, expected 0x%x\n", mode, 0); if (!is_wow64) { ok(NtCurrentTeb()->HardErrorMode == 0, - "The TEB contains 0x%x, expected 0x%x\n", + "The TEB contains 0x%lx, expected 0x%x\n", NtCurrentTeb()->HardErrorMode, 0); } @@ -962,11 +768,11 @@ static void test_RtlThreadErrorMode(void) if (mode & 0x70) ok(status == STATUS_SUCCESS || status == STATUS_WAIT_1, /* Vista */ - "RtlSetThreadErrorMode(%x,NULL) failed with error 0x%08x\n", + "RtlSetThreadErrorMode(%lx,NULL) failed with error 0x%08lx\n", mode, status); else ok(status == STATUS_INVALID_PARAMETER_1, - "RtlSetThreadErrorMode(%x,NULL) returns 0x%08x, " + "RtlSetThreadErrorMode(%lx,NULL) returns 0x%08lx, " "expected STATUS_INVALID_PARAMETER_1\n", mode, status); } @@ -981,26 +787,21 @@ static void test_LdrProcessRelocationBlock(void) DWORD addr32; SHORT addr16; - if(!pLdrProcessRelocationBlock) { - win_skip("LdrProcessRelocationBlock not available\n"); - return; - } - addr32 = 0x50005; reloc = IMAGE_REL_BASED_HIGHLOW<<12; - ret = pLdrProcessRelocationBlock(&addr32, 1, &reloc, 0x500050); + ret = LdrProcessRelocationBlock(&addr32, 1, &reloc, 0x500050); ok((USHORT*)ret == &reloc+1, "ret = %p, expected %p\n", ret, &reloc+1); - ok(addr32 == 0x550055, "addr32 = %x, expected 0x550055\n", addr32); + ok(addr32 == 0x550055, "addr32 = %lx, expected 0x550055\n", addr32); addr16 = 0x505; reloc = IMAGE_REL_BASED_HIGH<<12; - ret = pLdrProcessRelocationBlock(&addr16, 1, &reloc, 0x500060); + ret = LdrProcessRelocationBlock(&addr16, 1, &reloc, 0x500060); ok((USHORT*)ret == &reloc+1, "ret = %p, expected %p\n", ret, &reloc+1); ok(addr16 == 0x555, "addr16 = %x, expected 0x555\n", addr16); addr16 = 0x505; reloc = IMAGE_REL_BASED_LOW<<12; - ret = pLdrProcessRelocationBlock(&addr16, 1, &reloc, 0x500060); + ret = LdrProcessRelocationBlock(&addr16, 1, &reloc, 0x500060); ok((USHORT*)ret == &reloc+1, "ret = %p, expected %p\n", ret, &reloc+1); ok(addr16 == 0x565, "addr16 = %x, expected 0x565\n", addr16); } @@ -1012,12 +813,6 @@ static void test_RtlIpv4AddressToString(void) IN_ADDR ip; DWORD_PTR len; - if (!pRtlIpv4AddressToStringA) - { - win_skip("RtlIpv4AddressToStringA not available\n"); - return; - } - ip.S_un.S_un_b.s_b1 = 1; ip.S_un.S_un_b.s_b2 = 2; ip.S_un.S_un_b.s_b3 = 3; @@ -1025,11 +820,11 @@ static void test_RtlIpv4AddressToString(void) memset(buffer, '#', sizeof(buffer) - 1); buffer[sizeof(buffer) -1] = 0; - res = pRtlIpv4AddressToStringA(&ip, buffer); + res = RtlIpv4AddressToStringA(&ip, buffer); len = strlen(buffer); ok(res == (buffer + len), "got %p with '%s' (expected %p)\n", res, buffer, buffer + len); - res = pRtlIpv4AddressToStringA(&ip, NULL); + res = RtlIpv4AddressToStringA(&ip, NULL); ok( (res == (char *)~0) || broken(res == (char *)len), /* XP and w2003 */ "got %p (expected ~0)\n", res); @@ -1038,13 +833,13 @@ static void test_RtlIpv4AddressToString(void) /* this crashes in windows */ memset(buffer, '#', sizeof(buffer) - 1); buffer[sizeof(buffer) -1] = 0; - res = pRtlIpv4AddressToStringA(NULL, buffer); + res = RtlIpv4AddressToStringA(NULL, buffer); trace("got %p with '%s'\n", res, buffer); } if (0) { /* this crashes in windows */ - res = pRtlIpv4AddressToStringA(NULL, NULL); + res = RtlIpv4AddressToStringA(NULL, NULL); trace("got %p\n", res); } } @@ -1082,7 +877,7 @@ static void test_RtlIpv4AddressToStringEx(void) used = strlen(buffer); ok( (res == STATUS_SUCCESS) && (size == strlen(expect) + 1) && !strcmp(buffer, expect), - "got 0x%x and size %d with '%s'\n", res, size, buffer); + "got 0x%lx and size %ld with '%s'\n", res, size, buffer); size = used + 1; memset(buffer, '#', sizeof(buffer) - 1); @@ -1090,14 +885,14 @@ static void test_RtlIpv4AddressToStringEx(void) res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_SUCCESS) && (size == strlen(expect) + 1) && !strcmp(buffer, expect), - "got 0x%x and size %d with '%s'\n", res, size, buffer); + "got 0x%lx and size %ld with '%s'\n", res, size, buffer); size = used; memset(buffer, '#', sizeof(buffer) - 1); buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_INVALID_PARAMETER) && (size == used + 1), - "got 0x%x and %d with '%s' (expected STATUS_INVALID_PARAMETER and %d)\n", + "got 0x%lx and %ld with '%s' (expected STATUS_INVALID_PARAMETER and %ld)\n", res, size, buffer, used + 1); size = used - 1; @@ -1105,7 +900,7 @@ static void test_RtlIpv4AddressToStringEx(void) buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_INVALID_PARAMETER) && (size == used + 1), - "got 0x%x and %d with '%s' (expected STATUS_INVALID_PARAMETER and %d)\n", + "got 0x%lx and %ld with '%s' (expected STATUS_INVALID_PARAMETER and %ld)\n", res, size, buffer, used + 1); @@ -1120,7 +915,7 @@ static void test_RtlIpv4AddressToStringEx(void) used = strlen(buffer); ok( (res == STATUS_SUCCESS) && (size == strlen(expect) + 1) && !strcmp(buffer, expect), - "got 0x%x and size %d with '%s'\n", res, size, buffer); + "got 0x%lx and size %ld with '%s'\n", res, size, buffer); size = used + 1; memset(buffer, '#', sizeof(buffer) - 1); @@ -1128,14 +923,14 @@ static void test_RtlIpv4AddressToStringEx(void) res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_SUCCESS) && (size == strlen(expect) + 1) && !strcmp(buffer, expect), - "got 0x%x and size %d with '%s'\n", res, size, buffer); + "got 0x%lx and size %ld with '%s'\n", res, size, buffer); size = used; memset(buffer, '#', sizeof(buffer) - 1); buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_INVALID_PARAMETER) && (size == used + 1), - "got 0x%x and %d with '%s' (expected STATUS_INVALID_PARAMETER and %d)\n", + "got 0x%lx and %ld with '%s' (expected STATUS_INVALID_PARAMETER and %ld)\n", res, size, buffer, used + 1); size = used - 1; @@ -1143,7 +938,7 @@ static void test_RtlIpv4AddressToStringEx(void) buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(&ip, port, buffer, &size); ok( (res == STATUS_INVALID_PARAMETER) && (size == used + 1), - "got 0x%x and %d with '%s' (expected STATUS_INVALID_PARAMETER and %d)\n", + "got 0x%lx and %ld with '%s' (expected STATUS_INVALID_PARAMETER and %ld)\n", res, size, buffer, used + 1); @@ -1152,19 +947,19 @@ static void test_RtlIpv4AddressToStringEx(void) buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(&ip, 0, buffer, NULL); ok(res == STATUS_INVALID_PARAMETER, - "got 0x%x with '%s' (expected STATUS_INVALID_PARAMETER)\n", res, buffer); + "got 0x%lx with '%s' (expected STATUS_INVALID_PARAMETER)\n", res, buffer); size = sizeof(buffer); res = pRtlIpv4AddressToStringExA(&ip, 0, NULL, &size); ok( res == STATUS_INVALID_PARAMETER, - "got 0x%x and size %d (expected STATUS_INVALID_PARAMETER)\n", res, size); + "got 0x%lx and size %ld (expected STATUS_INVALID_PARAMETER)\n", res, size); size = sizeof(buffer); memset(buffer, '#', sizeof(buffer) - 1); buffer[sizeof(buffer) -1] = 0; res = pRtlIpv4AddressToStringExA(NULL, 0, buffer, &size); ok( res == STATUS_INVALID_PARAMETER, - "got 0x%x and size %d with '%s' (expected STATUS_INVALID_PARAMETER)\n", + "got 0x%lx and size %ld with '%s' (expected STATUS_INVALID_PARAMETER)\n", res, size, buffer); } @@ -1220,12 +1015,23 @@ static struct { "1", STATUS_SUCCESS, 1, { 0, 0, 0, 1 }, strict_diff_4, STATUS_INVALID_PARAMETER, 1, { -1 } }, { "-1", STATUS_INVALID_PARAMETER, 0, { -1 } }, + { "1.2", STATUS_SUCCESS, 3, { 1, 0, 0, 2 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 3, { -1 } }, + { "1000.2000", STATUS_INVALID_PARAMETER, 9, { -1 } }, + { "1.2.", STATUS_INVALID_PARAMETER, 4, { -1 } }, + { "1..2", STATUS_INVALID_PARAMETER, 3, { -1 } }, + { "1...2", STATUS_INVALID_PARAMETER, 3, { -1 } }, + { "1.2.3", STATUS_SUCCESS, 5, { 1, 2, 0, 3 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 5, { -1 } }, + { "1.2.3.", STATUS_INVALID_PARAMETER, 6, { -1 } }, { "203569230", STATUS_SUCCESS, 9, { 12, 34, 56, 78 }, strict_diff_4, STATUS_INVALID_PARAMETER, 9, { -1 } }, { "1.223756", STATUS_SUCCESS, 8, { 1, 3, 106, 12 }, strict_diff_4, STATUS_INVALID_PARAMETER, 8, { -1 } }, { "3.4.756", STATUS_SUCCESS, 7, { 3, 4, 2, 244 }, strict_diff_4, STATUS_INVALID_PARAMETER, 7, { -1 } }, + { "756.3.4", STATUS_INVALID_PARAMETER, 7, { -1 } }, + { "3.756.4", STATUS_INVALID_PARAMETER, 7, { -1 } }, { "3.4.756.1", STATUS_INVALID_PARAMETER, 9, { -1 } }, { "3.4.65536", STATUS_INVALID_PARAMETER, 9, { -1 } }, { "3.4.5.6.7", STATUS_INVALID_PARAMETER, 7, { -1 } }, @@ -1241,13 +1047,30 @@ static struct { ".1", STATUS_INVALID_PARAMETER, 1, { -1 } }, { ".1.", STATUS_INVALID_PARAMETER, 1, { -1 } }, { ".1.2.3", STATUS_INVALID_PARAMETER, 1, { -1 } }, + { ".1.2.3.4", STATUS_INVALID_PARAMETER, 1, { -1 } }, { "0.1.2.3", STATUS_SUCCESS, 7, { 0, 1, 2, 3 } }, { "0.1.2.3.", STATUS_INVALID_PARAMETER, 7, { -1 } }, { "[0.1.2.3]", STATUS_INVALID_PARAMETER, 0, { -1 } }, + { "0x00010203", STATUS_SUCCESS, 10, { 0, 1, 2, 3 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 2, { -1 } }, + { "0X00010203", STATUS_SUCCESS, 10, { 0, 1, 2, 3 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 2, { -1 } }, + { "0x1234", STATUS_SUCCESS, 6, { 0, 0, 18, 52 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 2, { -1 } }, + { "0x123456789", STATUS_SUCCESS, 11, { 35, 69, 103, 137 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 2, { -1 } }, + { "0x00010Q03", STATUS_SUCCESS, 7, { 0, 0, 0, 16 }, strict_diff_4 | ex_fail_4, + STATUS_INVALID_PARAMETER, 2, { -1 } }, + { "x00010203", STATUS_INVALID_PARAMETER, 0, { -1 } }, + { "1234BEEF", STATUS_SUCCESS, 4, { 0, 0, 4, 210 }, strict_diff_4 | ex_fail_4, + STATUS_INVALID_PARAMETER, 4, { -1 } }, + { "017700000001", STATUS_SUCCESS, 12, { 127, 0, 0, 1 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 1, { -1 } }, + { "0777", STATUS_SUCCESS, 4, { 0, 0, 1, 255 }, strict_diff_4, + STATUS_INVALID_PARAMETER, 1, { -1 } }, { "::1", STATUS_INVALID_PARAMETER, 0, { -1 } }, { ":1", STATUS_INVALID_PARAMETER, 0, { -1 } }, }; -const unsigned int ipv4_testcount = sizeof(ipv4_tests) / sizeof(ipv4_tests[0]); static void init_ip4(IN_ADDR* addr, const int src[4]) { @@ -1270,36 +1093,30 @@ static void test_RtlIpv4StringToAddress(void) IN_ADDR ip, expected_ip; PCSTR terminator; CHAR dummy; - unsigned int i; - - if (!pRtlIpv4StringToAddressA) - { - skip("RtlIpv4StringToAddress not available\n"); - return; - } + int i; if (0) { /* leaving either parameter NULL crashes on Windows */ - res = pRtlIpv4StringToAddressA(NULL, FALSE, &terminator, &ip); - res = pRtlIpv4StringToAddressA("1.1.1.1", FALSE, NULL, &ip); - res = pRtlIpv4StringToAddressA("1.1.1.1", FALSE, &terminator, NULL); + res = RtlIpv4StringToAddressA(NULL, FALSE, &terminator, &ip); + res = RtlIpv4StringToAddressA("1.1.1.1", FALSE, NULL, &ip); + res = RtlIpv4StringToAddressA("1.1.1.1", FALSE, &terminator, NULL); /* same for the wide char version */ /* - res = pRtlIpv4StringToAddressW(NULL, FALSE, &terminatorW, &ip); - res = pRtlIpv4StringToAddressW(L"1.1.1.1", FALSE, NULL, &ip); - res = pRtlIpv4StringToAddressW(L"1.1.1.1", FALSE, &terminatorW, NULL); + res = RtlIpv4StringToAddressW(NULL, FALSE, &terminatorW, &ip); + res = RtlIpv4StringToAddressW(L"1.1.1.1", FALSE, NULL, &ip); + res = RtlIpv4StringToAddressW(L"1.1.1.1", FALSE, &terminatorW, NULL); */ } - for (i = 0; i < ipv4_testcount; i++) + for (i = 0; i < ARRAY_SIZE(ipv4_tests); i++) { /* non-strict */ terminator = &dummy; ip.S_un.S_addr = 0xabababab; - res = pRtlIpv4StringToAddressA(ipv4_tests[i].address, FALSE, &terminator, &ip); + res = RtlIpv4StringToAddressA(ipv4_tests[i].address, FALSE, &terminator, &ip); ok(res == ipv4_tests[i].res, - "[%s] res = 0x%08x, expected 0x%08x\n", + "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv4_tests[i].address, res, ipv4_tests[i].res); ok(terminator == ipv4_tests[i].address + ipv4_tests[i].terminator_offset, "[%s] terminator = %p, expected %p\n", @@ -1307,7 +1124,7 @@ static void test_RtlIpv4StringToAddress(void) init_ip4(&expected_ip, ipv4_tests[i].ip); ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, - "[%s] ip = %08x, expected %08x\n", + "[%s] ip = %08lx, expected %08lx\n", ipv4_tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); if (!(ipv4_tests[i].flags & strict_diff_4)) @@ -1322,9 +1139,9 @@ static void test_RtlIpv4StringToAddress(void) /* strict */ terminator = &dummy; ip.S_un.S_addr = 0xabababab; - res = pRtlIpv4StringToAddressA(ipv4_tests[i].address, TRUE, &terminator, &ip); + res = RtlIpv4StringToAddressA(ipv4_tests[i].address, TRUE, &terminator, &ip); ok(res == ipv4_tests[i].res_strict, - "[%s] res = 0x%08x, expected 0x%08x\n", + "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv4_tests[i].address, res, ipv4_tests[i].res_strict); ok(terminator == ipv4_tests[i].address + ipv4_tests[i].terminator_offset_strict, "[%s] terminator = %p, expected %p\n", @@ -1332,7 +1149,7 @@ static void test_RtlIpv4StringToAddress(void) init_ip4(&expected_ip, ipv4_tests[i].ip_strict); ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, - "[%s] ip = %08x, expected %08x\n", + "[%s] ip = %08lx, expected %08lx\n", ipv4_tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); } } @@ -1369,13 +1186,12 @@ static void test_RtlIpv4StringToAddressEx(void) { "1.2.3.4: 1234", STATUS_INVALID_PARAMETER, { 1, 2, 3, 4 }, 0xdead }, { "1.2.3.4:\t1234", STATUS_INVALID_PARAMETER, { 1, 2, 3, 4 }, 0xdead }, }; - const unsigned int ipv4_ex_testcount = sizeof(ipv4_ex_tests) / sizeof(ipv4_ex_tests[0]); unsigned int i; BOOLEAN strict; if (!pRtlIpv4StringToAddressExA) { - skip("RtlIpv4StringToAddressEx not available\n"); + win_skip("RtlIpv4StringToAddressEx not available\n"); return; } @@ -1383,27 +1199,27 @@ static void test_RtlIpv4StringToAddressEx(void) ip.S_un.S_addr = 0xabababab; port = 0xdead; res = pRtlIpv4StringToAddressExA(NULL, FALSE, &ip, &port); - ok(res == STATUS_INVALID_PARAMETER, "[null address] res = 0x%08x, expected 0x%08x\n", + ok(res == STATUS_INVALID_PARAMETER, "[null address] res = 0x%08lx, expected 0x%08lx\n", res, STATUS_INVALID_PARAMETER); - ok(ip.S_un.S_addr == 0xabababab, "RtlIpv4StringToAddressExA should not touch the ip!, ip == %x\n", ip.S_un.S_addr); + ok(ip.S_un.S_addr == 0xabababab, "RtlIpv4StringToAddressExA should not touch the ip!, ip == %lx\n", ip.S_un.S_addr); ok(port == 0xdead, "RtlIpv4StringToAddressExA should not touch the port!, port == %x\n", port); port = 0xdead; res = pRtlIpv4StringToAddressExA("1.1.1.1", FALSE, NULL, &port); - ok(res == STATUS_INVALID_PARAMETER, "[null ip] res = 0x%08x, expected 0x%08x\n", + ok(res == STATUS_INVALID_PARAMETER, "[null ip] res = 0x%08lx, expected 0x%08lx\n", res, STATUS_INVALID_PARAMETER); ok(port == 0xdead, "RtlIpv4StringToAddressExA should not touch the port!, port == %x\n", port); ip.S_un.S_addr = 0xabababab; port = 0xdead; res = pRtlIpv4StringToAddressExA("1.1.1.1", FALSE, &ip, NULL); - ok(res == STATUS_INVALID_PARAMETER, "[null port] res = 0x%08x, expected 0x%08x\n", + ok(res == STATUS_INVALID_PARAMETER, "[null port] res = 0x%08lx, expected 0x%08lx\n", res, STATUS_INVALID_PARAMETER); - ok(ip.S_un.S_addr == 0xabababab, "RtlIpv4StringToAddressExA should not touch the ip!, ip == %x\n", ip.S_un.S_addr); + ok(ip.S_un.S_addr == 0xabababab, "RtlIpv4StringToAddressExA should not touch the ip!, ip == %lx\n", ip.S_un.S_addr); ok(port == 0xdead, "RtlIpv4StringToAddressExA should not touch the port!, port == %x\n", port); /* first we run the non-ex testcases on the ex function */ - for (i = 0; i < ipv4_testcount; i++) + for (i = 0; i < ARRAY_SIZE(ipv4_tests); i++) { NTSTATUS expect_res = (ipv4_tests[i].flags & ex_fail_4) ? STATUS_INVALID_PARAMETER : ipv4_tests[i].res; @@ -1411,11 +1227,11 @@ static void test_RtlIpv4StringToAddressEx(void) port = 0xdead; ip.S_un.S_addr = 0xabababab; res = pRtlIpv4StringToAddressExA(ipv4_tests[i].address, FALSE, &ip, &port); - ok(res == expect_res, "[%s] res = 0x%08x, expected 0x%08x\n", + ok(res == expect_res, "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv4_tests[i].address, res, expect_res); init_ip4(&expected_ip, ipv4_tests[i].ip); - ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08x, expected %08x\n", + ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08lx, expected %08lx\n", ipv4_tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); if (!(ipv4_tests[i].flags & strict_diff_4)) @@ -1432,16 +1248,16 @@ static void test_RtlIpv4StringToAddressEx(void) port = 0xdead; ip.S_un.S_addr = 0xabababab; res = pRtlIpv4StringToAddressExA(ipv4_tests[i].address, TRUE, &ip, &port); - ok(res == expect_res, "[%s] res = 0x%08x, expected 0x%08x\n", + ok(res == expect_res, "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv4_tests[i].address, res, expect_res); init_ip4(&expected_ip, ipv4_tests[i].ip_strict); - ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08x, expected %08x\n", + ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08lx, expected %08lx\n", ipv4_tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); } - for (i = 0; i < ipv4_ex_testcount; i++) + for (i = 0; i < ARRAY_SIZE(ipv4_ex_tests); i++) { /* Strict is only relevant for the ip address, so make sure that it does not influence the port */ for (strict = 0; strict < 2; strict++) @@ -1449,11 +1265,11 @@ static void test_RtlIpv4StringToAddressEx(void) ip.S_un.S_addr = 0xabababab; port = 0xdead; res = pRtlIpv4StringToAddressExA(ipv4_ex_tests[i].address, strict, &ip, &port); - ok(res == ipv4_ex_tests[i].res, "[%s] res = 0x%08x, expected 0x%08x\n", + ok(res == ipv4_ex_tests[i].res, "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv4_ex_tests[i].address, res, ipv4_ex_tests[i].res); init_ip4(&expected_ip, ipv4_ex_tests[i].ip); - ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08x, expected %08x\n", + ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08lx, expected %08lx\n", ipv4_ex_tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); ok(port == ipv4_ex_tests[i].port, "[%s] port = %u, expected %u\n", ipv4_ex_tests[i].address, port, ipv4_ex_tests[i].port); @@ -1468,10 +1284,10 @@ static const struct NTSTATUS res; int terminator_offset; int ip[8]; - /* win_broken: older versions of windows do not handle this correct + /* win_broken: XP and Vista do not handle this correctly ex_fail: Ex function does need the string to be terminated, non-Ex does not. - ex_skip: test doesnt make sense for Ex (f.e. it's invalid for non-Ex but valid for Ex) */ - enum { normal_6, win_broken_6 = 1, ex_fail_6 = 2, ex_skip_6 = 4 } flags; + ex_skip: test doesn't make sense for Ex (f.e. it's invalid for non-Ex but valid for Ex) */ + enum { normal_6, win_broken_6 = 1, ex_fail_6 = 2, ex_skip_6 = 4, win_extra_zero = 8 } flags; } ipv6_tests[] = { { "0000:0000:0000:0000:0000:0000:0000:0000", STATUS_SUCCESS, 39, @@ -1482,7 +1298,7 @@ static const struct { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "0:0:0:0:0:0:0:1", STATUS_SUCCESS, 15, { 0, 0, 0, 0, 0, 0, 0, 0x100 } }, - { "0:0:0:0:0:0:0::", STATUS_SUCCESS, 13, + { "0:0:0:0:0:0:0::", STATUS_SUCCESS, 15, { 0, 0, 0, 0, 0, 0, 0, 0 }, win_broken_6 }, { "0:0:0:0:0:0:13.1.68.3", STATUS_SUCCESS, 21, { 0, 0, 0, 0, 0, 0, 0x10d, 0x344 } }, @@ -1498,7 +1314,7 @@ static const struct { 0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700 } }, { "1080:0:0:0:8:800:200c:417a", STATUS_SUCCESS, 26, { 0x8010, 0, 0, 0, 0x800, 0x8, 0x0c20, 0x7a41 } }, - { "0:a:b:c:d:e:f::", STATUS_SUCCESS, 13, + { "0:a:b:c:d:e:f::", STATUS_SUCCESS, 15, { 0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0 }, win_broken_6 }, { "1111:2222:3333:4444:5555:6666:123.123.123.123", STATUS_SUCCESS, 45, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7b7b, 0x7b7b } }, @@ -1512,12 +1328,16 @@ static const struct { 0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1111:2222:3333:4444:r5555:6666:7777:8888", STATUS_INVALID_PARAMETER, 20, { 0x1111, 0x2222, 0x3333, 0x4444, 0xabab, 0xabab, 0xabab, 0xabab } }, - { "1111:2222:3333:4444:5555:6666:7777::", STATUS_SUCCESS, 34, + { "1111:2222:3333:4444:5555:6666:7777::", STATUS_SUCCESS, 36, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0 }, win_broken_6 }, { "1111:2222:3333:4444:5555:6666::", STATUS_SUCCESS, 31, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0 } }, { "1111:2222:3333:4444:5555:6666::8888", STATUS_SUCCESS, 35, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x8888 } }, + { "1111:2222:3333:4444:5555:6666::7777:8888", STATUS_SUCCESS, 35, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x7777 }, ex_fail_6 }, + { "1111:2222:3333:4444:5555:6666:7777::8888", STATUS_SUCCESS, 36, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0 }, ex_fail_6|win_broken_6 }, { "1111:2222:3333:4444:5555::", STATUS_SUCCESS, 26, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0 } }, { "1111:2222:3333:4444:5555::123.123.123.123", STATUS_SUCCESS, 41, @@ -1534,8 +1354,30 @@ static const struct { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8800 }, ex_fail_6 }, { "1111:2222:3333:4444:5555::0x8888", STATUS_SUCCESS, 27, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0x8888 }, ex_fail_6 }, + { "1111:2222:3333:4444:5555::0x80000000", STATUS_SUCCESS, 27, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0, 0, 0xffff }, ex_fail_6 }, + { "1111:2222:3333:4444::5555:0x012345678", STATUS_SUCCESS, 27, + { 0x1111, 0x2222, 0x3333, 0x4444, 0, 0, 0x5555, 0x7856 }, ex_fail_6 }, + { "1111:2222:3333:4444::5555:0x123456789", STATUS_SUCCESS, 27, + { 0x1111, 0x2222, 0x3333, 0x4444, 0, 0, 0x5555, 0xffff }, ex_fail_6 }, + { "1111:2222:3333:4444:5555:6666:0x12345678", STATUS_INVALID_PARAMETER, 31, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0xabab, 0xabab }, ex_fail_6 }, + { "1111:2222:3333:4444:5555:6666:7777:0x80000000", STATUS_SUCCESS, 36, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xffff }, ex_fail_6 }, + { "1111:2222:3333:4444:5555:6666:7777:0x012345678", STATUS_SUCCESS, 36, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x7856 }, ex_fail_6 }, + { "1111:2222:3333:4444:5555:6666:7777:0x123456789", STATUS_SUCCESS, 36, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xffff }, ex_fail_6 }, + { "111:222:333:444:555:666:777:0x123456789abcdef0", STATUS_SUCCESS, 29, + { 0x1101, 0x2202, 0x3303, 0x4404, 0x5505, 0x6606, 0x7707, 0xffff }, ex_fail_6 }, { "1111:2222:3333:4444:5555::08888", STATUS_INVALID_PARAMETER, 31, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab } }, + { "1111:2222:3333:4444:5555::08888::", STATUS_INVALID_PARAMETER, 31, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab } }, + { "1111:2222:3333:4444:5555:6666:7777:fffff:", STATUS_INVALID_PARAMETER, 40, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0xabab } }, + { "1111:2222:3333:4444:5555:6666::fffff:", STATUS_INVALID_PARAMETER, 36, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0xabab, 0xabab } }, { "1111:2222:3333:4444:5555::fffff", STATUS_INVALID_PARAMETER, 31, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0xabab, 0xabab, 0xabab } }, { "1111:2222:3333:4444::fffff", STATUS_INVALID_PARAMETER, 26, @@ -1598,6 +1440,8 @@ static const struct { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "::/16", STATUS_SUCCESS, 2, { 0, 0, 0, 0, 0, 0, 0, 0 }, ex_fail_6 }, + { "::01234", STATUS_INVALID_PARAMETER, 7, + { 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "::0", STATUS_SUCCESS, 3, { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "::0:0", STATUS_SUCCESS, 5, @@ -1610,12 +1454,12 @@ static const struct { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "::0:0:0:0:0:0", STATUS_SUCCESS, 13, { 0, 0, 0, 0, 0, 0, 0, 0 } }, - /* this one and the next one are incorrectly parsed by windows, + /* this one and the next one are incorrectly parsed before Windows 11, it adds one zero too many in front, cutting off the last digit. */ - { "::0:0:0:0:0:0:0", STATUS_SUCCESS, 13, - { 0, 0, 0, 0, 0, 0, 0, 0 }, ex_fail_6 }, - { "::0:a:b:c:d:e:f", STATUS_SUCCESS, 13, - { 0, 0, 0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00 }, ex_fail_6 }, + { "::0:0:0:0:0:0:0", STATUS_SUCCESS, 15, + { 0, 0, 0, 0, 0, 0, 0, 0 }, win_broken_6|win_extra_zero }, + { "::0:a:b:c:d:e:f", STATUS_SUCCESS, 15, + { 0, 0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00 }, win_broken_6|win_extra_zero }, { "::123.123.123.123", STATUS_SUCCESS, 17, { 0, 0, 0, 0, 0, 0, 0x7b7b, 0x7b7b } }, { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", STATUS_SUCCESS, 39, @@ -1631,6 +1475,12 @@ static const struct { 0x120, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "2001:0000:01234:0000:0000:C1C0:ABCD:0876", STATUS_INVALID_PARAMETER, -1, { 0x120, 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "2001:0000::01234.0", STATUS_INVALID_PARAMETER, -1, + { 0x120, 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "2001:0::b.0", STATUS_SUCCESS, 9, + { 0x120, 0, 0, 0, 0, 0, 0, 0xb00 }, ex_fail_6 }, + { "2001::0:b.0", STATUS_SUCCESS, 9, + { 0x120, 0, 0, 0, 0, 0, 0, 0xb00 }, ex_fail_6 }, { "1.2.3.4", STATUS_INVALID_PARAMETER, 7, { 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1.2.3.4:1111::5555", STATUS_INVALID_PARAMETER, 7, @@ -1643,16 +1493,24 @@ static const struct { -1 } }, { "1111", STATUS_INVALID_PARAMETER, 4, { -1 } }, + { "0x1111", STATUS_INVALID_PARAMETER, 1, + { -1 } }, { "1111:22223333:4444:5555:6666:1.2.3.4", STATUS_INVALID_PARAMETER, -1, { 0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1111:22223333:4444:5555:6666:7777:8888", STATUS_INVALID_PARAMETER, -1, { 0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1111:123456789:4444:5555:6666:7777:8888", STATUS_INVALID_PARAMETER, -1, + { 0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1111:1234567890abcdef0:4444:5555:6666:7777:888", STATUS_INVALID_PARAMETER, -1, + { 0x1111, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1111:2222:", STATUS_INVALID_PARAMETER, 10, { 0x1111, 0x2222, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1111:2222:1.2.3.4", STATUS_INVALID_PARAMETER, 17, { 0x1111, 0x2222, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1111:2222:3333", STATUS_INVALID_PARAMETER, 14, { 0x1111, 0x2222, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1111:2222:3333:4444:5555:6666::1.2.3.4", STATUS_SUCCESS, 32, + { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0, 0x100 }, ex_fail_6 }, { "1111:2222:3333:4444:5555:6666:7777:1.2.3.4", STATUS_SUCCESS, 36, { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x100 }, ex_fail_6 }, { "1111:2222:3333:4444:5555:6666:7777:8888:", STATUS_SUCCESS, 39, @@ -1673,12 +1531,30 @@ static const struct { 0x1111, 0, 0, 0, 0, 0, 0, 0x3333 }, ex_fail_6 }, { "12345::6:7:8", STATUS_INVALID_PARAMETER, -1, { -1 } }, + { "1::001.2.3.4", STATUS_SUCCESS, 12, + { 0x100, 0, 0, 0, 0, 0, 0x201, 0x403 } }, + { "1::1.002.3.4", STATUS_SUCCESS, 12, + { 0x100, 0, 0, 0, 0, 0, 0x201, 0x403 } }, + { "1::0001.2.3.4", STATUS_INVALID_PARAMETER, -1, + { 0x100, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.0002.3.4", STATUS_INVALID_PARAMETER, -1, + { 0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.2.256.4", STATUS_INVALID_PARAMETER, -1, { 0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2.4294967296.4", STATUS_INVALID_PARAMETER, -1, + { 0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2.18446744073709551616.4", STATUS_INVALID_PARAMETER, -1, + { 0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.2.3.256", STATUS_INVALID_PARAMETER, 12, { 0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2.3.4294967296", STATUS_INVALID_PARAMETER, 19, + { 0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2.3.18446744073709551616", STATUS_INVALID_PARAMETER, 29, + { 0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.2.3.300", STATUS_INVALID_PARAMETER, 12, { 0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2.3.300.", STATUS_INVALID_PARAMETER, 12, + { 0x100, 0x201, 0xab03, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.2::1", STATUS_INVALID_PARAMETER, 6, { 0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.2.3.4::1", STATUS_SUCCESS, 10, @@ -1701,8 +1577,14 @@ static const struct { 0x100, 0x201, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::1.256.3.4", STATUS_INVALID_PARAMETER, -1, { 0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.256:3.4", STATUS_INVALID_PARAMETER, 8, + { 0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1.2a.3.4", STATUS_INVALID_PARAMETER, 6, + { 0x100, 0xab01, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, { "1::256.2.3.4", STATUS_INVALID_PARAMETER, -1, { 0x100, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "1::1a.2.3.4", STATUS_SUCCESS, 5, + { 0x100, 0, 0, 0, 0, 0, 0, 0x1a00 }, ex_fail_6 }, { "1::2::3", STATUS_SUCCESS, 4, { 0x100, 0, 0, 0, 0, 0, 0, 0x200 }, ex_fail_6 }, { "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", STATUS_INVALID_PARAMETER, 15, @@ -1723,6 +1605,20 @@ static const struct { -1 } }, { "::-1", STATUS_SUCCESS, 2, { 0, 0, 0, 0, 0, 0, 0, 0 }, ex_fail_6 }, + { "::12345678", STATUS_INVALID_PARAMETER, 10, + { 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "::123456789", STATUS_INVALID_PARAMETER, 11, + { 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "::1234567890abcdef0", STATUS_INVALID_PARAMETER, 19, + { 0, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab, 0xabab } }, + { "::0x80000000", STATUS_SUCCESS, 3, + { 0, 0, 0, 0, 0, 0, 0, 0xffff }, ex_fail_6 }, + { "::0x012345678", STATUS_SUCCESS, 3, + { 0, 0, 0, 0, 0, 0, 0, 0x7856 }, ex_fail_6 }, + { "::0x123456789", STATUS_SUCCESS, 3, + { 0, 0, 0, 0, 0, 0, 0, 0xffff }, ex_fail_6 }, + { "::0x1234567890abcdef0", STATUS_SUCCESS, 3, + { 0, 0, 0, 0, 0, 0, 0, 0xffff }, ex_fail_6 }, { "::.", STATUS_SUCCESS, 2, { 0, 0, 0, 0, 0, 0, 0, 0 }, ex_fail_6 }, { "::..", STATUS_SUCCESS, 2, @@ -1734,7 +1630,6 @@ static const struct { "[::]", STATUS_INVALID_PARAMETER, 0, { -1 }, ex_skip_6 }, }; -const unsigned int ipv6_testcount = sizeof(ipv6_tests) / sizeof(ipv6_tests[0]); static void init_ip6(IN6_ADDR* addr, const int src[8]) { @@ -1757,7 +1652,7 @@ static void test_RtlIpv6AddressToString(void) LPCSTR result; IN6_ADDR ip; DWORD_PTR len; - struct + static const struct { PCSTR address; int ip[8]; @@ -1765,28 +1660,40 @@ static void test_RtlIpv6AddressToString(void) { /* ipv4 addresses & ISATAP addresses */ { "::13.1.68.3", { 0, 0, 0, 0, 0, 0, 0x10d, 0x344 } }, + { "::123.123.123.123", { 0, 0, 0, 0, 0, 0, 0x7b7b, 0x7b7b } }, + { "::ffff", { 0, 0, 0, 0, 0, 0, 0, 0xffff } }, + { "::0.1.0.0", { 0, 0, 0, 0, 0, 0, 0x100, 0 } }, { "::ffff:13.1.68.3", { 0, 0, 0, 0, 0, 0xffff, 0x10d, 0x344 } }, { "::feff:d01:4403", { 0, 0, 0, 0, 0, 0xfffe, 0x10d, 0x344 } }, { "::fffe:d01:4403", { 0, 0, 0, 0, 0, 0xfeff, 0x10d, 0x344 } }, { "::100:d01:4403", { 0, 0, 0, 0, 0, 1, 0x10d, 0x344 } }, { "::1:d01:4403", { 0, 0, 0, 0, 0, 0x100, 0x10d, 0x344 } }, + { "::1:0:d01:4403", { 0, 0, 0, 0, 0x100, 0, 0x10d, 0x344 } }, + { "::fffe:d01:4403", { 0, 0, 0, 0, 0, 0xfeff, 0x10d, 0x344 } }, + { "::fffe:0:d01:4403", { 0, 0, 0, 0, 0xfeff, 0, 0x10d, 0x344 } }, { "::ffff:0:4403", { 0, 0, 0, 0, 0, 0xffff, 0, 0x344 } }, + { "::ffff:0.1.0.0", { 0, 0, 0, 0, 0, 0xffff, 0x100, 0 } }, { "::ffff:13.1.0.0", { 0, 0, 0, 0, 0, 0xffff, 0x10d, 0 } }, { "::ffff:0:0", { 0, 0, 0, 0, 0, 0xffff, 0, 0 } }, + { "::ffff:0:ffff", { 0, 0, 0, 0, 0, 0xffff, 0, 0xffff } }, + { "::ffff:0:0.1.0.0", { 0, 0, 0, 0, 0xffff, 0, 0x100, 0 } }, { "::ffff:0:13.1.68.3", { 0, 0, 0, 0, 0xffff, 0, 0x10d, 0x344 } }, { "::ffff:ffff:d01:4403", { 0, 0, 0, 0, 0xffff, 0xffff, 0x10d, 0x344 } }, { "::ffff:0:0:d01:4403", { 0, 0, 0, 0xffff, 0, 0, 0x10d, 0x344 } }, { "::ffff:255.255.255.255", { 0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff } }, { "::ffff:129.144.52.38", { 0, 0, 0, 0, 0, 0xffff, 0x9081, 0x2634 } }, + { "::5efe:0.0.0.0", { 0, 0, 0, 0, 0, 0xfe5e, 0, 0 } }, { "::5efe:129.144.52.38", { 0, 0, 0, 0, 0, 0xfe5e, 0x9081, 0x2634 } }, { "1111:2222:3333:4444:0:5efe:129.144.52.38", { 0x1111, 0x2222, 0x3333, 0x4444, 0, 0xfe5e, 0x9081, 0x2634 } }, { "1111:2222:3333::5efe:129.144.52.38", { 0x1111, 0x2222, 0x3333, 0, 0, 0xfe5e, 0x9081, 0x2634 } }, { "1111:2222::5efe:129.144.52.38", { 0x1111, 0x2222, 0, 0, 0, 0xfe5e, 0x9081, 0x2634 } }, { "1111::5efe:129.144.52.38", { 0x1111, 0, 0, 0, 0, 0xfe5e, 0x9081, 0x2634 } }, + { "::300:5efe:8190:3426", { 0, 0, 0, 0, 3, 0xfe5e, 0x9081, 0x2634 } }, { "::200:5efe:129.144.52.38", { 0, 0, 0, 0, 2, 0xfe5e, 0x9081, 0x2634 } }, { "::100:5efe:8190:3426", { 0, 0, 0, 0, 1, 0xfe5e, 0x9081, 0x2634 } }, /* 'normal' addresses */ { "::1", { 0, 0, 0, 0, 0, 0, 0, 0x100 } }, + { "::2", { 0, 0, 0, 0, 0, 0, 0, 0x200 } }, { "0:1:2:3:4:5:6:7", { 0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700 } }, { "1080::8:800:200c:417a", { 0x8010, 0, 0, 0, 0x800, 0x8, 0x0c20, 0x7a41 } }, { "1111:2222:3333:4444:5555:6666:7b7b:7b7b", { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7b7b, 0x7b7b } }, @@ -1821,7 +1728,6 @@ static void test_RtlIpv6AddressToString(void) { "2001:db8:85a3::8a2e:370:7334", { 0x120, 0xb80d, 0xa385, 0, 0, 0x2e8a, 0x7003, 0x3473 } }, { "3ffe:b00::1:0:0:a", { 0xfe3f, 0xb, 0, 0, 0x100, 0, 0, 0xa00 } }, { "::a:b:c:d:e", { 0, 0, 0, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00 } }, - { "::123.123.123.123", { 0, 0, 0, 0, 0, 0, 0x7b7b, 0x7b7b } }, { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff } }, { "1111:2222:3333:4444:5555:6666:7777:1", { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x100 } }, { "1111:2222:3333:4444:5555:6666:7777:8888", { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888 } }, @@ -1832,35 +1738,28 @@ static void test_RtlIpv6AddressToString(void) { "2001:0:1234::c1c0:abcd:876", { 0x120, 0, 0x3412, 0, 0, 0xc0c1, 0xcdab, 0x7608 } }, { "2001::ffd3", { 0x120, 0, 0, 0, 0, 0, 0, 0xd3ff } }, }; - const size_t testcount = sizeof(tests) / sizeof(tests[0]); unsigned int i; - if (!pRtlIpv6AddressToStringA) - { - skip("RtlIpv6AddressToStringA not available\n"); - return; - } - memset(buffer, '#', sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; memset(&ip, 0, sizeof(ip)); - result = pRtlIpv6AddressToStringA(&ip, buffer); + result = RtlIpv6AddressToStringA(&ip, buffer); len = strlen(buffer); ok(result == (buffer + len) && !strcmp(buffer, "::"), "got %p with '%s' (expected %p with '::')\n", result, buffer, buffer + len); - result = pRtlIpv6AddressToStringA(&ip, NULL); + result = RtlIpv6AddressToStringA(&ip, NULL); ok(result == (LPCSTR)~0 || broken(result == (LPCSTR)len) /* WinXP / Win2k3 */, "got %p, expected %p\n", result, (LPCSTR)~0); - for (i = 0; i < testcount; i++) + for (i = 0; i < ARRAY_SIZE(tests); i++) { init_ip6(&ip, tests[i].ip); memset(buffer, '#', sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; - result = pRtlIpv6AddressToStringA(&ip, buffer); + result = RtlIpv6AddressToStringA(&ip, buffer); len = strlen(buffer); ok(result == (buffer + len) && !strcmp(buffer, tests[i].address), "got %p with '%s' (expected %p with '%s')\n", result, buffer, buffer + len, tests[i].address); @@ -1877,7 +1776,7 @@ static void test_RtlIpv6AddressToStringEx(void) NTSTATUS res; IN6_ADDR ip; ULONG len; - struct + static const struct { PCSTR address; ULONG scopeid; @@ -1935,12 +1834,11 @@ static void test_RtlIpv6AddressToStringEx(void) { "[2001::ffd3%4294949819]:256", 0xffffbbbb, 1, { 0x120, 0, 0, 0, 0, 0, 0, 0xd3ff } }, { "[2001::ffd3]:256", 0, 1, { 0x120, 0, 0, 0, 0, 0, 0, 0xd3ff } }, }; - const size_t testcount = sizeof(tests) / sizeof(tests[0]); unsigned int i; if (!pRtlIpv6AddressToStringExA) { - skip("RtlIpv6AddressToStringExA not available\n"); + win_skip("RtlIpv6AddressToStringExA not available\n"); return; } @@ -1950,33 +1848,33 @@ static void test_RtlIpv6AddressToStringEx(void) len = sizeof(buffer); res = pRtlIpv6AddressToStringExA(&ip, 0, 0, buffer, &len); - ok(res == STATUS_SUCCESS, "[validate] res = 0x%08x, expected STATUS_SUCCESS\n", res); + ok(res == STATUS_SUCCESS, "[validate] res = 0x%08lx, expected STATUS_SUCCESS\n", res); ok(len == 3 && !strcmp(buffer, "::"), - "got len %d with '%s' (expected 3 with '::')\n", len, buffer); + "got len %ld with '%s' (expected 3 with '::')\n", len, buffer); memset(buffer, '#', sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; len = sizeof(buffer); res = pRtlIpv6AddressToStringExA(NULL, 0, 0, buffer, &len); - ok(res == STATUS_INVALID_PARAMETER, "[null ip] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); + ok(res == STATUS_INVALID_PARAMETER, "[null ip] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); len = sizeof(buffer); res = pRtlIpv6AddressToStringExA(&ip, 0, 0, NULL, &len); - ok(res == STATUS_INVALID_PARAMETER, "[null buffer] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); + ok(res == STATUS_INVALID_PARAMETER, "[null buffer] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); res = pRtlIpv6AddressToStringExA(&ip, 0, 0, buffer, NULL); - ok(res == STATUS_INVALID_PARAMETER, "[null length] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); + ok(res == STATUS_INVALID_PARAMETER, "[null length] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); len = 2; memset(buffer, '#', sizeof(buffer)); buffer[sizeof(buffer)-1] = 0; res = pRtlIpv6AddressToStringExA(&ip, 0, 0, buffer, &len); - ok(res == STATUS_INVALID_PARAMETER, "[null length] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); + ok(res == STATUS_INVALID_PARAMETER, "[null length] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); ok(buffer[0] == '#', "got first char %c (expected '#')\n", buffer[0]); - ok(len == 3, "got len %d (expected len 3)\n", len); + ok(len == 3, "got len %ld (expected len 3)\n", len); - for (i = 0; i < testcount; i++) + for (i = 0; i < ARRAY_SIZE(tests); i++) { init_ip6(&ip, tests[i].ip); len = sizeof(buffer); @@ -1985,9 +1883,9 @@ static void test_RtlIpv6AddressToStringEx(void) res = pRtlIpv6AddressToStringExA(&ip, tests[i].scopeid, tests[i].port, buffer, &len); - ok(res == STATUS_SUCCESS, "[validate] res = 0x%08x, expected STATUS_SUCCESS\n", res); + ok(res == STATUS_SUCCESS, "[validate] res = 0x%08lx, expected STATUS_SUCCESS\n", res); ok(len == (strlen(tests[i].address) + 1) && !strcmp(buffer, tests[i].address), - "got len %d with '%s' (expected %d with '%s')\n", len, buffer, (int)strlen(tests[i].address), tests[i].address); + "got len %ld with '%s' (expected %d with '%s')\n", len, buffer, (int)strlen(tests[i].address), tests[i].address); } } @@ -1999,15 +1897,12 @@ static void compare_RtlIpv6StringToAddressW(PCSTR name_a, int terminator_offset_ IN6_ADDR ip; PCWSTR terminator; - if (!pRtlIpv6StringToAddressW) - return; - - pRtlMultiByteToUnicodeN(name, sizeof(name), NULL, name_a, strlen(name_a) + 1); + RtlMultiByteToUnicodeN(name, sizeof(name), NULL, name_a, strlen(name_a) + 1); init_ip6(&ip, NULL); terminator = (void *)0xdeadbeef; - res = pRtlIpv6StringToAddressW(name, &terminator, &ip); - ok(res == res_a, "[W:%s] res = 0x%08x, expected 0x%08x\n", name_a, res, res_a); + res = RtlIpv6StringToAddressW(name, &terminator, &ip); + ok(res == res_a, "[W:%s] res = 0x%08lx, expected 0x%08lx\n", name_a, res, res_a); if (terminator_offset_a < 0) { @@ -2038,46 +1933,34 @@ static void test_RtlIpv6StringToAddress(void) PCSTR terminator; unsigned int i; - if (!pRtlIpv6StringToAddressW) - { - skip("RtlIpv6StringToAddressW not available\n"); - /* we can continue, just not test W */ - } - - if (!pRtlIpv6StringToAddressA) - { - skip("RtlIpv6StringToAddressA not available\n"); - return; /* all tests are centered around A, we cannot continue */ - } - - res = pRtlIpv6StringToAddressA("::", &terminator, &ip); - ok(res == STATUS_SUCCESS, "[validate] res = 0x%08x, expected STATUS_SUCCESS\n", res); + res = RtlIpv6StringToAddressA("::", &terminator, &ip); + ok(res == STATUS_SUCCESS, "[validate] res = 0x%08lx, expected STATUS_SUCCESS\n", res); if (0) { /* any of these crash */ - res = pRtlIpv6StringToAddressA(NULL, &terminator, &ip); - ok(res == STATUS_INVALID_PARAMETER, "[null string] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - res = pRtlIpv6StringToAddressA("::", NULL, &ip); - ok(res == STATUS_INVALID_PARAMETER, "[null terminator] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - res = pRtlIpv6StringToAddressA("::", &terminator, NULL); - ok(res == STATUS_INVALID_PARAMETER, "[null result] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); + res = RtlIpv6StringToAddressA(NULL, &terminator, &ip); + ok(res == STATUS_INVALID_PARAMETER, "[null string] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + res = RtlIpv6StringToAddressA("::", NULL, &ip); + ok(res == STATUS_INVALID_PARAMETER, "[null terminator] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + res = RtlIpv6StringToAddressA("::", &terminator, NULL); + ok(res == STATUS_INVALID_PARAMETER, "[null result] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); } /* sanity check */ ok(sizeof(ip) == sizeof(USHORT)* 8, "sizeof(ip)\n"); - for (i = 0; i < ipv6_testcount; i++) + for (i = 0; i < ARRAY_SIZE(ipv6_tests); i++) { init_ip6(&ip, NULL); terminator = (void *)0xdeadbeef; - res = pRtlIpv6StringToAddressA(ipv6_tests[i].address, &terminator, &ip); + res = RtlIpv6StringToAddressA(ipv6_tests[i].address, &terminator, &ip); compare_RtlIpv6StringToAddressW(ipv6_tests[i].address, (terminator != (void *)0xdeadbeef) ? (terminator - ipv6_tests[i].address) : -1, &ip, res); if (ipv6_tests[i].flags & win_broken_6) { ok(res == ipv6_tests[i].res || broken(res == STATUS_INVALID_PARAMETER), - "[%s] res = 0x%08x, expected 0x%08x\n", + "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv6_tests[i].address, res, ipv6_tests[i].res); if (res == STATUS_INVALID_PARAMETER) @@ -2086,7 +1969,7 @@ static void test_RtlIpv6StringToAddress(void) else { ok(res == ipv6_tests[i].res, - "[%s] res = 0x%08x, expected 0x%08x\n", + "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv6_tests[i].address, res, ipv6_tests[i].res); } @@ -2096,28 +1979,34 @@ static void test_RtlIpv6StringToAddress(void) "[%s] terminator = %p, expected it not to change\n", ipv6_tests[i].address, terminator); } - else if (ipv6_tests[i].flags & win_broken_6) - { - PCSTR expected = ipv6_tests[i].address + ipv6_tests[i].terminator_offset; - ok(terminator == expected || broken(terminator == expected + 2), - "[%s] terminator = %p, expected %p\n", - ipv6_tests[i].address, terminator, expected); - } else { - ok(terminator == ipv6_tests[i].address + ipv6_tests[i].terminator_offset, - "[%s] terminator = %p, expected %p\n", - ipv6_tests[i].address, terminator, ipv6_tests[i].address + ipv6_tests[i].terminator_offset); + if (ipv6_tests[i].flags & win_extra_zero) + ok(terminator == ipv6_tests[i].address + ipv6_tests[i].terminator_offset || + broken(terminator != ipv6_tests[i].address + ipv6_tests[i].terminator_offset), + "[%s] terminator = %p, expected %p\n", + ipv6_tests[i].address, terminator, ipv6_tests[i].address + ipv6_tests[i].terminator_offset); + else + ok(terminator == ipv6_tests[i].address + ipv6_tests[i].terminator_offset, + "[%s] terminator = %p, expected %p\n", + ipv6_tests[i].address, terminator, ipv6_tests[i].address + ipv6_tests[i].terminator_offset); } init_ip6(&expected_ip, ipv6_tests[i].ip); - ok(!memcmp(&ip, &expected_ip, sizeof(ip)), - "[%s] ip = %x:%x:%x:%x:%x:%x:%x:%x, expected %x:%x:%x:%x:%x:%x:%x:%x\n", - ipv6_tests[i].address, - ip.s6_words[0], ip.s6_words[1], ip.s6_words[2], ip.s6_words[3], - ip.s6_words[4], ip.s6_words[5], ip.s6_words[6], ip.s6_words[7], - expected_ip.s6_words[0], expected_ip.s6_words[1], expected_ip.s6_words[2], expected_ip.s6_words[3], - expected_ip.s6_words[4], expected_ip.s6_words[5], expected_ip.s6_words[6], expected_ip.s6_words[7]); + if (ipv6_tests[i].flags & win_extra_zero) + ok(!memcmp(&ip, &expected_ip, sizeof(ip)) || broken(memcmp(&ip, &expected_ip, sizeof(ip))), + "[%s] ip = %x:%x:%x:%x:%x:%x:%x:%x, expected %x:%x:%x:%x:%x:%x:%x:%x\n", + ipv6_tests[i].address, ip.s6_words[0], ip.s6_words[1], ip.s6_words[2], ip.s6_words[3], + ip.s6_words[4], ip.s6_words[5], ip.s6_words[6], ip.s6_words[7], + expected_ip.s6_words[0], expected_ip.s6_words[1], expected_ip.s6_words[2], expected_ip.s6_words[3], + expected_ip.s6_words[4], expected_ip.s6_words[5], expected_ip.s6_words[6], expected_ip.s6_words[7]); + else + ok(!memcmp(&ip, &expected_ip, sizeof(ip)), + "[%s] ip = %x:%x:%x:%x:%x:%x:%x:%x, expected %x:%x:%x:%x:%x:%x:%x:%x\n", + ipv6_tests[i].address, ip.s6_words[0], ip.s6_words[1], ip.s6_words[2], ip.s6_words[3], + ip.s6_words[4], ip.s6_words[5], ip.s6_words[6], ip.s6_words[7], + expected_ip.s6_words[0], expected_ip.s6_words[1], expected_ip.s6_words[2], expected_ip.s6_words[3], + expected_ip.s6_words[4], expected_ip.s6_words[5], expected_ip.s6_words[6], expected_ip.s6_words[7]); } } @@ -2132,13 +2021,13 @@ static void compare_RtlIpv6StringToAddressExW(PCSTR name_a, const struct in6_add if (!pRtlIpv6StringToAddressExW) return; - pRtlMultiByteToUnicodeN(name, sizeof(name), NULL, name_a, strlen(name_a) + 1); + RtlMultiByteToUnicodeN(name, sizeof(name), NULL, name_a, strlen(name_a) + 1); init_ip6(&ip, NULL); res = pRtlIpv6StringToAddressExW(name, &ip, &scope, &port); - ok(res == res_a, "[W:%s] res = 0x%08x, expected 0x%08x\n", name_a, res, res_a); - ok(scope == scope_a, "[W:%s] scope = 0x%08x, expected 0x%08x\n", name_a, scope, scope_a); + ok(res == res_a, "[W:%s] res = 0x%08lx, expected 0x%08lx\n", name_a, res, res_a); + ok(scope == scope_a, "[W:%s] scope = 0x%08lx, expected 0x%08lx\n", name_a, scope, scope_a); ok(port == port_a, "[W:%s] port = 0x%08x, expected 0x%08x\n", name_a, port, port_a); ok(!memcmp(&ip, addr_a, sizeof(ip)), @@ -2230,24 +2119,23 @@ static void test_RtlIpv6StringToAddressEx(void) { "[ff01::8:800:200C:417A/16]:8080", STATUS_INVALID_PARAMETER, 0xbadf00d, 0xbeef, { 0x1ff, 0, 0, 0, 0x800, 8, 0xc20, 0x7a41 } }, }; - const unsigned int ipv6_ex_testcount = sizeof(ipv6_ex_tests) / sizeof(ipv6_ex_tests[0]); const char *simple_ip = "::"; unsigned int i; if (!pRtlIpv6StringToAddressExW) { - skip("RtlIpv6StringToAddressExW not available\n"); + win_skip("RtlIpv6StringToAddressExW not available\n"); /* we can continue, just not test W */ } if (!pRtlIpv6StringToAddressExA) { - skip("RtlIpv6StringToAddressExA not available\n"); + win_skip("RtlIpv6StringToAddressExA not available\n"); return; } res = pRtlIpv6StringToAddressExA(simple_ip, &ip, &scope, &port); - ok(res == STATUS_SUCCESS, "[validate] res = 0x%08x, expected STATUS_SUCCESS\n", res); + ok(res == STATUS_SUCCESS, "[validate] res = 0x%08lx, expected STATUS_SUCCESS\n", res); init_ip6(&ip, NULL); init_ip6(&expected_ip, NULL); @@ -2255,8 +2143,8 @@ static void test_RtlIpv6StringToAddressEx(void) port = 0xbeef; res = pRtlIpv6StringToAddressExA(NULL, &ip, &scope, &port); ok(res == STATUS_INVALID_PARAMETER, - "[null string] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - ok(scope == 0xbadf00d, "[null string] scope = 0x%08x, expected 0xbadf00d\n", scope); + "[null string] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + ok(scope == 0xbadf00d, "[null string] scope = 0x%08lx, expected 0xbadf00d\n", scope); ok(port == 0xbeef, "[null string] port = 0x%08x, expected 0xbeef\n", port); ok(!memcmp(&ip, &expected_ip, sizeof(ip)), "[null string] ip is changed, expected it not to change\n"); @@ -2267,8 +2155,8 @@ static void test_RtlIpv6StringToAddressEx(void) port = 0xbeef; res = pRtlIpv6StringToAddressExA(simple_ip, NULL, &scope, &port); ok(res == STATUS_INVALID_PARAMETER, - "[null result] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - ok(scope == 0xbadf00d, "[null result] scope = 0x%08x, expected 0xbadf00d\n", scope); + "[null result] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + ok(scope == 0xbadf00d, "[null result] scope = 0x%08lx, expected 0xbadf00d\n", scope); ok(port == 0xbeef, "[null result] port = 0x%08x, expected 0xbeef\n", port); ok(!memcmp(&ip, &expected_ip, sizeof(ip)), "[null result] ip is changed, expected it not to change\n"); @@ -2278,8 +2166,8 @@ static void test_RtlIpv6StringToAddressEx(void) port = 0xbeef; res = pRtlIpv6StringToAddressExA(simple_ip, &ip, NULL, &port); ok(res == STATUS_INVALID_PARAMETER, - "[null scope] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - ok(scope == 0xbadf00d, "[null scope] scope = 0x%08x, expected 0xbadf00d\n", scope); + "[null scope] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + ok(scope == 0xbadf00d, "[null scope] scope = 0x%08lx, expected 0xbadf00d\n", scope); ok(port == 0xbeef, "[null scope] port = 0x%08x, expected 0xbeef\n", port); ok(!memcmp(&ip, &expected_ip, sizeof(ip)), "[null scope] ip is changed, expected it not to change\n"); @@ -2289,8 +2177,8 @@ static void test_RtlIpv6StringToAddressEx(void) port = 0xbeef; res = pRtlIpv6StringToAddressExA(simple_ip, &ip, &scope, NULL); ok(res == STATUS_INVALID_PARAMETER, - "[null port] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res); - ok(scope == 0xbadf00d, "[null port] scope = 0x%08x, expected 0xbadf00d\n", scope); + "[null port] res = 0x%08lx, expected STATUS_INVALID_PARAMETER\n", res); + ok(scope == 0xbadf00d, "[null port] scope = 0x%08lx, expected 0xbadf00d\n", scope); ok(port == 0xbeef, "[null port] port = 0x%08x, expected 0xbeef\n", port); ok(!memcmp(&ip, &expected_ip, sizeof(ip)), "[null port] ip is changed, expected it not to change\n"); @@ -2298,8 +2186,8 @@ static void test_RtlIpv6StringToAddressEx(void) /* sanity check */ ok(sizeof(ip) == sizeof(USHORT)* 8, "sizeof(ip)\n"); - /* first we run all ip related tests, to make sure someone didnt accidentally reimplement instead of re-use. */ - for (i = 0; i < ipv6_testcount; i++) + /* first we run all ip related tests, to make sure someone didn't accidentally reimplement instead of re-use. */ + for (i = 0; i < ARRAY_SIZE(ipv6_tests); i++) { ULONG scope = 0xbadf00d; USHORT port = 0xbeef; @@ -2315,14 +2203,14 @@ static void test_RtlIpv6StringToAddressEx(void) /* make sure nothing was changed if this function fails. */ if (res == STATUS_INVALID_PARAMETER) { - ok(scope == 0xbadf00d, "[%s] scope = 0x%08x, expected 0xbadf00d\n", + ok(scope == 0xbadf00d, "[%s] scope = 0x%08lx, expected 0xbadf00d\n", ipv6_tests[i].address, scope); ok(port == 0xbeef, "[%s] port = 0x%08x, expected 0xbeef\n", ipv6_tests[i].address, port); } else { - ok(scope != 0xbadf00d, "[%s] scope = 0x%08x, not expected 0xbadf00d\n", + ok(scope != 0xbadf00d, "[%s] scope = 0x%08lx, not expected 0xbadf00d\n", ipv6_tests[i].address, scope); ok(port != 0xbeef, "[%s] port = 0x%08x, not expected 0xbeef\n", ipv6_tests[i].address, port); @@ -2331,14 +2219,14 @@ static void test_RtlIpv6StringToAddressEx(void) if (ipv6_tests[i].flags & win_broken_6) { ok(res == expect_ret || broken(res == STATUS_INVALID_PARAMETER), - "[%s] res = 0x%08x, expected 0x%08x\n", ipv6_tests[i].address, res, expect_ret); + "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv6_tests[i].address, res, expect_ret); if (res == STATUS_INVALID_PARAMETER) continue; } else { - ok(res == expect_ret, "[%s] res = 0x%08x, expected 0x%08x\n", + ok(res == expect_ret, "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv6_tests[i].address, res, expect_ret); } @@ -2358,7 +2246,7 @@ static void test_RtlIpv6StringToAddressEx(void) } /* now we run scope / port related tests */ - for (i = 0; i < ipv6_ex_testcount; i++) + for (i = 0; i < ARRAY_SIZE(ipv6_ex_tests); i++) { scope = 0xbadf00d; port = 0xbeef; @@ -2366,9 +2254,9 @@ static void test_RtlIpv6StringToAddressEx(void) res = pRtlIpv6StringToAddressExA(ipv6_ex_tests[i].address, &ip, &scope, &port); compare_RtlIpv6StringToAddressExW(ipv6_ex_tests[i].address, &ip, res, scope, port); - ok(res == ipv6_ex_tests[i].res, "[%s] res = 0x%08x, expected 0x%08x\n", + ok(res == ipv6_ex_tests[i].res, "[%s] res = 0x%08lx, expected 0x%08lx\n", ipv6_ex_tests[i].address, res, ipv6_ex_tests[i].res); - ok(scope == ipv6_ex_tests[i].scope, "[%s] scope = 0x%08x, expected 0x%08x\n", + ok(scope == ipv6_ex_tests[i].scope, "[%s] scope = 0x%08lx, expected 0x%08lx\n", ipv6_ex_tests[i].address, scope, ipv6_ex_tests[i].scope); ok(port == ipv6_ex_tests[i].port, "[%s] port = 0x%08x, expected 0x%08x\n", ipv6_ex_tests[i].address, port, ipv6_ex_tests[i].port); @@ -2390,12 +2278,6 @@ static void test_LdrAddRefDll(void) NTSTATUS status; BOOL ret; - if (!pLdrAddRefDll) - { - win_skip( "LdrAddRefDll not supported\n" ); - return; - } - mod = LoadLibraryA("comctl32.dll"); ok(mod != NULL, "got %p\n", mod); ret = FreeLibrary(mod); @@ -2407,8 +2289,8 @@ static void test_LdrAddRefDll(void) /* load, addref and release 2 times */ mod = LoadLibraryA("comctl32.dll"); ok(mod != NULL, "got %p\n", mod); - status = pLdrAddRefDll(0, mod); - ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + status = LdrAddRefDll(0, mod); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); ret = FreeLibrary(mod); ok(ret, "got %d\n", ret); @@ -2423,8 +2305,8 @@ static void test_LdrAddRefDll(void) /* pin refcount */ mod = LoadLibraryA("comctl32.dll"); ok(mod != NULL, "got %p\n", mod); - status = pLdrAddRefDll(LDR_ADDREF_DLL_PIN, mod); - ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); + status = LdrAddRefDll(LDR_ADDREF_DLL_PIN, mod); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); ret = FreeLibrary(mod); ok(ret, "got %d\n", ret); @@ -2445,121 +2327,109 @@ static void test_LdrLockLoaderLock(void) ULONG result; NTSTATUS status; - if (!pLdrLockLoaderLock) - { - win_skip("LdrLockLoaderLock() is not available\n"); - return; - } - /* invalid flags */ result = 10; magic = 0xdeadbeef; - status = pLdrLockLoaderLock(0x10, &result, &magic); - ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status); - ok(result == 0, "got %d\n", result); - ok(magic == 0, "got %lx\n", magic); + status = LdrLockLoaderLock(0x10, &result, &magic); + ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08lx\n", status); + ok(result == 0, "got %ld\n", result); + ok(magic == 0, "got %Ix\n", magic); magic = 0xdeadbeef; - status = pLdrLockLoaderLock(0x10, NULL, &magic); - ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status); - ok(magic == 0, "got %lx\n", magic); + status = LdrLockLoaderLock(0x10, NULL, &magic); + ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08lx\n", status); + ok(magic == 0, "got %Ix\n", magic); result = 10; - status = pLdrLockLoaderLock(0x10, &result, NULL); - ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status); - ok(result == 0, "got %d\n", result); + status = LdrLockLoaderLock(0x10, &result, NULL); + ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08lx\n", status); + ok(result == 0, "got %ld\n", result); /* non-blocking mode, result is null */ magic = 0xdeadbeef; - status = pLdrLockLoaderLock(0x2, NULL, &magic); - ok(status == STATUS_INVALID_PARAMETER_2, "got 0x%08x\n", status); - ok(magic == 0, "got %lx\n", magic); + status = LdrLockLoaderLock(0x2, NULL, &magic); + ok(status == STATUS_INVALID_PARAMETER_2, "got 0x%08lx\n", status); + ok(magic == 0, "got %Ix\n", magic); /* magic pointer is null */ result = 10; - status = pLdrLockLoaderLock(0, &result, NULL); - ok(status == STATUS_INVALID_PARAMETER_3, "got 0x%08x\n", status); - ok(result == 0, "got %d\n", result); + status = LdrLockLoaderLock(0, &result, NULL); + ok(status == STATUS_INVALID_PARAMETER_3, "got 0x%08lx\n", status); + ok(result == 0, "got %ld\n", result); /* lock in non-blocking mode */ result = 0; magic = 0; - status = pLdrLockLoaderLock(0x2, &result, &magic); - ok(status == STATUS_SUCCESS, "got 0x%08x\n", status); - ok(result == 1, "got %d\n", result); - ok(magic != 0, "got %lx\n", magic); - pLdrUnlockLoaderLock(0, magic); + status = LdrLockLoaderLock(0x2, &result, &magic); + ok(status == STATUS_SUCCESS, "got 0x%08lx\n", status); + ok(result == 1, "got %ld\n", result); + ok(magic != 0, "got %Ix\n", magic); + LdrUnlockLoaderLock(0, magic); } static void test_RtlCompressBuffer(void) { ULONG compress_workspace, decompress_workspace; - static const UCHAR test_buffer[] = "WineWineWine"; + static UCHAR test_buffer[] = "WineWineWine"; static UCHAR buf1[0x1000], buf2[0x1000]; ULONG final_size, buf_size; UCHAR *workspace = NULL; NTSTATUS status; - if (!pRtlCompressBuffer || !pRtlDecompressBuffer || !pRtlGetCompressionWorkSpaceSize) - { - win_skip("skipping RtlCompressBuffer tests, required functions not available\n"); - return; - } - compress_workspace = decompress_workspace = 0xdeadbeef; - status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace, - &decompress_workspace); - ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); - ok(compress_workspace != 0, "got wrong compress_workspace %u\n", compress_workspace); + status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08lx\n", status); + ok(compress_workspace != 0, "got wrong compress_workspace %lu\n", compress_workspace); workspace = HeapAlloc(GetProcessHeap(), 0, compress_workspace); - ok(workspace != NULL, "HeapAlloc failed %d\n", GetLastError()); + ok(workspace != NULL, "HeapAlloc failed %ld\n", GetLastError()); /* test compression format / engine */ final_size = 0xdeadbeef; - status = pRtlCompressBuffer(COMPRESSION_FORMAT_NONE, test_buffer, sizeof(test_buffer), - buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlCompressBuffer(COMPRESSION_FORMAT_NONE, test_buffer, sizeof(test_buffer), + buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); final_size = 0xdeadbeef; - status = pRtlCompressBuffer(COMPRESSION_FORMAT_DEFAULT, test_buffer, sizeof(test_buffer), - buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlCompressBuffer(COMPRESSION_FORMAT_DEFAULT, test_buffer, sizeof(test_buffer), + buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); final_size = 0xdeadbeef; - status = pRtlCompressBuffer(0xFF, test_buffer, sizeof(test_buffer), - buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); - ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlCompressBuffer(0xFF, test_buffer, sizeof(test_buffer), + buf1, sizeof(buf1) - 1, 4096, &final_size, workspace); + ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); /* test compression */ final_size = 0xdeadbeef; memset(buf1, 0x11, sizeof(buf1)); - status = pRtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer), - buf1, sizeof(buf1), 4096, &final_size, workspace); - ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + status = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer), + buf1, sizeof(buf1), 4096, &final_size, workspace); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08lx\n", status); ok((*(WORD *)buf1 & 0x7000) == 0x3000, "no chunk signature found %04x\n", *(WORD *)buf1); todo_wine - ok(final_size < sizeof(test_buffer), "got wrong final_size %u\n", final_size); + ok(final_size < sizeof(test_buffer), "got wrong final_size %lu\n", final_size); /* test decompression */ buf_size = final_size; final_size = 0xdeadbeef; memset(buf2, 0x11, sizeof(buf2)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf2, sizeof(buf2), - buf1, buf_size, &final_size); - ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); - ok(final_size == sizeof(test_buffer), "got wrong final_size %u\n", final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf2, sizeof(buf2), + buf1, buf_size, &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08lx\n", status); + ok(final_size == sizeof(test_buffer), "got wrong final_size %lu\n", final_size); ok(!memcmp(buf2, test_buffer, sizeof(test_buffer)), "got wrong decoded data\n"); ok(buf2[sizeof(test_buffer)] == 0x11, "too many bytes written\n"); /* buffer too small */ final_size = 0xdeadbeef; memset(buf1, 0x11, sizeof(buf1)); - status = pRtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer), - buf1, 4, 4096, &final_size, workspace); - ok(status == STATUS_BUFFER_TOO_SMALL, "got wrong status 0x%08x\n", status); + status = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, test_buffer, sizeof(test_buffer), + buf1, 4, 4096, &final_size, workspace); + ok(status == STATUS_BUFFER_TOO_SMALL, "got wrong status 0x%08lx\n", status); HeapFree(GetProcessHeap(), 0, workspace); } @@ -2569,38 +2439,32 @@ static void test_RtlGetCompressionWorkSpaceSize(void) ULONG compress_workspace, decompress_workspace; NTSTATUS status; - if (!pRtlGetCompressionWorkSpaceSize) - { - win_skip("RtlGetCompressionWorkSpaceSize is not available\n"); - return; - } - /* test invalid format / engine */ - status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_NONE, &compress_workspace, - &decompress_workspace); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); + status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_NONE, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); - status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_DEFAULT, &compress_workspace, - &decompress_workspace); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); + status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_DEFAULT, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); - status = pRtlGetCompressionWorkSpaceSize(0xFF, &compress_workspace, &decompress_workspace); - ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status); + status = RtlGetCompressionWorkSpaceSize(0xFF, &compress_workspace, &decompress_workspace); + ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08lx\n", status); /* test LZNT1 with normal and maximum compression */ compress_workspace = decompress_workspace = 0xdeadbeef; - status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace, - &decompress_workspace); - ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); - ok(compress_workspace != 0, "got wrong compress_workspace %u\n", compress_workspace); - ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %u\n", decompress_workspace); + status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08lx\n", status); + ok(compress_workspace != 0, "got wrong compress_workspace %lu\n", compress_workspace); + ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %lu\n", decompress_workspace); compress_workspace = decompress_workspace = 0xdeadbeef; - status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, - &compress_workspace, &decompress_workspace); - ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); - ok(compress_workspace != 0, "got wrong compress_workspace %u\n", compress_workspace); - ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %u\n", decompress_workspace); + status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, + &compress_workspace, &decompress_workspace); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08lx\n", status); + ok(compress_workspace != 0, "got wrong compress_workspace %lu\n", compress_workspace); + ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %lu\n", decompress_workspace); } /* helper for test_RtlDecompressBuffer, checks if a chunk is incomplete */ @@ -2630,7 +2494,7 @@ static BOOL is_incomplete_chunk(const UCHAR *compressed, ULONG compressed_size, static void test_RtlDecompressBuffer(void) { - static const struct + static struct { UCHAR compressed[32]; ULONG compressed_size; @@ -2837,69 +2701,63 @@ static void test_RtlDecompressBuffer(void) ULONG final_size; int i; - if (!pRtlDecompressBuffer || !pRtlDecompressFragment) - { - win_skip("RtlDecompressBuffer or RtlDecompressFragment is not available\n"); - return; - } - /* test compression format / engine */ final_size = 0xdeadbeef; - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_NONE, buf, sizeof(buf), test_lznt[0].compressed, - test_lznt[0].compressed_size, &final_size); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_NONE, buf, sizeof(buf), test_lznt[0].compressed, + test_lznt[0].compressed_size, &final_size); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); final_size = 0xdeadbeef; - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_DEFAULT, buf, sizeof(buf), test_lznt[0].compressed, - test_lznt[0].compressed_size, &final_size); - ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_DEFAULT, buf, sizeof(buf), test_lznt[0].compressed, + test_lznt[0].compressed_size, &final_size); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); final_size = 0xdeadbeef; - status = pRtlDecompressBuffer(0xFF, buf, sizeof(buf), test_lznt[0].compressed, - test_lznt[0].compressed_size, &final_size); - ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status); - ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size); + status = RtlDecompressBuffer(0xFF, buf, sizeof(buf), test_lznt[0].compressed, + test_lznt[0].compressed_size, &final_size); + ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08lx\n", status); + ok(final_size == 0xdeadbeef, "got wrong final_size %lu\n", final_size); /* regular tests for RtlDecompressBuffer */ - for (i = 0; i < sizeof(test_lznt) / sizeof(test_lznt[0]); i++) + for (i = 0; i < ARRAY_SIZE(test_lznt); i++) { - trace("Running test %d (compressed_size=%u, uncompressed_size=%u, status=0x%08x)\n", + trace("Running test %d (compressed_size=%lu, uncompressed_size=%lu, status=0x%08lx)\n", i, test_lznt[i].compressed_size, test_lznt[i].uncompressed_size, test_lznt[i].status); /* test with very big buffer */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, - test_lznt[i].compressed_size, &final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, + test_lznt[i].compressed_size, &final_size); ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER && - (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08x\n", i, status); + (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08lx\n", i, status); if (!status) { ok(final_size == test_lznt[i].uncompressed_size, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size); } /* test that modifier for compression engine is ignored */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buf, sizeof(buf), - test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buf, sizeof(buf), + test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER && - (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08x\n", i, status); + (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08lx\n", i, status); if (!status) { ok(final_size == test_lznt[i].uncompressed_size, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size); } /* test with expected output size */ @@ -2907,17 +2765,17 @@ static void test_RtlDecompressBuffer(void) { final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size, - test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); - ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size, + test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08lx\n", i, status); if (!status) { ok(final_size == test_lznt[i].uncompressed_size, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size); } } @@ -2926,102 +2784,100 @@ static void test_RtlDecompressBuffer(void) { final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size - 1, - test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); - if (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_TRUNCATED) - todo_wine - ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status); - else - ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size - 1, + test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size); + ok(status == test_lznt[i].status || + broken(status == STATUS_BAD_COMPRESSION_BUFFER && (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_TRUNCATED)), + "%d: got wrong status 0x%08lx\n", i, status); if (!status) { ok(final_size == test_lznt[i].uncompressed_size - 1, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size - 1), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size - 1] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size - 1); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size - 1); } } /* test with zero output size */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_lznt[i].compressed, - test_lznt[i].compressed_size, &final_size); + status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_lznt[i].compressed, + test_lznt[i].compressed_size, &final_size); if (is_incomplete_chunk(test_lznt[i].compressed, test_lznt[i].compressed_size, FALSE)) - ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status); + ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08lx\n", i, status); else { - ok(status == STATUS_SUCCESS, "%d: got wrong status 0x%08x\n", i, status); - ok(final_size == 0, "%d: got wrong final_size %u\n", i, final_size); + ok(status == STATUS_SUCCESS, "%d: got wrong status 0x%08lx\n", i, status); + ok(final_size == 0, "%d: got wrong final_size %lu\n", i, final_size); ok(buf[0] == 0x11, "%d: buf[0] was modified\n", i); } /* test RtlDecompressFragment with offset = 0 */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, - test_lznt[i].compressed_size, 0, &final_size, workspace); + status = RtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, + test_lznt[i].compressed_size, 0, &final_size, workspace); if (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT) todo_wine - ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status); + ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08lx\n", i, status); else - ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08lx\n", i, status); if (!status) { ok(final_size == test_lznt[i].uncompressed_size, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size); } /* test RtlDecompressFragment with offset = 1 */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, - test_lznt[i].compressed_size, 1, &final_size, workspace); + status = RtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, + test_lznt[i].compressed_size, 1, &final_size, workspace); if (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT) todo_wine - ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status); + ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08lx\n", i, status); else - ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08lx\n", i, status); if (!status) { if (test_lznt[i].uncompressed_size == 0) { todo_wine - ok(final_size == 4095, "%d: got wrong final_size %u\n", i, final_size); + ok(final_size == 4095, "%d: got wrong final_size %lu\n", i, final_size); /* Buffer doesn't contain any useful value on Windows */ ok(buf[4095] == 0x11, "%d: buf[4095] was modified\n", i); } else { ok(final_size == test_lznt[i].uncompressed_size - 1, - "%d: got wrong final_size %u\n", i, final_size); + "%d: got wrong final_size %lu\n", i, final_size); ok(!memcmp(buf, test_lznt[i].uncompressed + 1, test_lznt[i].uncompressed_size - 1), "%d: got wrong decoded data\n", i); ok(buf[test_lznt[i].uncompressed_size - 1] == 0x11, - "%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size - 1); + "%d: buf[%lu] was modified\n", i, test_lznt[i].uncompressed_size - 1); } } /* test RtlDecompressFragment with offset = 4095 */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, - test_lznt[i].compressed_size, 4095, &final_size, workspace); + status = RtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, + test_lznt[i].compressed_size, 4095, &final_size, workspace); if (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT) todo_wine - ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status); + ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08lx\n", i, status); else - ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08lx\n", i, status); if (!status) { todo_wine - ok(final_size == 1, "%d: got wrong final_size %u\n", i, final_size); + ok(final_size == 1, "%d: got wrong final_size %lu\n", i, final_size); todo_wine ok(buf[0] == 0, "%d: padding is not zero\n", i); ok(buf[1] == 0x11, "%d: buf[1] was modified\n", i); @@ -3030,14 +2886,14 @@ static void test_RtlDecompressBuffer(void) /* test RtlDecompressFragment with offset = 4096 */ final_size = 0xdeadbeef; memset(buf, 0x11, sizeof(buf)); - status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, - test_lznt[i].compressed_size, 4096, &final_size, workspace); + status = RtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed, + test_lznt[i].compressed_size, 4096, &final_size, workspace); expected_status = is_incomplete_chunk(test_lznt[i].compressed, test_lznt[i].compressed_size, TRUE) ? test_lznt[i].status : STATUS_SUCCESS; - ok(status == expected_status, "%d: got wrong status 0x%08x, expected 0x%08x\n", i, status, expected_status); + ok(status == expected_status, "%d: got wrong status 0x%08lx, expected 0x%08lx\n", i, status, expected_status); if (!status) { - ok(final_size == 0, "%d: got wrong final_size %u\n", i, final_size); + ok(final_size == 0, "%d: got wrong final_size %lu\n", i, final_size); ok(buf[0] == 0x11, "%d: buf[4096] was modified\n", i); } } @@ -3058,29 +2914,29 @@ static DWORD WINAPI critsect_locked_thread(void *param) DWORD ret; ret = pRtlIsCriticalSectionLocked(&info->crit); - ok(ret == TRUE, "expected TRUE, got %u\n", ret); + ok(ret == TRUE, "expected TRUE, got %lu\n", ret); ret = pRtlIsCriticalSectionLockedByThread(&info->crit); - ok(ret == FALSE, "expected FALSE, got %u\n", ret); + ok(ret == FALSE, "expected FALSE, got %lu\n", ret); ReleaseSemaphore(info->semaphores[0], 1, NULL); ret = WaitForSingleObject(info->semaphores[1], 1000); - ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", ret); + ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %lu\n", ret); ret = pRtlIsCriticalSectionLocked(&info->crit); - ok(ret == FALSE, "expected FALSE, got %u\n", ret); + ok(ret == FALSE, "expected FALSE, got %lu\n", ret); ret = pRtlIsCriticalSectionLockedByThread(&info->crit); - ok(ret == FALSE, "expected FALSE, got %u\n", ret); + ok(ret == FALSE, "expected FALSE, got %lu\n", ret); EnterCriticalSection(&info->crit); ret = pRtlIsCriticalSectionLocked(&info->crit); - ok(ret == TRUE, "expected TRUE, got %u\n", ret); + ok(ret == TRUE, "expected TRUE, got %lu\n", ret); ret = pRtlIsCriticalSectionLockedByThread(&info->crit); - ok(ret == TRUE, "expected TRUE, got %u\n", ret); + ok(ret == TRUE, "expected TRUE, got %lu\n", ret); ReleaseSemaphore(info->semaphores[0], 1, NULL); ret = WaitForSingleObject(info->semaphores[1], 1000); - ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", ret); + ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %lu\n", ret); LeaveCriticalSection(&info->crit); return 0; @@ -3100,9 +2956,9 @@ static void test_RtlIsCriticalSectionLocked(void) InitializeCriticalSection(&info.crit); info.semaphores[0] = CreateSemaphoreW(NULL, 0, 1, NULL); - ok(info.semaphores[0] != NULL, "CreateSemaphore failed with %u\n", GetLastError()); + ok(info.semaphores[0] != NULL, "CreateSemaphore failed with %lu\n", GetLastError()); info.semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); - ok(info.semaphores[1] != NULL, "CreateSemaphore failed with %u\n", GetLastError()); + ok(info.semaphores[1] != NULL, "CreateSemaphore failed with %lu\n", GetLastError()); ret = pRtlIsCriticalSectionLocked(&info.crit); ok(ret == FALSE, "expected FALSE, got %u\n", ret); @@ -3117,7 +2973,7 @@ static void test_RtlIsCriticalSectionLocked(void) ok(ret == TRUE, "expected TRUE, got %u\n", ret); thread = CreateThread(NULL, 0, critsect_locked_thread, &info, 0, NULL); - ok(thread != NULL, "CreateThread failed with %u\n", GetLastError()); + ok(thread != NULL, "CreateThread failed with %lu\n", GetLastError()); ret = WaitForSingleObject(info.semaphores[0], 1000); ok(ret == WAIT_OBJECT_0, "expected WAIT_OBJECT_0, got %u\n", ret); @@ -3155,25 +3011,24 @@ static void test_RtlInitializeCriticalSectionEx(void) memset(&cs, 0x11, sizeof(cs)); pRtlInitializeCriticalSectionEx(&cs, 0, 0); - ok((cs.DebugInfo != NULL && cs.DebugInfo != no_debug) || broken(cs.DebugInfo == no_debug) /* >= Win 8 */, + ok(cs.DebugInfo == no_debug || broken(cs.DebugInfo != NULL && cs.DebugInfo != no_debug) /* < Win8 */, "expected DebugInfo != NULL and DebugInfo != ~0, got %p\n", cs.DebugInfo); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %ld\n", cs.RecursionCount); ok(cs.LockSemaphore == NULL, "expected LockSemaphore == NULL, got %p\n", cs.LockSemaphore); ok(cs.SpinCount == 0 || broken(cs.SpinCount != 0) /* >= Win 8 */, - "expected SpinCount == 0, got %ld\n", cs.SpinCount); - RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&cs); + "expected SpinCount == 0, got %Id\n", cs.SpinCount); + RtlDeleteCriticalSection(&cs); memset(&cs, 0x11, sizeof(cs)); pRtlInitializeCriticalSectionEx(&cs, 0, RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO); - todo_wine ok(cs.DebugInfo == no_debug, "expected DebugInfo == ~0, got %p\n", cs.DebugInfo); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %ld\n", cs.RecursionCount); ok(cs.LockSemaphore == NULL, "expected LockSemaphore == NULL, got %p\n", cs.LockSemaphore); ok(cs.SpinCount == 0 || broken(cs.SpinCount != 0) /* >= Win 8 */, - "expected SpinCount == 0, got %ld\n", cs.SpinCount); - RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)&cs); + "expected SpinCount == 0, got %Id\n", cs.SpinCount); + RtlDeleteCriticalSection(&cs); } static void test_RtlLeaveCriticalSection(void) @@ -3185,19 +3040,19 @@ static void test_RtlLeaveCriticalSection(void) return; /* Skip winxp */ status = RtlInitializeCriticalSection(&cs); - ok(!status, "RtlInitializeCriticalSection failed: %x\n", status); + ok(!status, "RtlInitializeCriticalSection failed: %lx\n", status); status = RtlEnterCriticalSection(&cs); - ok(!status, "RtlEnterCriticalSection failed: %x\n", status); + ok(!status, "RtlEnterCriticalSection failed: %lx\n", status); todo_wine - ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount); + ok(cs.LockCount == -2, "expected LockCount == -2, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %ld\n", cs.RecursionCount); ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n"); status = RtlLeaveCriticalSection(&cs); - ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(!status, "RtlLeaveCriticalSection failed: %lx\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %ld\n", cs.RecursionCount); ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); /* @@ -3205,34 +3060,34 @@ static void test_RtlLeaveCriticalSection(void) * but doesn't modify LockCount so that an attempt to enter the section later will work. */ status = RtlLeaveCriticalSection(&cs); - ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == -1, "expected RecursionCount == -1, got %d\n", cs.RecursionCount); + ok(!status, "RtlLeaveCriticalSection failed: %lx\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == -1, "expected RecursionCount == -1, got %ld\n", cs.RecursionCount); ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); /* and again */ status = RtlLeaveCriticalSection(&cs); - ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == -2, "expected RecursionCount == -2, got %d\n", cs.RecursionCount); + ok(!status, "RtlLeaveCriticalSection failed: %lx\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == -2, "expected RecursionCount == -2, got %ld\n", cs.RecursionCount); ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); /* entering section fixes RecursionCount */ status = RtlEnterCriticalSection(&cs); - ok(!status, "RtlEnterCriticalSection failed: %x\n", status); + ok(!status, "RtlEnterCriticalSection failed: %lx\n", status); todo_wine - ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount); + ok(cs.LockCount == -2, "expected LockCount == -2, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %ld\n", cs.RecursionCount); ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n"); status = RtlLeaveCriticalSection(&cs); - ok(!status, "RtlLeaveCriticalSection failed: %x\n", status); - ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount); - ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount); + ok(!status, "RtlLeaveCriticalSection failed: %lx\n", status); + ok(cs.LockCount == -1, "expected LockCount == -1, got %ld\n", cs.LockCount); + ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %ld\n", cs.RecursionCount); ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread); status = RtlDeleteCriticalSection(&cs); - ok(!status, "RtlDeleteCriticalSection failed: %x\n", status); + ok(!status, "RtlDeleteCriticalSection failed: %lx\n", status); } struct ldr_enum_context @@ -3269,24 +3124,24 @@ static void test_LdrEnumerateLoadedModules(void) ctx.found = FALSE; ctx.count = 0; status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx); - ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status); + ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08lx\n", status); ok(ctx.count > 1, "Expected more than one module, got %d\n", ctx.count); ok(ctx.found, "Could not find ntdll in list of modules\n"); ctx.abort = TRUE; ctx.count = 0; status = pLdrEnumerateLoadedModules(NULL, ldr_enum_callback, &ctx); - ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08x\n", status); + ok(status == STATUS_SUCCESS, "LdrEnumerateLoadedModules failed with %08lx\n", status); ok(ctx.count == 1, "Expected exactly one module, got %d\n", ctx.count); status = pLdrEnumerateLoadedModules((void *)0x1, ldr_enum_callback, (void *)0xdeadbeef); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08lx\n", status); status = pLdrEnumerateLoadedModules((void *)0xdeadbeef, ldr_enum_callback, (void *)0xdeadbeef); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08lx\n", status); status = pLdrEnumerateLoadedModules(NULL, NULL, (void *)0xdeadbeef); - ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got 0x%08lx\n", status); } static void test_RtlMakeSelfRelativeSD(void) @@ -3297,112 +3152,36 @@ static void test_RtlMakeSelfRelativeSD(void) NTSTATUS status; DWORD len; - if (!pRtlMakeSelfRelativeSD || !pRtlAbsoluteToSelfRelativeSD) - { - win_skip( "RtlMakeSelfRelativeSD/RtlAbsoluteToSelfRelativeSD not available\n" ); - return; - } - memset( &sd, 0, sizeof(sd) ); sd.Revision = SECURITY_DESCRIPTOR_REVISION; len = 0; - status = pRtlMakeSelfRelativeSD( &sd, NULL, &len ); - ok( status == STATUS_BUFFER_TOO_SMALL, "got %08x\n", status ); - ok( len == sizeof(*sd_rel), "got %u\n", len ); + status = RtlMakeSelfRelativeSD( &sd, NULL, &len ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %08lx\n", status ); + ok( len == sizeof(*sd_rel), "got %lu\n", len ); len += 4; - status = pRtlMakeSelfRelativeSD( &sd, sd_rel, &len ); - ok( status == STATUS_SUCCESS, "got %08x\n", status ); - ok( len == sizeof(*sd_rel) + 4, "got %u\n", len ); + status = RtlMakeSelfRelativeSD( &sd, sd_rel, &len ); + ok( status == STATUS_SUCCESS, "got %08lx\n", status ); + ok( len == sizeof(*sd_rel) + 4, "got %lu\n", len ); len = 0; - status = pRtlAbsoluteToSelfRelativeSD( &sd, NULL, &len ); - ok( status == STATUS_BUFFER_TOO_SMALL, "got %08x\n", status ); - ok( len == sizeof(*sd_rel), "got %u\n", len ); + status = RtlAbsoluteToSelfRelativeSD( &sd, NULL, &len ); + ok( status == STATUS_BUFFER_TOO_SMALL, "got %08lx\n", status ); + ok( len == sizeof(*sd_rel), "got %lu\n", len ); len += 4; - status = pRtlAbsoluteToSelfRelativeSD( &sd, sd_rel, &len ); - ok( status == STATUS_SUCCESS, "got %08x\n", status ); - ok( len == sizeof(*sd_rel) + 4, "got %u\n", len ); + status = RtlAbsoluteToSelfRelativeSD( &sd, sd_rel, &len ); + ok( status == STATUS_SUCCESS, "got %08lx\n", status ); + ok( len == sizeof(*sd_rel) + 4, "got %lu\n", len ); sd.Control = SE_SELF_RELATIVE; - status = pRtlMakeSelfRelativeSD( &sd, sd_rel, &len ); - ok( status == STATUS_SUCCESS, "got %08x\n", status ); - ok( len == sizeof(*sd_rel) + 4, "got %u\n", len ); + status = RtlMakeSelfRelativeSD( &sd, sd_rel, &len ); + ok( status == STATUS_SUCCESS, "got %08lx\n", status ); + ok( len == sizeof(*sd_rel) + 4, "got %lu\n", len ); - status = pRtlAbsoluteToSelfRelativeSD( &sd, sd_rel, &len ); - ok( status == STATUS_BAD_DESCRIPTOR_FORMAT, "got %08x\n", status ); -} - -static void test_RtlQueryPackageIdentity(void) -{ - const WCHAR programW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.', - 'P','h','o','t','o','s','_','8','w','e','k','y','b','3','d','8','b','b','w','e','!','A','p','p',0}; - const WCHAR fullnameW[] = {'M','i','c','r','o','s','o','f','t','.','W','i','n','d','o','w','s','.', - 'P','h','o','t','o','s', 0}; - const WCHAR appidW[] = {'A','p','p',0}; - IApplicationActivationManager *manager; - WCHAR buf1[MAX_PATH], buf2[MAX_PATH]; - HANDLE process, token; - SIZE_T size1, size2; - NTSTATUS status; - DWORD processid; - HRESULT hr; - BOOL ret; - - if (!pRtlQueryPackageIdentity) - { - win_skip("RtlQueryPackageIdentity not available\n"); - return; - } - - size1 = size2 = MAX_PATH * sizeof(WCHAR); - status = pRtlQueryPackageIdentity((HANDLE)~(ULONG_PTR)3, buf1, &size1, buf2, &size2, NULL); - ok(status == STATUS_NOT_FOUND, "expected STATUS_NOT_FOUND, got %08x\n", status); - - CoInitializeEx(0, COINIT_APARTMENTTHREADED); - hr = CoCreateInstance(&CLSID_ApplicationActivationManager, NULL, CLSCTX_LOCAL_SERVER, - &IID_IApplicationActivationManager, (void **)&manager); - if (FAILED(hr)) - { - todo_wine win_skip("Failed to create ApplicationActivationManager (%x)\n", hr); - goto done; - } - - hr = IApplicationActivationManager_ActivateApplication(manager, programW, NULL, - AO_NOERRORUI, &processid); - if (FAILED(hr)) - { - todo_wine win_skip("Failed to start program (%x)\n", hr); - IApplicationActivationManager_Release(manager); - goto done; - } - - process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, FALSE, processid); - ok(process != NULL, "OpenProcess failed with %u\n", GetLastError()); - ret = OpenProcessToken(process, TOKEN_QUERY, &token); - ok(ret, "OpenProcessToken failed with error %u\n", GetLastError()); - - size1 = size2 = MAX_PATH * sizeof(WCHAR); - status = pRtlQueryPackageIdentity(token, buf1, &size1, buf2, &size2, NULL); - ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08x\n", status); - - ok(!memcmp(buf1, fullnameW, sizeof(fullnameW) - sizeof(WCHAR)), - "Expected buf1 to begin with %s, got %s\n", wine_dbgstr_w(fullnameW), wine_dbgstr_w(buf1)); - ok(size1 >= sizeof(WCHAR) && !(size1 % sizeof(WCHAR)), "Unexpected size1 = %lu\n", size1); - ok(buf1[size1 / sizeof(WCHAR) - 1] == 0, "Expected buf1[%lu] == 0\n", size1 / sizeof(WCHAR) - 1); - - ok(!lstrcmpW(buf2, appidW), "Expected buf2 to be %s, got %s\n", wine_dbgstr_w(appidW), wine_dbgstr_w(buf2)); - ok(size2 >= sizeof(WCHAR) && !(size2 % sizeof(WCHAR)), "Unexpected size2 = %lu\n", size2); - ok(buf2[size2 / sizeof(WCHAR) - 1] == 0, "Expected buf2[%lu] == 0\n", size2 / sizeof(WCHAR) - 1); - - CloseHandle(token); - TerminateProcess(process, 0); - CloseHandle(process); - -done: - CoUninitialize(); + status = RtlAbsoluteToSelfRelativeSD( &sd, sd_rel, &len ); + ok( status == STATUS_BAD_DESCRIPTOR_FORMAT, "got %08lx\n", status ); } static DWORD (CALLBACK *orig_entry)(HMODULE,DWORD,LPVOID); @@ -3418,18 +3197,21 @@ static void CALLBACK ldr_notify_callback1(ULONG reason, LDR_DLL_NOTIFICATION_DAT const IMAGE_IMPORT_DESCRIPTOR *imports; const IMAGE_THUNK_DATA *import_list; IMAGE_THUNK_DATA *thunk_list; + LDR_DATA_TABLE_ENTRY *mod; DWORD *calls = context; LIST_ENTRY *mark; - LDR_DATA_TABLE_ENTRY *mod; ULONG size; int i, j; *calls <<= 4; *calls |= reason; - ok(data->Loaded.Flags == 0, "Expected flags 0, got %x\n", data->Loaded.Flags); - ok(!lstrcmpiW(data->Loaded.BaseDllName->Buffer, ws2_32dllW), "Expected ws2_32.dll, got %s\n", - wine_dbgstr_w(data->Loaded.BaseDllName->Buffer)); + if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll)) + return; + + ok(data->Loaded.Flags == 0, "Expected flags 0, got %lx\n", data->Loaded.Flags); + ok(!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll), "Expected %s, got %s\n", + wine_dbgstr_w(expected_dll), wine_dbgstr_w(data->Loaded.BaseDllName->Buffer)); ok(!!data->Loaded.DllBase, "Expected non zero base address\n"); ok(data->Loaded.SizeOfImage, "Expected non zero image size\n"); @@ -3438,8 +3220,8 @@ static void CALLBACK ldr_notify_callback1(ULONG reason, LDR_DLL_NOTIFICATION_DAT mod = CONTAINING_RECORD(mark->Blink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); ok(mod->DllBase == data->Loaded.DllBase, "Expected base address %p, got %p\n", data->Loaded.DllBase, mod->DllBase); - ok(!lstrcmpiW(mod->BaseDllName.Buffer, ws2_32dllW), "Expected ws2_32.dll, got %s\n", - wine_dbgstr_w(mod->BaseDllName.Buffer)); + ok(!lstrcmpiW(mod->BaseDllName.Buffer, expected_dll), "Expected %s, got %s\n", + wine_dbgstr_w(expected_dll), wine_dbgstr_w(mod->BaseDllName.Buffer)); /* show that imports have already been resolved */ imports = RtlImageDirectoryEntryToData(data->Loaded.DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size); @@ -3579,85 +3361,738 @@ static void test_LdrRegisterDllNotification(void) return; } + mod = LoadLibraryW(expected_dll); + if(mod) + FreeLibrary(mod); + else + expected_dll = ws2_32dllW; /* XP Default */ + /* generic test */ status = pLdrRegisterDllNotification(0, ldr_notify_callback1, &calls, &cookie); - ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(!status, "Expected STATUS_SUCCESS, got %08lx\n", status); calls = 0; - mod = LoadLibraryW(ws2_32dllW); - ok(!!mod, "Failed to load library: %d\n", GetLastError()); - ok(calls == LDR_DLL_NOTIFICATION_REASON_LOADED, "Expected LDR_DLL_NOTIFICATION_REASON_LOADED, got %x\n", calls); + mod = LoadLibraryW(expected_dll); + ok(!!mod, "Failed to load library: %ld\n", GetLastError()); + ok(calls == LDR_DLL_NOTIFICATION_REASON_LOADED, "Expected LDR_DLL_NOTIFICATION_REASON_LOADED, got %lx\n", calls); calls = 0; FreeLibrary(mod); - ok(calls == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Expected LDR_DLL_NOTIFICATION_REASON_UNLOADED, got %x\n", calls); + ok(calls == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Expected LDR_DLL_NOTIFICATION_REASON_UNLOADED, got %lx\n", calls); /* test order of callbacks */ status = pLdrRegisterDllNotification(0, ldr_notify_callback2, &calls, &cookie2); - ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(!status, "Expected STATUS_SUCCESS, got %08lx\n", status); calls = 0; - mod = LoadLibraryW(ws2_32dllW); - ok(!!mod, "Failed to load library: %d\n", GetLastError()); - ok(calls == 0x13, "Expected order 0x13, got %x\n", calls); + mod = LoadLibraryW(expected_dll); + ok(!!mod, "Failed to load library: %ld\n", GetLastError()); + ok(calls == 0x13, "Expected order 0x13, got %lx\n", calls); calls = 0; FreeLibrary(mod); - ok(calls == 0x24, "Expected order 0x24, got %x\n", calls); + ok(calls == 0x24, "Expected order 0x24, got %lx\n", calls); pLdrUnregisterDllNotification(cookie2); pLdrUnregisterDllNotification(cookie); /* test dll main order */ status = pLdrRegisterDllNotification(0, ldr_notify_callback_dll_main, &calls, &cookie); - ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(!status, "Expected STATUS_SUCCESS, got %08lx\n", status); calls = 0; - mod = LoadLibraryW(ws2_32dllW); - ok(!!mod, "Failed to load library: %d\n", GetLastError()); - ok(calls == 0x13, "Expected order 0x13, got %x\n", calls); + mod = LoadLibraryW(expected_dll); + ok(!!mod, "Failed to load library: %ld\n", GetLastError()); + ok(calls == 0x13, "Expected order 0x13, got %lx\n", calls); calls = 0; FreeLibrary(mod); - ok(calls == 0x42, "Expected order 0x42, got %x\n", calls); + ok(calls == 0x42, "Expected order 0x42, got %lx\n", calls); pLdrUnregisterDllNotification(cookie); /* test dll main order */ status = pLdrRegisterDllNotification(0, ldr_notify_callback_fail, &calls, &cookie); - ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(!status, "Expected STATUS_SUCCESS, got %08lx\n", status); calls = 0; - mod = LoadLibraryW(ws2_32dllW); + mod = LoadLibraryW(expected_dll); ok(!mod, "Expected library to fail loading\n"); - ok(calls == 0x1342, "Expected order 0x1342, got %x\n", calls); + ok(calls == 0x1342, "Expected order 0x1342, got %lx\n", calls); pLdrUnregisterDllNotification(cookie); /* test dll with dependencies */ status = pLdrRegisterDllNotification(0, ldr_notify_callback_imports, &calls, &cookie); - ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status); + ok(!status, "Expected STATUS_SUCCESS, got %08lx\n", status); calls = 0; mod = LoadLibraryW(wintrustdllW); - ok(!!mod, "Failed to load library: %d\n", GetLastError()); - ok(calls == 0x12, "Expected order 0x12, got %x\n", calls); + ok(!!mod, "Failed to load library: %ld\n", GetLastError()); + ok(calls == 0x12 || calls == 0x21, "got %lx\n", calls); FreeLibrary(mod); pLdrUnregisterDllNotification(cookie); } +static BOOL test_dbg_print_except; +static LONG test_dbg_print_except_ret; + +static LONG CALLBACK test_dbg_print_except_handler( EXCEPTION_POINTERS *eptrs ) +{ + if (eptrs->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C) + { + ok( eptrs->ExceptionRecord->NumberParameters == 2, + "Unexpected NumberParameters: %ld\n", eptrs->ExceptionRecord->NumberParameters ); + ok( eptrs->ExceptionRecord->ExceptionInformation[0] == strlen("test_DbgPrint: Hello World") + 1, + "Unexpected ExceptionInformation[0]: %d\n", (int)eptrs->ExceptionRecord->ExceptionInformation[0] ); + ok( !strcmp((char *)eptrs->ExceptionRecord->ExceptionInformation[1], "test_DbgPrint: Hello World"), + "Unexpected ExceptionInformation[1]: %s\n", wine_dbgstr_a((char *)eptrs->ExceptionRecord->ExceptionInformation[1]) ); + test_dbg_print_except = TRUE; + return test_dbg_print_except_ret; + } + + return (LONG)EXCEPTION_CONTINUE_SEARCH; +} + +static NTSTATUS WINAPIV test_vDbgPrintEx( ULONG id, ULONG level, const char *fmt, ... ) +{ + NTSTATUS status; + va_list args; + va_start( args, fmt ); + status = vDbgPrintEx( id, level, fmt, args ); + va_end( args ); + return status; +} + +static NTSTATUS WINAPIV test_vDbgPrintExWithPrefix( const char *prefix, ULONG id, ULONG level, const char *fmt, ... ) +{ + NTSTATUS status; + va_list args; + va_start( args, fmt ); + status = vDbgPrintExWithPrefix( prefix, id, level, fmt, args ); + va_end( args ); + return status; +} + +static void test_DbgPrint(void) +{ + NTSTATUS status; + void *handler = RtlAddVectoredExceptionHandler( TRUE, test_dbg_print_except_handler ); + PEB *Peb = NtCurrentTeb()->Peb; + BOOL debugged = Peb->BeingDebugged; + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrint( "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrint returned %lx\n", status ); + ok( !test_dbg_print_except, "DBG_PRINTEXCEPTION_C received\n" ); + + Peb->BeingDebugged = TRUE; + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrint( "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrint returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_CONTINUE_EXECUTION; + status = DbgPrint( "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrint returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_CONTINUE_SEARCH; + status = DbgPrint( "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrint returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + + /* FIXME: NtSetDebugFilterState / DbgSetDebugFilterState are probably what's controlling these */ + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrintEx( 0, DPFLTR_ERROR_LEVEL, "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrintEx returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrintEx( 0, DPFLTR_WARNING_LEVEL, "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrintEx returned %lx\n", status ); + ok( !test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrintEx( 0, DPFLTR_MASK|(1 << DPFLTR_ERROR_LEVEL), "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrintEx returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = DbgPrintEx( 0, DPFLTR_MASK|(1 << DPFLTR_WARNING_LEVEL), "test_DbgPrint: %s", "Hello World" ); + ok( !status, "DbgPrintEx returned %lx\n", status ); + ok( !test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = test_vDbgPrintEx( 0, 0xFFFFFFFF, "test_DbgPrint: %s", "Hello World" ); + ok( !status, "vDbgPrintEx returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + test_dbg_print_except = FALSE; + test_dbg_print_except_ret = (LONG)EXCEPTION_EXECUTE_HANDLER; + status = test_vDbgPrintExWithPrefix( "test_", 0, 0xFFFFFFFF, "DbgPrint: %s", "Hello World" ); + ok( !status, "vDbgPrintExWithPrefix returned %lx\n", status ); + ok( test_dbg_print_except, "DBG_PRINTEXCEPTION_C not received\n" ); + + Peb->BeingDebugged = debugged; + RtlRemoveVectoredExceptionHandler( handler ); +} + +static BOOL test_heap_destroy_dbgstr = FALSE; +static BOOL test_heap_destroy_break = FALSE; + +static LONG CALLBACK test_heap_destroy_except_handler( EXCEPTION_POINTERS *eptrs ) +{ + if (eptrs->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) + { +#if defined( __i386__ ) + eptrs->ContextRecord->Eip += 1; + test_heap_destroy_break = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; +#elif defined( __x86_64__ ) + eptrs->ContextRecord->Rip += 1; + test_heap_destroy_break = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; +#elif defined( __aarch64__ ) + eptrs->ContextRecord->Pc += 4; + test_heap_destroy_break = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; +#endif + } + + if (eptrs->ExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C) + { + test_heap_destroy_dbgstr = TRUE; + return (LONG)EXCEPTION_CONTINUE_EXECUTION; + } + + return (LONG)EXCEPTION_CONTINUE_SEARCH; +} + +/* partially copied from ntdll/heap.c */ +#define HEAP_VALIDATE_PARAMS 0x40000000 + +struct heap +{ + DWORD_PTR unknown1[2]; + DWORD unknown2[2]; + DWORD_PTR unknown3[4]; + DWORD unknown4; + DWORD_PTR unknown5[2]; + DWORD unknown6[3]; + DWORD_PTR unknown7[2]; + DWORD flags; + DWORD force_flags; + DWORD_PTR unknown8[6]; +}; + +static void test_RtlDestroyHeap(void) +{ + const struct heap invalid = {{0, 0}, {0, HEAP_VALIDATE_PARAMS}, {0, 0, 0, 0}, 0, {0, 0}, {0, 0, 0}, {0, 0}, HEAP_VALIDATE_PARAMS, 0, {0}}; + HANDLE heap = (HANDLE)&invalid, ret; + PEB *Peb = NtCurrentTeb()->Peb; + BOOL debugged; + void *handler = RtlAddVectoredExceptionHandler( TRUE, test_heap_destroy_except_handler ); + + test_heap_destroy_dbgstr = FALSE; + test_heap_destroy_break = FALSE; + debugged = Peb->BeingDebugged; + Peb->BeingDebugged = TRUE; + ret = RtlDestroyHeap( heap ); + ok( ret == heap, "RtlDestroyHeap(%p) returned %p\n", heap, ret ); + ok( test_heap_destroy_dbgstr, "HeapDestroy didn't call OutputDebugStrA\n" ); + ok( test_heap_destroy_break, "HeapDestroy didn't call DbgBreakPoint\n" ); + Peb->BeingDebugged = debugged; + + RtlRemoveVectoredExceptionHandler( handler ); +} + +struct commit_routine_context +{ + void *base; + SIZE_T size; +}; + +static struct commit_routine_context commit_context; + +static NTSTATUS NTAPI test_commit_routine(void *base, void **address, SIZE_T *size) +{ + commit_context.base = base; + commit_context.size = *size; + + return VirtualAlloc(*address, *size, MEM_COMMIT, PAGE_READWRITE) ? 0 : STATUS_ASSERTION_FAILURE; +} + +static void test_RtlCreateHeap(void) +{ + void *ptr, *base, *reserve; + RTL_HEAP_PARAMETERS params; + HANDLE heap; + BOOL ret; + + heap = RtlCreateHeap(0, NULL, 0, 0, NULL, NULL); + ok(!!heap, "Failed to create a heap.\n"); + RtlDestroyHeap(heap); + + memset(¶ms, 0, sizeof(params)); + heap = RtlCreateHeap(0, NULL, 0, 0, NULL, ¶ms); + ok(!!heap, "Failed to create a heap.\n"); + RtlDestroyHeap(heap); + + params.Length = 1; + heap = RtlCreateHeap(0, NULL, 0, 0, NULL, ¶ms); + ok(!!heap, "Failed to create a heap.\n"); + RtlDestroyHeap(heap); + + params.Length = sizeof(params); + params.CommitRoutine = test_commit_routine; + params.InitialCommit = 0x1000; + params.InitialReserve = 0x10000; + + heap = RtlCreateHeap(0, NULL, 0, 0, NULL, ¶ms); + todo_wine + ok(!heap, "Unexpected heap.\n"); + if (heap) + RtlDestroyHeap(heap); + + reserve = VirtualAlloc(NULL, 0x10000, MEM_RESERVE, PAGE_READWRITE); + base = VirtualAlloc(reserve, 0x1000, MEM_COMMIT, PAGE_READWRITE); + ok(!!base, "Unexpected pointer.\n"); + + heap = RtlCreateHeap(0, base, 0, 0, NULL, ¶ms); + ok(!!heap, "Unexpected heap.\n"); + + /* Using block size above initially committed size to trigger + new allocation via user callback. */ + ptr = RtlAllocateHeap(heap, 0, 0x4000); + ok(!!ptr, "Failed to allocate a block.\n"); + todo_wine + ok(commit_context.base == base, "Unexpected base %p.\n", commit_context.base); + todo_wine + ok(!!commit_context.size, "Unexpected allocation size.\n"); + RtlFreeHeap(heap, 0, ptr); + RtlDestroyHeap(heap); + + ret = VirtualFree(reserve, 0, MEM_RELEASE); + todo_wine + ok(ret, "Unexpected return value.\n"); +} + +static void test_RtlFirstFreeAce(void) +{ + PACL acl; + PACE_HEADER first; + BOOL ret; + DWORD size; + BOOLEAN found; + + size = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)); + acl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + ret = InitializeAcl(acl, sizeof(ACL), ACL_REVISION); + ok(ret, "InitializeAcl failed with error %ld\n", GetLastError()); + + /* AceCount = 0 */ + first = (ACE_HEADER *)0xdeadbeef; + found = RtlFirstFreeAce(acl, &first); + ok(found, "RtlFirstFreeAce failed\n"); + ok(first == (PACE_HEADER)(acl + 1), "Failed to find ACL\n"); + + acl->AclSize = sizeof(ACL) - 1; + first = (ACE_HEADER *)0xdeadbeef; + found = RtlFirstFreeAce(acl, &first); + ok(found, "RtlFirstFreeAce failed\n"); + ok(first == NULL, "Found FirstAce = %p\n", first); + + /* AceCount = 1 */ + acl->AceCount = 1; + acl->AclSize = size; + first = (ACE_HEADER *)0xdeadbeef; + found = RtlFirstFreeAce(acl, &first); + ok(found, "RtlFirstFreeAce failed\n"); + ok(first == (PACE_HEADER)(acl + 1), "Failed to find ACL %p, %p\n", first, (PACE_HEADER)(acl + 1)); + + acl->AclSize = sizeof(ACL) - 1; + first = (ACE_HEADER *)0xdeadbeef; + found = RtlFirstFreeAce(acl, &first); + ok(!found, "RtlFirstFreeAce failed\n"); + ok(first == NULL, "Found FirstAce = %p\n", first); + + acl->AclSize = sizeof(ACL); + first = (ACE_HEADER *)0xdeadbeef; + found = RtlFirstFreeAce(acl, &first); + ok(!found, "RtlFirstFreeAce failed\n"); + ok(first == NULL, "Found FirstAce = %p\n", first); + + HeapFree(GetProcessHeap(), 0, acl); +} + +static void test_RtlInitializeSid(void) +{ + SID_IDENTIFIER_AUTHORITY sid_ident = { SECURITY_NT_AUTHORITY }; + char buffer[SECURITY_MAX_SID_SIZE]; + PSID sid = (PSID)&buffer; + NTSTATUS status; + + status = RtlInitializeSid(sid, &sid_ident, 1); + ok(!status, "Unexpected status %#lx.\n", status); + + status = RtlInitializeSid(sid, &sid_ident, SID_MAX_SUB_AUTHORITIES); + ok(!status, "Unexpected status %#lx.\n", status); + + status = RtlInitializeSid(sid, &sid_ident, SID_MAX_SUB_AUTHORITIES + 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#lx.\n", status); +} + +static void test_RtlValidSecurityDescriptor(void) +{ + SECURITY_DESCRIPTOR *sd; + NTSTATUS status; + BOOLEAN ret; + + ret = RtlValidSecurityDescriptor(NULL); + ok(!ret, "Unexpected return value %d.\n", ret); + + sd = calloc(1, SECURITY_DESCRIPTOR_MIN_LENGTH); + + ret = RtlValidSecurityDescriptor(sd); + ok(!ret, "Unexpected return value %d.\n", ret); + + status = RtlCreateSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); + ok(!status, "Unexpected return value %#lx.\n", status); + + ret = RtlValidSecurityDescriptor(sd); + ok(ret, "Unexpected return value %d.\n", ret); + + free(sd); +} + +static void test_RtlFindExportedRoutineByName(void) +{ + void *proc; + + if (!pRtlFindExportedRoutineByName) + { + win_skip( "RtlFindExportedRoutineByName is not present\n" ); + return; + } + proc = pRtlFindExportedRoutineByName( GetModuleHandleW( L"kernelbase" ), "CtrlRoutine" ); + ok( proc != NULL, "Expected non NULL address\n" ); + proc = pRtlFindExportedRoutineByName( GetModuleHandleW( L"kernel32" ), "CtrlRoutine" ); + ok( proc == NULL, "Shouldn't find forwarded function\n" ); +} + +static void test_RtlGetDeviceFamilyInfoEnum(void) +{ +#if !defined(__REACTOS__) || _WIN32_WINNT >= _WIN32_WINNT_WIN10 + ULONGLONG version; + DWORD family, form; + + if (!pRtlGetDeviceFamilyInfoEnum) + { + win_skip( "RtlGetDeviceFamilyInfoEnum is not present\n" ); + return; + } + + version = 0x1234567; + family = 1234567; + form = 1234567; + pRtlGetDeviceFamilyInfoEnum(&version, &family, &form); + ok( version != 0x1234567, "got unexpected unchanged value 0x1234567\n" ); + ok( family <= DEVICEFAMILYINFOENUM_MAX, "got unexpected %lu\n", family ); + ok( form <= DEVICEFAMILYDEVICEFORM_MAX, "got unexpected %lu\n", form ); + trace( "UAP version is %#I64x, device family is %lu, form factor is %lu\n", version, family, form ); +#endif +} + +struct test_rb_tree_entry +{ + int value; + struct rb_entry wine_rb_entry; + RTL_BALANCED_NODE rtl_entry; +}; + +static int test_rb_tree_entry_compare( const void *key, const struct wine_rb_entry *entry ) +{ + const struct test_rb_tree_entry *t = WINE_RB_ENTRY_VALUE(entry, struct test_rb_tree_entry, wine_rb_entry); + const int *value = key; + + return *value - t->value; +} + +static int test_rtl_rb_tree_entry_compare( const void *key, const RTL_BALANCED_NODE *entry ) +{ + const struct test_rb_tree_entry *t = CONTAINING_RECORD(entry, struct test_rb_tree_entry, rtl_entry); + const int *value = key; + + return *value - t->value; +} + +static int rtl_rb_tree_put( RTL_RB_TREE *tree, const void *key, RTL_BALANCED_NODE *entry, + int (*compare_func)( const void *key, const RTL_BALANCED_NODE *entry )) +{ + RTL_BALANCED_NODE *parent = tree->root; + BOOLEAN right = 0; + int c; + + while (parent) + { + if (!(c = compare_func( key, parent ))) return -1; + right = c > 0; + if (!parent->Children[right]) break; + parent = parent->Children[right]; + } + pRtlRbInsertNodeEx( tree, parent, right, entry ); + return 0; +} + +static struct test_rb_tree_entry *test_rb_tree_entry_from_wine_rb( struct rb_entry *entry ) +{ + if (!entry) return NULL; + return CONTAINING_RECORD(entry, struct test_rb_tree_entry, wine_rb_entry); +} + +static struct test_rb_tree_entry *test_rb_tree_entry_from_rtl_rb( RTL_BALANCED_NODE *entry ) +{ + if (!entry) return NULL; + return CONTAINING_RECORD(entry, struct test_rb_tree_entry, rtl_entry); +} + +static struct test_rb_tree_entry *test_rb_tree_entry_rtl_parent( struct test_rb_tree_entry *node ) +{ + return test_rb_tree_entry_from_rtl_rb( (void *)(node->rtl_entry.ParentValue + & ~(ULONG_PTR)RTL_BALANCED_NODE_RESERVED_PARENT_MASK) ); +} + +static void test_rb_tree(void) +{ + static int test_values[] = { 44, 51, 6, 66, 69, 20, 87, 80, 72, 86, 90, 16, 54, 61, 62, 14, 27, 39, 42, 41 }; + static const unsigned int count = ARRAY_SIZE(test_values); + + struct test_rb_tree_entry *nodes, *parent, *parent2; + RTL_BALANCED_NODE *prev_min_entry = NULL; + int ret, is_red, min_val; + struct rb_tree wine_tree; + RTL_RB_TREE rtl_tree; + unsigned int i; + + if (!pRtlRbInsertNodeEx) + { + win_skip( "RtlRbInsertNodeEx is not present.\n" ); + return; + } + + memset( &rtl_tree, 0, sizeof(rtl_tree) ); + nodes = malloc( count * sizeof(*nodes) ); + memset( nodes, 0xcc, count * sizeof(*nodes) ); + + min_val = test_values[0]; + rb_init( &wine_tree, test_rb_tree_entry_compare ); + for (i = 0; i < count; ++i) + { + winetest_push_context( "i %u", i ); + nodes[i].value = test_values[i]; + ret = rb_put( &wine_tree, &nodes[i].value, &nodes[i].wine_rb_entry ); + ok( !ret, "got %d.\n", ret ); + parent = test_rb_tree_entry_from_wine_rb( nodes[i].wine_rb_entry.parent ); + ret = rtl_rb_tree_put( &rtl_tree, &nodes[i].value, &nodes[i].rtl_entry, test_rtl_rb_tree_entry_compare ); + ok( !ret, "got %d.\n", ret ); + parent2 = test_rb_tree_entry_rtl_parent( &nodes[i] ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + is_red = nodes[i].rtl_entry.ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK; + ok( is_red == rb_is_red( &nodes[i].wine_rb_entry ), "got %d, expected %d.\n", is_red, + rb_is_red( &nodes[i].wine_rb_entry )); + + parent = test_rb_tree_entry_from_wine_rb( wine_tree.root ); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.root ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + if (nodes[i].value <= min_val) + { + min_val = nodes[i].value; + prev_min_entry = &nodes[i].rtl_entry; + } + ok( rtl_tree.min == prev_min_entry, "unexpected min tree entry.\n" ); + winetest_pop_context(); + } + + for (i = 0; i < count; ++i) + { + struct test_rb_tree_entry *node; + + winetest_push_context( "i %u", i ); + rb_remove( &wine_tree, &nodes[i].wine_rb_entry ); + pRtlRbRemoveNode( &rtl_tree, &nodes[i].rtl_entry ); + + parent = test_rb_tree_entry_from_wine_rb( wine_tree.root ); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.root ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + + parent = test_rb_tree_entry_from_wine_rb( rb_head( wine_tree.root )); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.min ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + + RB_FOR_EACH_ENTRY(node, &wine_tree, struct test_rb_tree_entry, wine_rb_entry) + { + is_red = node->rtl_entry.ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK; + ok( is_red == rb_is_red( &node->wine_rb_entry ), "got %d, expected %d.\n", is_red, rb_is_red( &node->wine_rb_entry )); + parent = test_rb_tree_entry_from_wine_rb( node->wine_rb_entry.parent ); + parent2 = test_rb_tree_entry_rtl_parent( node ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + } + winetest_pop_context(); + } + ok( !rtl_tree.root, "got %p.\n", rtl_tree.root ); + ok( !rtl_tree.min, "got %p.\n", rtl_tree.min ); + free(nodes); +} + +static void test_RtlConvertDeviceFamilyInfoToString(void) +{ + DWORD device_family_size, device_form_size, ret; + WCHAR device_family[16], device_form[16]; + + if (!pRtlConvertDeviceFamilyInfoToString) + { + win_skip("RtlConvertDeviceFamilyInfoToString is unavailable.\n" ); + return; + } + + if (0) /* Crash on Windows */ + { + ret = pRtlConvertDeviceFamilyInfoToString(NULL, NULL, NULL, NULL); + ok(ret == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", ret); + + device_family_size = 0; + ret = pRtlConvertDeviceFamilyInfoToString(&device_family_size, NULL, NULL, NULL); + ok(ret == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#lx.\n", ret); + ok(device_family_size == (wcslen(L"Windows.Desktop") + 1) * sizeof(WCHAR), + "Got unexpected %#lx.\n", device_family_size); + + device_form_size = 0; + ret = pRtlConvertDeviceFamilyInfoToString(NULL, &device_form_size, NULL, NULL); + ok(ret == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#lx.\n", ret); + ok(device_form_size == (wcslen(L"Unknown") + 1) * sizeof(WCHAR), "Got unexpected %#lx.\n", + device_form_size); + + ret = pRtlConvertDeviceFamilyInfoToString(&device_family_size, NULL, device_family, NULL); + ok(ret == STATUS_SUCCESS, "Got unexpected status %#lx.\n", ret); + ok(device_family_size == (wcslen(L"Windows.Desktop") + 1) * sizeof(WCHAR), + "Got unexpected %#lx.\n", device_family_size); + ok(!wcscmp(device_family, L"Windows.Desktop"), "Got unexpected %s.\n", wine_dbgstr_w(device_family)); + + ret = pRtlConvertDeviceFamilyInfoToString(NULL, &device_form_size, NULL, device_form); + ok(ret == STATUS_SUCCESS, "Got unexpected status %#lx.\n", ret); + ok(device_form_size == (wcslen(L"Unknown") + 1) * sizeof(WCHAR), "Got unexpected %#lx.\n", + device_form_size); + ok(!wcscmp(device_form, L"Unknown"), "Got unexpected %s.\n", wine_dbgstr_w(device_form)); + + ret = pRtlConvertDeviceFamilyInfoToString(&device_family_size, &device_form_size, NULL, NULL); + ok(ret == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", ret); + } + + device_family_size = wcslen(L"Windows.Desktop") * sizeof(WCHAR); + device_form_size = wcslen(L"Unknown") * sizeof(WCHAR); + ret = pRtlConvertDeviceFamilyInfoToString(&device_family_size, &device_form_size, NULL, NULL); + ok(ret == STATUS_BUFFER_TOO_SMALL, "Got unexpected status %#lx.\n", ret); + ok(device_family_size == (wcslen(L"Windows.Desktop") + 1) * sizeof(WCHAR), + "Got unexpected %#lx.\n", device_family_size); + ok(device_form_size == (wcslen(L"Unknown") + 1) * sizeof(WCHAR), "Got unexpected %#lx.\n", + device_form_size); + + ret = pRtlConvertDeviceFamilyInfoToString(&device_family_size, &device_form_size, device_family, device_form); + ok(ret == STATUS_SUCCESS, "Got unexpected status %#lx.\n", ret); + ok(!wcscmp(device_family, L"Windows.Desktop"), "Got unexpected %s.\n", wine_dbgstr_w(device_family)); + ok(!wcscmp(device_form, L"Unknown"), "Got unexpected %s.\n", wine_dbgstr_w(device_form)); +} + +static void test_user_procs(void) +{ + UINT64 ptrs[32], dummy[32] = { 0 }; + NTSTATUS status; + const UINT64 *ptr_A, *ptr_W, *ptr_workers; + ULONG size_A, size_W, size_workers; + + if (!pRtlRetrieveNtUserPfn || !pRtlInitializeNtUserPfn) + { + win_skip( "user procs not supported\n" ); + return; + } + + status = pRtlRetrieveNtUserPfn( &ptr_A, &ptr_W, &ptr_workers ); + ok( !status || broken(!is_win64 && status == STATUS_INVALID_PARAMETER), /* <= win8 32-bit */ + "RtlRetrieveNtUserPfn failed %lx\n", status ); + if (status) return; + + /* assume that the tables are consecutive */ + size_A = (ptr_W - ptr_A) * sizeof(UINT64); + size_W = (ptr_workers - ptr_W) * sizeof(UINT64); + ok( size_A > 0x80 && size_A < 0x100, "unexpected size for %p %p %p\n", ptr_A, ptr_W, ptr_workers ); + ok( size_W == size_A, "unexpected size for %p %p %p\n", ptr_A, ptr_W, ptr_workers ); + memcpy( ptrs, ptr_A, size_A ); + + status = pRtlInitializeNtUserPfn( dummy, size_A, dummy + 1, size_W, dummy + 2, 0 ); + ok( status == STATUS_INVALID_PARAMETER, "RtlInitializeNtUserPfn failed %lx\n", status ); + + if (!pRtlResetNtUserPfn) + { + win_skip( "RtlResetNtUserPfn not supported\n" ); + return; + } + + status = pRtlResetNtUserPfn(); + ok( !status, "RtlResetNtUserPfn failed %lx\n", status ); + ok( !memcmp( ptrs, ptr_A, size_A ), "pointers changed by reset\n" ); + + /* can't do anything after reset except set them again */ + status = pRtlResetNtUserPfn(); + ok( status == STATUS_INVALID_PARAMETER, "RtlResetNtUserPfn failed %lx\n", status ); + status = pRtlRetrieveNtUserPfn( &ptr_A, &ptr_W, &ptr_workers ); + ok( status == STATUS_INVALID_PARAMETER, "RtlRetrieveNtUserPfn failed %lx\n", status ); + + for (size_workers = 0x100; size_workers > 0; size_workers--) + { + status = pRtlInitializeNtUserPfn( dummy, size_A, dummy + 1, size_W, dummy + 2, size_workers ); + if (!status) break; + ok( status == STATUS_INVALID_PARAMETER, "RtlInitializeNtUserPfn failed %lx\n", status ); + } + trace( "got sizes %lx %lx %lx\n", size_A, size_W, size_workers ); + if (!size_workers) return; /* something went wrong */ + ok( !memcmp( ptrs, ptr_A, size_A ), "pointers changed by init\n" ); + + /* can't set twice without a reset */ + status = pRtlInitializeNtUserPfn( dummy, size_A, dummy + 1, size_W, dummy + 2, size_workers ); + ok( status == STATUS_INVALID_PARAMETER, "RtlInitializeNtUserPfn failed %lx\n", status ); + status = pRtlResetNtUserPfn(); + ok( !status, "RtlResetNtUserPfn failed %lx\n", status ); + status = pRtlInitializeNtUserPfn( dummy, size_A, dummy + 1, size_W, dummy + 2, size_workers ); + ok( !status, "RtlInitializeNtUserPfn failed %lx\n", status ); + ok( !memcmp( ptrs, ptr_A, size_A ), "pointers changed by init\n" ); +} + START_TEST(rtl) { InitFunctionPtrs(); + test_RtlQueryProcessDebugInformation(); test_RtlCompareMemory(); test_RtlCompareMemoryUlong(); test_RtlMoveMemory(); test_RtlFillMemory(); test_RtlFillMemoryUlong(); test_RtlZeroMemory(); - test_RtlUlonglongByteSwap(); + test_RtlByteSwap(); test_RtlUniform(); test_RtlRandom(); test_RtlAreAllAccessesGranted(); @@ -3685,7 +4120,17 @@ START_TEST(rtl) test_RtlInitializeCriticalSectionEx(); test_RtlLeaveCriticalSection(); test_LdrEnumerateLoadedModules(); - test_RtlQueryPackageIdentity(); test_RtlMakeSelfRelativeSD(); test_LdrRegisterDllNotification(); + test_DbgPrint(); + test_RtlDestroyHeap(); + test_RtlCreateHeap(); + test_RtlFirstFreeAce(); + test_RtlInitializeSid(); + test_RtlValidSecurityDescriptor(); + test_RtlFindExportedRoutineByName(); + test_RtlGetDeviceFamilyInfoEnum(); + test_RtlConvertDeviceFamilyInfoToString(); + test_rb_tree(); + test_user_procs(); } diff --git a/modules/rostests/winetests/ntdll/rtlbitmap.c b/modules/rostests/winetests/ntdll/rtlbitmap.c index 9552318e7f9..520c3fb5bb2 100644 --- a/modules/rostests/winetests/ntdll/rtlbitmap.c +++ b/modules/rostests/winetests/ntdll/rtlbitmap.c @@ -21,7 +21,14 @@ * in later versions of ntdll. */ -#include "ntdll_test.h" +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/test.h" #ifdef __WINE_WINTERNL_H @@ -40,6 +47,8 @@ static CCHAR (WINAPI *pRtlFindMostSignificantBit)(ULONGLONG); static CCHAR (WINAPI *pRtlFindLeastSignificantBit)(ULONGLONG); static ULONG (WINAPI *pRtlFindSetRuns)(PRTL_BITMAP,PRTL_BITMAP_RUN,ULONG,BOOLEAN); static ULONG (WINAPI *pRtlFindClearRuns)(PRTL_BITMAP,PRTL_BITMAP_RUN,ULONG,BOOLEAN); +static ULONG (WINAPI *pRtlFindNextForwardRunSet)(PRTL_BITMAP,ULONG,PULONG); +static ULONG (WINAPI *pRtlFindNextForwardRunClear)(PRTL_BITMAP,ULONG,PULONG); static ULONG (WINAPI *pRtlNumberOfSetBits)(PRTL_BITMAP); static ULONG (WINAPI *pRtlNumberOfClearBits)(PRTL_BITMAP); static ULONG (WINAPI *pRtlFindLongestRunSet)(PRTL_BITMAP,PULONG); @@ -69,6 +78,8 @@ static void InitFunctionPtrs(void) pRtlFindLeastSignificantBit = (void *)GetProcAddress(hntdll, "RtlFindLeastSignificantBit"); pRtlFindSetRuns = (void *)GetProcAddress(hntdll, "RtlFindSetRuns"); pRtlFindClearRuns = (void *)GetProcAddress(hntdll, "RtlFindClearRuns"); + pRtlFindNextForwardRunSet = (void *)GetProcAddress(hntdll, "RtlFindNextForwardRunSet"); + pRtlFindNextForwardRunClear = (void *)GetProcAddress(hntdll, "RtlFindNextForwardRunClear"); pRtlFindLongestRunSet = (void *)GetProcAddress(hntdll, "RtlFindLongestRunSet"); pRtlFindLongestRunClear = (void *)GetProcAddress(hntdll, "RtlFindLongestRunClear"); } @@ -497,7 +508,7 @@ static void test_RtlFindSetRuns(void) /* Get first 2 */ ulCount = pRtlFindSetRuns(&bm, runs, 2, FALSE); - ok(ulCount == 2, "RtlFindClearRuns returned %d, expected 2\n", ulCount); + ok(ulCount == 2, "RtlFindClearRuns returned %ld, expected 2\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101,"bad find\n"); ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 19 + 3,"bad size\n"); @@ -507,7 +518,7 @@ static void test_RtlFindSetRuns(void) /* Get longest 3 */ memset(runs, 0, sizeof(runs)); ulCount = pRtlFindSetRuns(&bm, runs, 2, TRUE); - ok(ulCount == 2, "RtlFindClearRuns returned %d, expected 2\n", ulCount); + ok(ulCount == 2, "RtlFindClearRuns returned %ld, expected 2\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 1877,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 1877,"bad find\n"); ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 33 + 19,"bad size\n"); @@ -517,7 +528,7 @@ static void test_RtlFindSetRuns(void) /* Get all 3 */ memset(runs, 0, sizeof(runs)); ulCount = pRtlFindSetRuns(&bm, runs, 3, TRUE); - ok(ulCount == 3, "RtlFindClearRuns returned %d, expected 3\n", ulCount); + ok(ulCount == 3, "RtlFindClearRuns returned %ld, expected 3\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101 || runs[0].StartingIndex == 1877,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101 || @@ -535,7 +546,7 @@ static void test_RtlFindSetRuns(void) ULONG ulStart = 0; ulCount = pRtlFindLongestRunSet(&bm, &ulStart); - ok(ulCount == 33 && ulStart == 1877,"didn't find longest %d %d\n",ulCount,ulStart); + ok(ulCount == 33 && ulStart == 1877,"didn't find longest %ld %ld\n",ulCount,ulStart); memset(buff, 0, sizeof(buff)); ulCount = pRtlFindLongestRunSet(&bm, &ulStart); @@ -574,7 +585,7 @@ static void test_RtlFindClearRuns(void) /* Get first 2 */ ulCount = pRtlFindClearRuns(&bm, runs, 2, FALSE); - ok(ulCount == 2, "RtlFindClearRuns returned %d, expected 2\n", ulCount); + ok(ulCount == 2, "RtlFindClearRuns returned %ld, expected 2\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101,"bad find\n"); ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 19 + 3,"bad size\n"); @@ -584,7 +595,7 @@ static void test_RtlFindClearRuns(void) /* Get longest 3 */ memset(runs, 0, sizeof(runs)); ulCount = pRtlFindClearRuns(&bm, runs, 2, TRUE); - ok(ulCount == 2, "RtlFindClearRuns returned %d, expected 2\n", ulCount); + ok(ulCount == 2, "RtlFindClearRuns returned %ld, expected 2\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 1877,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 1877,"bad find\n"); ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 33 + 19,"bad size\n"); @@ -594,7 +605,7 @@ static void test_RtlFindClearRuns(void) /* Get all 3 */ memset(runs, 0, sizeof(runs)); ulCount = pRtlFindClearRuns(&bm, runs, 3, TRUE); - ok(ulCount == 3, "RtlFindClearRuns returned %d, expected 3\n", ulCount); + ok(ulCount == 3, "RtlFindClearRuns returned %ld, expected 3\n", ulCount); ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101 || runs[0].StartingIndex == 1877,"bad find\n"); ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101 || @@ -620,6 +631,35 @@ static void test_RtlFindClearRuns(void) } } + +static void test_RtlFindNextForwardRunSet(void) +{ + BYTE mask[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }; + ULONG ulStart = 0; + ULONG ulCount, lpPos; + if (!pRtlFindNextForwardRunSet) + return; + + pRtlInitializeBitMap(&bm, mask, 62); + ulCount = pRtlFindNextForwardRunSet(&bm, ulStart, &lpPos); + ok(ulCount == 6, "Invalid length of found set run: %ld, expected 6\n", ulCount); + ok(lpPos == 56, "Invalid position of found set run: %ld, expected 56\n", lpPos); +} + +static void test_RtlFindNextForwardRunClear(void) +{ + BYTE mask[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + ULONG ulStart = 0; + ULONG ulCount, lpPos; + if (!pRtlFindNextForwardRunClear) + return; + + pRtlInitializeBitMap(&bm, mask, 62); + ulCount = pRtlFindNextForwardRunClear(&bm, ulStart, &lpPos); + ok(ulCount == 6, "Invalid length of found clear run: %ld, expected 6\n", ulCount); + ok(lpPos == 56, "Invalid position of found clear run: %ld, expected 56\n", lpPos); +} + #endif START_TEST(rtlbitmap) @@ -645,6 +685,8 @@ START_TEST(rtlbitmap) test_RtlFindLeastSignificantBit(); test_RtlFindSetRuns(); test_RtlFindClearRuns(); + test_RtlFindNextForwardRunSet(); + test_RtlFindNextForwardRunClear(); } #endif } diff --git a/modules/rostests/winetests/ntdll/rtlstr.c b/modules/rostests/winetests/ntdll/rtlstr.c index 7f889dfa0d0..ffd816265ac 100644 --- a/modules/rostests/winetests/ntdll/rtlstr.c +++ b/modules/rostests/winetests/ntdll/rtlstr.c @@ -23,15 +23,18 @@ */ #include +#include #define INITGUID -#include "ntdll_test.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" #include "winnls.h" #include "guiddef.h" - -#define HASH_STRING_ALGORITHM_X65599 1 -#define HASH_STRING_ALGORITHM_INVALID 0xffffffff +#include "wine/test.h" /* Function ptrs for ntdll calls */ static HMODULE hntdll = 0; @@ -72,6 +75,7 @@ static BOOLEAN (WINAPI *pRtlIsTextUnicode)(LPVOID, INT, INT *); static NTSTATUS (WINAPI *pRtlHashUnicodeString)(PCUNICODE_STRING,BOOLEAN,ULONG,ULONG*); static NTSTATUS (WINAPI *pRtlUnicodeToUTF8N)(CHAR *, ULONG, ULONG *, const WCHAR *, ULONG); static NTSTATUS (WINAPI *pRtlUTF8ToUnicodeN)(WCHAR *, ULONG, ULONG *, const CHAR *, ULONG); +static NTSTATUS (WINAPI *pRtlFormatMessage)(const WCHAR*,ULONG,BOOLEAN,BOOLEAN,BOOLEAN,va_list*,LPWSTR,ULONG,ULONG*); /*static VOID (WINAPI *pRtlFreeOemString)(PSTRING);*/ /*static VOID (WINAPI *pRtlCopyUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *);*/ @@ -144,6 +148,7 @@ static void InitFunctionPtrs(void) pRtlHashUnicodeString = (void*)GetProcAddress(hntdll, "RtlHashUnicodeString"); pRtlUnicodeToUTF8N = (void*)GetProcAddress(hntdll, "RtlUnicodeToUTF8N"); pRtlUTF8ToUnicodeN = (void*)GetProcAddress(hntdll, "RtlUTF8ToUnicodeN"); + pRtlFormatMessage = (void*)GetProcAddress(hntdll, "RtlFormatMessage"); } } @@ -218,7 +223,7 @@ static void test_RtlInitUnicodeStringEx(void) uni.Buffer = (void *) 0xdeadbeef; result = pRtlInitUnicodeStringEx(&uni, teststring); ok(result == STATUS_SUCCESS, - "pRtlInitUnicodeStringEx(&uni, 0) returns %x, expected 0\n", + "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected 0\n", result); ok(uni.Length == 32, "pRtlInitUnicodeStringEx(&uni, 0) sets Length to %u, expected %u\n", @@ -249,7 +254,7 @@ static void test_RtlInitUnicodeStringEx(void) uni.Buffer = (void *) 0xdeadbeef; result = pRtlInitUnicodeStringEx(&uni, teststring2); ok(result == STATUS_NAME_TOO_LONG, - "pRtlInitUnicodeStringEx(&uni, 0) returns %x, expected %x\n", + "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected %lx\n", result, STATUS_NAME_TOO_LONG); ok(uni.Length == 12345 || uni.Length == 0, /* win2k3 */ @@ -285,7 +290,7 @@ static void test_RtlInitUnicodeStringEx(void) uni.Buffer = (void *) 0xdeadbeef; result = pRtlInitUnicodeStringEx(&uni, 0); ok(result == STATUS_SUCCESS, - "pRtlInitUnicodeStringEx(&uni, 0) returns %x, expected 0\n", + "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected 0\n", result); ok(uni.Length == 0, "pRtlInitUnicodeStringEx(&uni, 0) sets Length to %u, expected %u\n", @@ -406,7 +411,6 @@ static const dupl_ustr_t dupl_ustr[] = { { 3, 0, 2, 2, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER}, { 3, 0, 0, 0, NULL, 40, 42, 42, NULL, 0, 2, 2, "", STATUS_SUCCESS}, }; -#define NB_DUPL_USTR (sizeof(dupl_ustr)/sizeof(*dupl_ustr)) static void test_RtlDuplicateUnicodeString(void) @@ -429,7 +433,7 @@ static void test_RtlDuplicateUnicodeString(void) return; } - for (test_num = 0; test_num < NB_DUPL_USTR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(dupl_ustr); test_num++) { source_str.Length = dupl_ustr[test_num].source_Length; source_str.MaximumLength = dupl_ustr[test_num].source_MaximumLength; if (dupl_ustr[test_num].source_buf != NULL) { @@ -469,7 +473,7 @@ static void test_RtlDuplicateUnicodeString(void) dest_ansi_buf[dest_ansi_str.Length] = '\0'; dest_ansi_str.Buffer = dest_ansi_buf; ok(result == dupl_ustr[test_num].result, - "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has result %x, expected %x\n", + "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has result %lx, expected %lx\n", test_num, dupl_ustr[test_num].add_nul, result, dupl_ustr[test_num].result); ok(dest_str.Length == dupl_ustr[test_num].res_Length, "(test %d): RtlDuplicateUnicodeString(%d, source, dest) destination has Length %d, expected %d\n", @@ -607,7 +611,7 @@ static void test_RtlUpcaseUnicodeChar(void) static void test_RtlUpcaseUnicodeString(void) { - int i; + int i, j; WCHAR ch; WCHAR upper_ch; WCHAR ascii_buf[257]; @@ -653,6 +657,25 @@ static void test_RtlUpcaseUnicodeString(void) result_str.Buffer[i], result_str.Buffer[i], upper_str.Buffer[i], upper_str.Buffer[i]); } + + /* test surrogates */ + for (i = 0x100; i < 0x1100; i++) + { + WCHAR src[512], dst[512]; + for (j = 0; j < 256; j++) + { + unsigned int ch = ((i << 8) + j) - 0x10000; + src[2 * j] = 0xd800 | (ch >> 10); + src[2 * j + 1] = 0xdc00 | (ch & 0x3ff); + } + upper_str.Length = upper_str.MaximumLength = 512 * sizeof(WCHAR); + upper_str.Buffer = src; + result_str.Length = result_str.MaximumLength = 512 * sizeof(WCHAR); + result_str.Buffer = dst; + pRtlUpcaseUnicodeString(&result_str, &upper_str, 0); + ok( !memcmp(src, dst, sizeof(dst)), + "string compare mismatch in %04x-%04x\n", i << 8, (i << 8) + 255 ); + } } @@ -694,6 +717,7 @@ static void test_RtlDowncaseUnicodeString(void) case 0x19d: lower_ch = 0x272; break; case 0x19f: lower_ch = 0x275; break; case 0x1a9: lower_ch = 0x283; break; + case 0x1a6: lower_ch = 0x280; break; case 0x1ae: lower_ch = 0x288; break; case 0x1b1: lower_ch = 0x28a; break; case 0x1b2: lower_ch = 0x28b; break; @@ -702,6 +726,16 @@ static void test_RtlDowncaseUnicodeString(void) case 0x1c7: lower_ch = 0x1c9; break; case 0x1ca: lower_ch = 0x1cc; break; case 0x1f1: lower_ch = 0x1f3; break; + case 0x1f6: lower_ch = 0x195; break; + case 0x1f7: lower_ch = 0x1bf; break; + case 0x220: lower_ch = 0x19e; break; + case 0x23a: lower_ch = 0x2c65; break; + case 0x23d: lower_ch = 0x19a; break; + case 0x23e: lower_ch = 0x2c66; break; + case 0x243: lower_ch = 0x180; break; + case 0x244: lower_ch = 0x289; break; + case 0x245: lower_ch = 0x28c; break; + case 0x37f: lower_ch = 0x3f3; break; case 0x386: lower_ch = 0x3ac; break; case 0x388: lower_ch = 0x3ad; break; case 0x389: lower_ch = 0x3ae; break; @@ -709,6 +743,11 @@ static void test_RtlDowncaseUnicodeString(void) case 0x38c: lower_ch = 0x3cc; break; case 0x38e: lower_ch = 0x3cd; break; case 0x38f: lower_ch = 0x3ce; break; + case 0x3cf: lower_ch = 0x3d7; break; + case 0x3f9: lower_ch = 0x3f2; break; + case 0x3fd: lower_ch = 0x37b; break; + case 0x3fe: lower_ch = 0x37c; break; + case 0x3ff: lower_ch = 0x37d; break; default: lower_ch = ch; break; } /* switch */ } @@ -731,7 +770,8 @@ static void test_RtlDowncaseUnicodeString(void) pRtlDowncaseUnicodeString(&result_str, &source_str, 0); for (i = 0; i <= 1024; i++) { - ok(result_str.Buffer[i] == lower_str.Buffer[i] || result_str.Buffer[i] == source_str.Buffer[i] + 1, + ok(result_str.Buffer[i] == lower_str.Buffer[i] || result_str.Buffer[i] == source_str.Buffer[i] + 1 || + broken( result_str.Buffer[i] == source_str.Buffer[i] ), "RtlDowncaseUnicodeString works wrong: '%c'[=0x%x] is converted to '%c'[=0x%x], expected: '%c'[=0x%x]\n", source_str.Buffer[i], source_str.Buffer[i], result_str.Buffer[i], result_str.Buffer[i], @@ -755,6 +795,7 @@ typedef struct { int res_buf_size; const char *res_buf; NTSTATUS result; + int broken_len; } ustr2astr_t; static const ustr2astr_t ustr2astr[] = { @@ -762,7 +803,7 @@ static const ustr2astr_t ustr2astr[] = { { 10, 12, 12, "------------", 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS}, { 0, 2, 12, "------------", 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS}, { 10, 12, 12, NULL, 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS}, - { 0, 0, 12, "------------", 12, 12, 12, "abcdef", FALSE, 6, 0, 0, "", STATUS_BUFFER_OVERFLOW}, + { 0, 0, 12, "------------", 12, 12, 12, "abcdef", FALSE, 6, 0, 0, "", STATUS_BUFFER_OVERFLOW, 1}, { 0, 1, 12, "------------", 12, 12, 12, "abcdef", FALSE, 0, 1, 1, "", STATUS_BUFFER_OVERFLOW}, { 0, 2, 12, "------------", 12, 12, 12, "abcdef", FALSE, 1, 2, 2, "a", STATUS_BUFFER_OVERFLOW}, { 0, 3, 12, "------------", 12, 12, 12, "abcdef", FALSE, 2, 3, 3, "ab", STATUS_BUFFER_OVERFLOW}, @@ -776,7 +817,6 @@ static const ustr2astr_t ustr2astr[] = { { 0, 0, 12, NULL, 10, 10, 12, NULL, FALSE, 5, 0, 0, NULL, STATUS_BUFFER_OVERFLOW}, #endif }; -#define NB_USTR2ASTR (sizeof(ustr2astr)/sizeof(*ustr2astr)) static void test_RtlUnicodeStringToAnsiString(void) @@ -789,7 +829,7 @@ static void test_RtlUnicodeStringToAnsiString(void) NTSTATUS result; unsigned int test_num; - for (test_num = 0; test_num < NB_USTR2ASTR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(ustr2astr); test_num++) { ansi_str.Length = ustr2astr[test_num].ansi_Length; ansi_str.MaximumLength = ustr2astr[test_num].ansi_MaximumLength; if (ustr2astr[test_num].ansi_buf != NULL) { @@ -811,9 +851,10 @@ static void test_RtlUnicodeStringToAnsiString(void) } result = pRtlUnicodeStringToAnsiString(&ansi_str, &uni_str, ustr2astr[test_num].doalloc); ok(result == ustr2astr[test_num].result, - "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) has result %x, expected %x\n", + "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) has result %lx, expected %lx\n", test_num, ustr2astr[test_num].doalloc, result, ustr2astr[test_num].result); - ok(ansi_str.Length == ustr2astr[test_num].res_Length, + ok(ansi_str.Length == ustr2astr[test_num].res_Length || + broken(ustr2astr[test_num].broken_len && !ansi_str.Length) /* win11 */, "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) ansi has Length %d, expected %d\n", test_num, ustr2astr[test_num].doalloc, ansi_str.Length, ustr2astr[test_num].res_Length); ok(ansi_str.MaximumLength == ustr2astr[test_num].res_MaximumLength, @@ -852,7 +893,6 @@ static const app_asc2str_t app_asc2str[] = { { 5, 14, 15, NULL, NULL, 5, 14, 15, NULL, STATUS_SUCCESS}, { 5, 12, 15, "Tst\0S01234abcde", "tr\0i", 7, 12, 15, "Tst\0Str234abcde", STATUS_SUCCESS}, }; -#define NB_APP_ASC2STR (sizeof(app_asc2str)/sizeof(*app_asc2str)) static void test_RtlAppendAsciizToString(void) @@ -862,7 +902,7 @@ static void test_RtlAppendAsciizToString(void) NTSTATUS result; unsigned int test_num; - for (test_num = 0; test_num < NB_APP_ASC2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(app_asc2str); test_num++) { dest_str.Length = app_asc2str[test_num].dest_Length; dest_str.MaximumLength = app_asc2str[test_num].dest_MaximumLength; if (app_asc2str[test_num].dest_buf != NULL) { @@ -874,7 +914,7 @@ static void test_RtlAppendAsciizToString(void) } result = pRtlAppendAsciizToString(&dest_str, app_asc2str[test_num].src); ok(result == app_asc2str[test_num].result, - "(test %d): RtlAppendAsciizToString(dest, src) has result %x, expected %x\n", + "(test %d): RtlAppendAsciizToString(dest, src) has result %lx, expected %lx\n", test_num, result, app_asc2str[test_num].result); ok(dest_str.Length == app_asc2str[test_num].res_Length, "(test %d): RtlAppendAsciizToString(dest, src) dest has Length %d, expected %d\n", @@ -922,7 +962,6 @@ static const app_str2str_t app_str2str[] = { { 5, 14, 15, NULL, 0, 0, 7, NULL, 5, 14, 15, NULL, STATUS_SUCCESS}, { 5, 12, 15, "Tst\0S01234abcde", 4, 4, 7, "tr\0iZY", 9, 12, 15, "Tst\0Str\0i4abcde", STATUS_SUCCESS}, }; -#define NB_APP_STR2STR (sizeof(app_str2str)/sizeof(*app_str2str)) static void test_RtlAppendStringToString(void) @@ -934,7 +973,7 @@ static void test_RtlAppendStringToString(void) NTSTATUS result; unsigned int test_num; - for (test_num = 0; test_num < NB_APP_STR2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(app_str2str); test_num++) { dest_str.Length = app_str2str[test_num].dest_Length; dest_str.MaximumLength = app_str2str[test_num].dest_MaximumLength; if (app_str2str[test_num].dest_buf != NULL) { @@ -955,7 +994,7 @@ static void test_RtlAppendStringToString(void) } result = pRtlAppendStringToString(&dest_str, &src_str); ok(result == app_str2str[test_num].result, - "(test %d): RtlAppendStringToString(dest, src) has result %x, expected %x\n", + "(test %d): RtlAppendStringToString(dest, src) has result %lx, expected %lx\n", test_num, result, app_str2str[test_num].result); ok(dest_str.Length == app_str2str[test_num].res_Length, "(test %d): RtlAppendStringToString(dest, src) dest has Length %d, expected %d\n", @@ -1005,7 +1044,6 @@ static const app_uni2str_t app_uni2str[] = { { 4, 14, 14, "Fake0123abcdef", "U\0stri\0", 10, 14, 14, "FakeU\0stri\0\0ef", STATUS_SUCCESS}, { 6, 14, 16, "Te\0\0stabcdefghij", "St\0\0ri", 8, 14, 16, "Te\0\0stSt\0\0efghij", STATUS_SUCCESS}, }; -#define NB_APP_UNI2STR (sizeof(app_uni2str)/sizeof(*app_uni2str)) static void test_RtlAppendUnicodeToString(void) @@ -1015,7 +1053,7 @@ static void test_RtlAppendUnicodeToString(void) NTSTATUS result; unsigned int test_num; - for (test_num = 0; test_num < NB_APP_UNI2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(app_uni2str); test_num++) { dest_str.Length = app_uni2str[test_num].dest_Length; dest_str.MaximumLength = app_uni2str[test_num].dest_MaximumLength; if (app_uni2str[test_num].dest_buf != NULL) { @@ -1027,7 +1065,7 @@ static void test_RtlAppendUnicodeToString(void) } result = pRtlAppendUnicodeToString(&dest_str, (LPCWSTR) app_uni2str[test_num].src); ok(result == app_uni2str[test_num].result, - "(test %d): RtlAppendUnicodeToString(dest, src) has result %x, expected %x\n", + "(test %d): RtlAppendUnicodeToString(dest, src) has result %lx, expected %lx\n", test_num, result, app_uni2str[test_num].result); ok(dest_str.Length == app_uni2str[test_num].res_Length, "(test %d): RtlAppendUnicodeToString(dest, src) dest has Length %d, expected %d\n", @@ -1079,7 +1117,6 @@ static const app_ustr2str_t app_ustr2str[] = { { 4, 14, 14, NULL, 0, 0, 8, NULL, 4, 14, 14, NULL, STATUS_SUCCESS}, { 6, 14, 16, "Te\0\0stabcdefghij", 6, 8, 8, "St\0\0riZY", 12, 14, 16, "Te\0\0stSt\0\0ri\0\0ij", STATUS_SUCCESS}, }; -#define NB_APP_USTR2STR (sizeof(app_ustr2str)/sizeof(*app_ustr2str)) static void test_RtlAppendUnicodeStringToString(void) @@ -1091,7 +1128,7 @@ static void test_RtlAppendUnicodeStringToString(void) NTSTATUS result; unsigned int test_num; - for (test_num = 0; test_num < NB_APP_USTR2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(app_ustr2str); test_num++) { dest_str.Length = app_ustr2str[test_num].dest_Length; dest_str.MaximumLength = app_ustr2str[test_num].dest_MaximumLength; if (app_ustr2str[test_num].dest_buf != NULL) { @@ -1112,7 +1149,7 @@ static void test_RtlAppendUnicodeStringToString(void) } result = pRtlAppendUnicodeStringToString(&dest_str, &src_str); ok(result == app_ustr2str[test_num].result, - "(test %d): RtlAppendStringToString(dest, src) has result %x, expected %x\n", + "(test %d): RtlAppendStringToString(dest, src) has result %lx, expected %lx\n", test_num, result, app_ustr2str[test_num].result); ok(dest_str.Length == app_ustr2str[test_num].res_Length, "(test %d): RtlAppendStringToString(dest, src) dest has Length %d, expected %d\n", @@ -1189,7 +1226,6 @@ static const find_ch_in_ustr_t find_ch_in_ustr[] = { { 2, "abcdabcdabcdabcdabcdabcd", "abcd", 0, STATUS_NOT_FOUND}, { 3, "abcdabcdabcdabcdabcdabcd", "abcd", 0, STATUS_NOT_FOUND}, }; -#define NB_FIND_CH_IN_USTR (sizeof(find_ch_in_ustr)/sizeof(*find_ch_in_ustr)) static void test_RtlFindCharInUnicodeString(void) @@ -1209,7 +1245,7 @@ static void test_RtlFindCharInUnicodeString(void) return; } - for (test_num = 0; test_num < NB_FIND_CH_IN_USTR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(find_ch_in_ustr); test_num++) { if (find_ch_in_ustr[test_num].main_str != NULL) { main_str.Length = strlen(find_ch_in_ustr[test_num].main_str) * sizeof(WCHAR); main_str.MaximumLength = main_str.Length + sizeof(WCHAR); @@ -1237,7 +1273,7 @@ static void test_RtlFindCharInUnicodeString(void) pos = 12345; result = pRtlFindCharInUnicodeString(find_ch_in_ustr[test_num].flags, &main_str, &search_chars, &pos); ok(result == find_ch_in_ustr[test_num].result, - "(test %d): RtlFindCharInUnicodeString(%d, %s, %s, [out]) has result %x, expected %x\n", + "(test %d): RtlFindCharInUnicodeString(%d, %s, %s, [out]) has result %lx, expected %lx\n", test_num, find_ch_in_ustr[test_num].flags, find_ch_in_ustr[test_num].main_str, find_ch_in_ustr[test_num].search_chars, result, find_ch_in_ustr[test_num].result); @@ -1371,7 +1407,6 @@ static const str2int_t str2int[] = { {-8, "0", 0, STATUS_INVALID_PARAMETER}, /* Negative base */ /* { 0, NULL, 0, STATUS_SUCCESS}, */ /* NULL as string */ }; -#define NB_STR2INT (sizeof(str2int)/sizeof(*str2int)) static void test_RtlUnicodeStringToInteger(void) @@ -1382,14 +1417,14 @@ static void test_RtlUnicodeStringToInteger(void) WCHAR *wstr; UNICODE_STRING uni; - for (test_num = 0; test_num < NB_STR2INT; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2int); test_num++) { wstr = AtoW(str2int[test_num].str); value = 0xdeadbeef; pRtlInitUnicodeString(&uni, wstr); result = pRtlUnicodeStringToInteger(&uni, str2int[test_num].base, &value); ok(result == str2int[test_num].result || (str2int[test_num].alternative && result == str2int[test_num].alternative), - "(test %d): RtlUnicodeStringToInteger(\"%s\", %d, [out]) has result %x, expected: %x (%x)\n", + "(test %d): RtlUnicodeStringToInteger(\"%s\", %d, [out]) has result %lx, expected: %lx (%lx)\n", test_num, str2int[test_num].str, str2int[test_num].base, result, str2int[test_num].result, str2int[test_num].alternative); if (result == STATUS_SUCCESS) @@ -1408,17 +1443,17 @@ static void test_RtlUnicodeStringToInteger(void) pRtlInitUnicodeString(&uni, wstr); result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, NULL); ok(result == STATUS_ACCESS_VIOLATION, - "call failed: RtlUnicodeStringToInteger(\"%s\", %d, NULL) has result %x\n", + "call failed: RtlUnicodeStringToInteger(\"%s\", %d, NULL) has result %lx\n", str2int[1].str, str2int[1].base, result); result = pRtlUnicodeStringToInteger(&uni, 20, NULL); ok(result == STATUS_INVALID_PARAMETER || result == STATUS_ACCESS_VIOLATION, - "call failed: RtlUnicodeStringToInteger(\"%s\", 20, NULL) has result %x\n", + "call failed: RtlUnicodeStringToInteger(\"%s\", 20, NULL) has result %lx\n", str2int[1].str, result); uni.Length = 10; /* Make Length shorter (5 WCHARS instead of 7) */ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value); ok(result == STATUS_SUCCESS, - "call failed: RtlUnicodeStringToInteger(\"12345\", %d, [out]) has result %x\n", + "call failed: RtlUnicodeStringToInteger(\"12345\", %d, [out]) has result %lx\n", str2int[1].base, result); ok(value == 12345, "didn't return expected value (test a): expected: %d, got: %d\n", @@ -1427,7 +1462,7 @@ static void test_RtlUnicodeStringToInteger(void) uni.Length = 5; /* Use odd Length (2.5 WCHARS) */ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value); ok(result == STATUS_SUCCESS || result == STATUS_INVALID_PARAMETER /* vista */, - "call failed: RtlUnicodeStringToInteger(\"12\", %d, [out]) has result %x\n", + "call failed: RtlUnicodeStringToInteger(\"12\", %d, [out]) has result %lx\n", str2int[1].base, result); if (result == STATUS_SUCCESS) ok(value == 12, "didn't return expected value (test b): expected: %d, got: %d\n", 12, value); @@ -1435,7 +1470,7 @@ static void test_RtlUnicodeStringToInteger(void) uni.Length = 2; result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value); ok(result == STATUS_SUCCESS, - "call failed: RtlUnicodeStringToInteger(\"1\", %d, [out]) has result %x\n", + "call failed: RtlUnicodeStringToInteger(\"1\", %d, [out]) has result %lx\n", str2int[1].base, result); ok(value == 1, "didn't return expected value (test c): expected: %d, got: %d\n", @@ -1451,14 +1486,14 @@ static void test_RtlCharToInteger(void) int value; NTSTATUS result; - for (test_num = 0; test_num < NB_STR2INT; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2int); test_num++) { /* w2k skips a leading '\0' and processes the string after */ if (str2int[test_num].str[0] != '\0') { value = 0xdeadbeef; result = pRtlCharToInteger(str2int[test_num].str, str2int[test_num].base, &value); ok(result == str2int[test_num].result || (str2int[test_num].alternative && result == str2int[test_num].alternative), - "(test %d): call failed: RtlCharToInteger(\"%s\", %d, [out]) has result %x, expected: %x (%x)\n", + "(test %d): call failed: RtlCharToInteger(\"%s\", %d, [out]) has result %lx, expected: %lx (%lx)\n", test_num, str2int[test_num].str, str2int[test_num].base, result, str2int[test_num].result, str2int[test_num].alternative); if (result == STATUS_SUCCESS) @@ -1474,12 +1509,12 @@ static void test_RtlCharToInteger(void) result = pRtlCharToInteger(str2int[1].str, str2int[1].base, NULL); ok(result == STATUS_ACCESS_VIOLATION, - "call failed: RtlCharToInteger(\"%s\", %d, NULL) has result %x\n", + "call failed: RtlCharToInteger(\"%s\", %d, NULL) has result %lx\n", str2int[1].str, str2int[1].base, result); result = pRtlCharToInteger(str2int[1].str, 20, NULL); ok(result == STATUS_INVALID_PARAMETER, - "call failed: RtlCharToInteger(\"%s\", 20, NULL) has result %x\n", + "call failed: RtlCharToInteger(\"%s\", 20, NULL) has result %lx\n", str2int[1].str, result); } @@ -1493,6 +1528,7 @@ typedef struct { USHORT MaximumLength; const char *Buffer; NTSTATUS result; + int broken_len; } int2str_t; static const int2str_t int2str[] = { @@ -1592,15 +1628,14 @@ static const int2str_t int2str[] = { { 2, 131072, 18, 19, "100000000000000000\0----------------", STATUS_SUCCESS}, { 2, 131072, 18, 18, "100000000000000000-----------------", STATUS_SUCCESS}, {16, 0xffffffff, 8, 9, "FFFFFFFF\0--------------------------", STATUS_SUCCESS}, - {16, 0xffffffff, 8, 8, "FFFFFFFF---------------------------", STATUS_SUCCESS}, /* No \0 term */ - {16, 0xffffffff, 8, 7, "-----------------------------------", STATUS_BUFFER_OVERFLOW}, /* Too short */ + {16, 0xffffffff, 8, 8, "FFFFFFFF---------------------------", STATUS_SUCCESS, 1}, /* No \0 term */ + {16, 0xffffffff, 8, 7, "-----------------------------------", STATUS_BUFFER_OVERFLOW, 1}, /* Too short */ {16, 0xa, 1, 2, "A\0---------------------------------", STATUS_SUCCESS}, - {16, 0xa, 1, 1, "A----------------------------------", STATUS_SUCCESS}, /* No \0 term */ - {16, 0, 1, 0, "-----------------------------------", STATUS_BUFFER_OVERFLOW}, + {16, 0xa, 1, 1, "A----------------------------------", STATUS_SUCCESS, 1}, /* No \0 term */ + {16, 0, 1, 0, "-----------------------------------", STATUS_BUFFER_OVERFLOW, 1}, {20, 0xdeadbeef, 0, 9, "-----------------------------------", STATUS_INVALID_PARAMETER}, /* ill. base */ {-8, 07654321, 0, 12, "-----------------------------------", STATUS_INVALID_PARAMETER}, /* neg. base */ }; -#define NB_INT2STR (sizeof(int2str)/sizeof(*int2str)) static void one_RtlIntegerToUnicodeString_test(int test_num, const int2str_t *int2str) @@ -1647,22 +1682,23 @@ static void one_RtlIntegerToUnicodeString_test(int test_num, const int2str_t *in } } else { ok(result == int2str->result, - "(test %d): RtlIntegerToUnicodeString(%u, %d, [out]) has result %x, expected: %x\n", + "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) has result %lx, expected: %lx\n", test_num, int2str->value, int2str->base, result, int2str->result); if (result == STATUS_SUCCESS) { ok(unicode_string.Buffer[unicode_string.Length/sizeof(WCHAR)] == '\0', - "(test %d): RtlIntegerToUnicodeString(%u, %d, [out]) string \"%s\" is not NULL terminated\n", + "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string \"%s\" is not NULL terminated\n", test_num, int2str->value, int2str->base, ansi_str.Buffer); } } ok(memcmp(unicode_string.Buffer, expected_unicode_string.Buffer, STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0, - "(test %d): RtlIntegerToUnicodeString(%u, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", test_num, int2str->value, int2str->base, ansi_str.Buffer, expected_ansi_str.Buffer); - ok(unicode_string.Length == expected_unicode_string.Length, - "(test %d): RtlIntegerToUnicodeString(%u, %d, [out]) string has Length %d, expected: %d\n", + ok(unicode_string.Length == expected_unicode_string.Length || + broken(int2str->broken_len && !unicode_string.Length) /* win11 */, + "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string has Length %d, expected: %d\n", test_num, int2str->value, int2str->base, unicode_string.Length, expected_unicode_string.Length); ok(unicode_string.MaximumLength == expected_unicode_string.MaximumLength, - "(test %d): RtlIntegerToUnicodeString(%u, %d, [out]) string has MaximumLength %d, expected: %d\n", + "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string has MaximumLength %d, expected: %d\n", test_num, int2str->value, int2str->base, unicode_string.MaximumLength, expected_unicode_string.MaximumLength); pRtlFreeAnsiString(&expected_ansi_str); pRtlFreeAnsiString(&ansi_str); @@ -1673,7 +1709,7 @@ static void test_RtlIntegerToUnicodeString(void) { size_t test_num; - for (test_num = 0; test_num < NB_INT2STR; test_num++) + for (test_num = 0; test_num < ARRAY_SIZE(int2str); test_num++) one_RtlIntegerToUnicodeString_test(test_num, &int2str[test_num]); } @@ -1687,10 +1723,10 @@ static void one_RtlIntegerToChar_test(int test_num, const int2str_t *int2str) dest_str[STRI_BUFFER_LENGTH] = '\0'; result = pRtlIntegerToChar(int2str->value, int2str->base, int2str->MaximumLength, dest_str); ok(result == int2str->result, - "(test %d): RtlIntegerToChar(%u, %d, %d, [out]) has result %x, expected: %x\n", + "(test %d): RtlIntegerToChar(%lu, %d, %d, [out]) has result %lx, expected: %lx\n", test_num, int2str->value, int2str->base, int2str->MaximumLength, result, int2str->result); ok(memcmp(dest_str, int2str->Buffer, STRI_BUFFER_LENGTH) == 0, - "(test %d): RtlIntegerToChar(%u, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): RtlIntegerToChar(%lu, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n", test_num, int2str->value, int2str->base, int2str->MaximumLength, dest_str, int2str->Buffer); } @@ -1700,27 +1736,27 @@ static void test_RtlIntegerToChar(void) NTSTATUS result; size_t test_num; - for (test_num = 0; test_num < NB_INT2STR; test_num++) + for (test_num = 0; test_num < ARRAY_SIZE(int2str); test_num++) one_RtlIntegerToChar_test(test_num, &int2str[test_num]); result = pRtlIntegerToChar(int2str[0].value, 20, int2str[0].MaximumLength, NULL); ok(result == STATUS_INVALID_PARAMETER, - "(test a): RtlIntegerToChar(%u, %d, %d, NULL) has result %x, expected: %x\n", + "(test a): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n", int2str[0].value, 20, int2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER); result = pRtlIntegerToChar(int2str[0].value, 20, 0, NULL); ok(result == STATUS_INVALID_PARAMETER, - "(test b): RtlIntegerToChar(%u, %d, %d, NULL) has result %x, expected: %x\n", + "(test b): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n", int2str[0].value, 20, 0, result, STATUS_INVALID_PARAMETER); result = pRtlIntegerToChar(int2str[0].value, int2str[0].base, 0, NULL); ok(result == STATUS_BUFFER_OVERFLOW, - "(test c): RtlIntegerToChar(%u, %d, %d, NULL) has result %x, expected: %x\n", + "(test c): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n", int2str[0].value, int2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW); result = pRtlIntegerToChar(int2str[0].value, int2str[0].base, int2str[0].MaximumLength, NULL); ok(result == STATUS_ACCESS_VIOLATION, - "(test d): RtlIntegerToChar(%u, %d, %d, NULL) has result %x, expected: %x\n", + "(test d): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n", int2str[0].value, int2str[0].base, int2str[0].MaximumLength, result, STATUS_ACCESS_VIOLATION); } @@ -1769,7 +1805,7 @@ static void test_RtlIsTextUnicode(void) be_unicode = HeapAlloc(GetProcessHeap(), 0, sizeof(unicode) + sizeof(WCHAR)); be_unicode[0] = 0xfffe; - for (i = 0; i < sizeof(unicode)/sizeof(unicode[0]); i++) + for (i = 0; i < ARRAY_SIZE(unicode); i++) { be_unicode[i + 1] = (unicode[i] >> 8) | ((unicode[i] & 0xff) << 8); } @@ -1791,7 +1827,7 @@ static void test_RtlIsTextUnicode(void) be_unicode_no_controls = HeapAlloc(GetProcessHeap(), 0, sizeof(unicode) + sizeof(WCHAR)); ok(be_unicode_no_controls != NULL, "Expected HeapAlloc to succeed.\n"); be_unicode_no_controls[0] = 0xfffe; - for (i = 0; i < sizeof(unicode_no_controls)/sizeof(unicode_no_controls[0]); i++) + for (i = 0; i < ARRAY_SIZE(unicode_no_controls); i++) be_unicode_no_controls[i + 1] = (unicode_no_controls[i] >> 8) | ((unicode_no_controls[i] & 0xff) << 8); @@ -1873,17 +1909,17 @@ static void test_RtlCompareUnicodeString(void) for (ch2 = 0; ch2 < 1024; ch2++) { LONG res = pRtlCompareUnicodeString( &str1, &str2, FALSE ); - ok( res == (ch1 - ch2), "wrong result %d %04x %04x\n", res, ch1, ch2 ); + ok( res == (ch1 - ch2), "wrong result %ld %04x %04x\n", res, ch1, ch2 ); res = pRtlCompareUnicodeString( &str1, &str2, TRUE ); ok( res == (pRtlUpcaseUnicodeChar(ch1) - pRtlUpcaseUnicodeChar(ch2)), - "wrong result %d %04x %04x\n", res, ch1, ch2 ); + "wrong result %ld %04x %04x\n", res, ch1, ch2 ); if (pRtlCompareUnicodeStrings) { res = pRtlCompareUnicodeStrings( &ch1, 1, &ch2, 1, FALSE ); - ok( res == (ch1 - ch2), "wrong result %d %04x %04x\n", res, ch1, ch2 ); + ok( res == (ch1 - ch2), "wrong result %ld %04x %04x\n", res, ch1, ch2 ); res = pRtlCompareUnicodeStrings( &ch1, 1, &ch2, 1, TRUE ); ok( res == (pRtlUpcaseUnicodeChar(ch1) - pRtlUpcaseUnicodeChar(ch2)), - "wrong result %d %04x %04x\n", res, ch1, ch2 ); + "wrong result %ld %04x %04x\n", res, ch1, ch2 ); } } } @@ -1914,7 +1950,7 @@ static void test_RtlGUIDFromString(void) str.Buffer = (LPWSTR)szGuid; ret = pRtlGUIDFromString(&str, &guid); - ok(ret == 0, "expected ret=0, got 0x%0x\n", ret); + ok(ret == 0, "expected ret=0, got 0x%0lx\n", ret); ok(IsEqualGUID(&guid, &IID_Endianness), "Endianness broken\n"); str.Length = str.MaximumLength = sizeof(szGuid2) - sizeof(WCHAR); @@ -1939,7 +1975,7 @@ static void test_RtlStringFromGUID(void) str.Buffer = NULL; ret = pRtlStringFromGUID(&IID_Endianness, &str); - ok(ret == 0, "expected ret=0, got 0x%0x\n", ret); + ok(ret == 0, "expected ret=0, got 0x%0lx\n", ret); ok(str.Buffer && !lstrcmpiW(str.Buffer, szGuid), "Endianness broken\n"); pRtlFreeUnicodeString(&str); } @@ -1951,14 +1987,18 @@ struct hash_unicodestring_test { }; static const struct hash_unicodestring_test hash_test[] = { - { {'T',0}, FALSE, 0x00000054 }, - { {'T','e','s','t',0}, FALSE, 0x766bb952 }, - { {'T','e','S','t',0}, FALSE, 0x764bb172 }, - { {'t','e','s','t',0}, FALSE, 0x4745d132 }, - { {'t','e','s','t',0}, TRUE, 0x6689c132 }, - { {'T','E','S','T',0}, TRUE, 0x6689c132 }, - { {'T','E','S','T',0}, FALSE, 0x6689c132 }, - { {'a','b','c','d','e','f',0}, FALSE, 0x971318c3 }, + { L"T", FALSE, 0x00000054 }, + { L"Test", FALSE, 0x766bb952 }, + { L"TeSt", FALSE, 0x764bb172 }, + { L"test", FALSE, 0x4745d132 }, + { L"test", TRUE, 0x6689c132 }, + { L"TEST", TRUE, 0x6689c132 }, + { L"TEST", FALSE, 0x6689c132 }, + { L"t\xe9st", FALSE, 0x8845cfb6 }, + { L"t\xe9st", TRUE, 0xa789bfb6 }, + { L"T\xc9ST", TRUE, 0xa789bfb6 }, + { L"T\xc9ST", FALSE, 0xa789bfb6 }, + { L"abcdef", FALSE, 0x971318c3 }, { { 0 } } }; @@ -1977,31 +2017,31 @@ static void test_RtlHashUnicodeString(void) } status = pRtlHashUnicodeString(NULL, FALSE, HASH_STRING_ALGORITHM_X65599, &hash); - ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08lx\n", status); - RtlInitUnicodeString(&str, strW); + pRtlInitUnicodeString(&str, strW); status = pRtlHashUnicodeString(&str, FALSE, HASH_STRING_ALGORITHM_X65599, NULL); - ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08lx\n", status); status = pRtlHashUnicodeString(&str, FALSE, HASH_STRING_ALGORITHM_INVALID, &hash); - ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "got status 0x%08lx\n", status); /* embedded null */ str.Buffer = (PWSTR)strW; str.Length = sizeof(strW) - sizeof(WCHAR); str.MaximumLength = sizeof(strW); status = pRtlHashUnicodeString(&str, FALSE, HASH_STRING_ALGORITHM_X65599, &hash); - ok(status == STATUS_SUCCESS, "got status 0x%08x\n", status); - ok(hash == 0x32803083, "got 0x%08x\n", hash); + ok(status == STATUS_SUCCESS, "got status 0x%08lx\n", status); + ok(hash == 0x32803083, "got 0x%08lx\n", hash); ptr = hash_test; while (*ptr->str) { - RtlInitUnicodeString(&str, ptr->str); + pRtlInitUnicodeString(&str, ptr->str); hash = 0; status = pRtlHashUnicodeString(&str, ptr->case_insensitive, HASH_STRING_ALGORITHM_X65599, &hash); - ok(status == STATUS_SUCCESS, "got status 0x%08x for %s\n", status, wine_dbgstr_w(ptr->str)); - ok(hash == ptr->hash, "got wrong hash 0x%08x, expected 0x%08x, for %s, mode %d\n", hash, ptr->hash, + ok(status == STATUS_SUCCESS, "got status 0x%08lx for %s\n", status, wine_dbgstr_w(ptr->str)); + ok(hash == ptr->hash, "got wrong hash 0x%08lx, expected 0x%08lx, for %s, mode %d\n", hash, ptr->hash, wine_dbgstr_w(ptr->str), ptr->case_insensitive); ptr++; @@ -2037,8 +2077,8 @@ static const struct unicode_to_utf8_test unicode_to_utf8[] = { { { '-',0xfeff,'-',0xfffe,'-',0 }, "-\xEF\xBB\xBF-\xEF\xBF\xBE-", STATUS_SUCCESS }, { { 0xfeff,'-',0 }, "\xEF\xBB\xBF-", STATUS_SUCCESS }, { { 0xfffe,'-',0 }, "\xEF\xBF\xBE-", STATUS_SUCCESS }, - /* invalid code point */ - { { 0xffff,'-',0 }, "\xEF\xBF\xBF-", STATUS_SUCCESS }, + /* invalid code points */ + { { 0xfffd, '-', 0xfffe, '-', 0xffff,'-',0 }, "\xEF\xBF\xBD-\xEF\xBF\xBE-\xEF\xBF\xBF-", STATUS_SUCCESS }, /* canonically equivalent representations -- no normalization should happen */ { { '-',0x1e09,'-',0 }, "-\xE1\xB8\x89-", STATUS_SUCCESS }, { { '-',0x0107,0x0327,'-',0 }, "-\xC4\x87\xCC\xA7-", STATUS_SUCCESS }, @@ -2064,8 +2104,8 @@ static void utf8_expect_(const unsigned char *out_string, ULONG buflen, ULONG ou status = pRtlUnicodeToUTF8N( out_string ? buffer : NULL, buflen, &bytes_out, in_string, in_bytes); - ok_(__FILE__, line)(status == expect_status, "status = 0x%x\n", status); - ok_(__FILE__, line)(bytes_out == out_bytes, "bytes_out = %u\n", bytes_out); + ok_(__FILE__, line)(status == expect_status, "status 0x%lx, expected 0x%lx\n", status, expect_status); + ok_(__FILE__, line)(bytes_out == out_bytes, "bytes_out = %lu, expected %lu\n", bytes_out, out_bytes); if (out_string) { for (i = 0; i < bytes_out; i++) @@ -2091,65 +2131,66 @@ static void test_RtlUnicodeToUTF8N(void) const WCHAR empty_string[] = { 0 }; const WCHAR test_string[] = { 'A',0,'a','b','c','d','e','f','g',0 }; const WCHAR special_string[] = { 'X',0x80,0xd800,0 }; + const ULONG special_string_len[] = { 0, 1, 1, 3, 3, 3, 6, 7 }; const unsigned char special_expected[] = { 'X',0xc2,0x80,0xef,0xbf,0xbd,0 }; unsigned int input_len; - const unsigned int test_count = sizeof(unicode_to_utf8) / sizeof(unicode_to_utf8[0]); - unsigned int i; + const unsigned int test_count = ARRAY_SIZE(unicode_to_utf8); + unsigned int i, ret; if (!pRtlUnicodeToUTF8N) { - skip("RtlUnicodeToUTF8N unavailable\n"); + win_skip("RtlUnicodeToUTF8N is not available\n"); return; } /* show that bytes_out is really ULONG */ memset(bytes_out_array, 0x55, sizeof(bytes_out_array)); status = pRtlUnicodeToUTF8N(NULL, 0, bytes_out_array, empty_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out_array[0] == 0x00000000, "Got 0x%x\n", bytes_out_array[0]); - ok(bytes_out_array[1] == 0x55555555, "Got 0x%x\n", bytes_out_array[1]); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out_array[0] == 0x00000000, "Got 0x%lx\n", bytes_out_array[0]); + ok(bytes_out_array[1] == 0x55555555, "Got 0x%lx\n", bytes_out_array[1]); /* parameter checks */ status = pRtlUnicodeToUTF8N(NULL, 0, NULL, NULL, 0); - ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status); + ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%lx\n", status); status = pRtlUnicodeToUTF8N(NULL, 0, NULL, empty_string, 0); - ok(status == STATUS_INVALID_PARAMETER, "status = 0x%x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "status = 0x%lx\n", status); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, NULL, 0); - ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status); - ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%lx\n", status); + ok(bytes_out == 0x55555555, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, invalid_pointer, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, empty_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, test_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(NULL, 0, &bytes_out, empty_string, 1); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(invalid_pointer, 0, &bytes_out, empty_string, 1); - ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%x\n", status); - ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%lx\n", status); + ok(bytes_out == 0x55555555, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUnicodeToUTF8N(invalid_pointer, 8, &bytes_out, empty_string, 1); - ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%x\n", status); - ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_INVALID_PARAMETER_5, "status = 0x%lx\n", status); + ok(bytes_out == 0x55555555, "bytes_out = 0x%lx\n", bytes_out); /* length output with special chars */ #define length_expect(in_chars, out_bytes, expect_status) \ @@ -2164,21 +2205,20 @@ static void test_RtlUnicodeToUTF8N(void) length_expect(4, 7, STATUS_SOME_NOT_MAPPED); #undef length_expect - /* output truncation */ -#define truncate_expect(buflen, out_bytes, expect_status) \ - utf8_expect_(special_expected, buflen, out_bytes, \ - special_string, sizeof(special_string), \ - expect_status, __LINE__) + for (i = 0; i <= 6; i++) + { + memset(buffer, 0x55, sizeof(buffer)); + bytes_out = 0xdeadbeef; + status = pRtlUnicodeToUTF8N(buffer, i, &bytes_out, special_string, sizeof(special_string)); + ok(status == STATUS_BUFFER_TOO_SMALL, "%d: status = 0x%lx\n", i, status); + ok(bytes_out == special_string_len[i], "%d: expected %lu, got %lu\n", i, special_string_len[i], bytes_out); + ok(memcmp(buffer, special_expected, special_string_len[i]) == 0, "%d: bad conversion\n", i); + } - truncate_expect(0, 0, STATUS_BUFFER_TOO_SMALL); - truncate_expect(1, 1, STATUS_BUFFER_TOO_SMALL); - truncate_expect(2, 1, STATUS_BUFFER_TOO_SMALL); - truncate_expect(3, 3, STATUS_BUFFER_TOO_SMALL); - truncate_expect(4, 3, STATUS_BUFFER_TOO_SMALL); - truncate_expect(5, 3, STATUS_BUFFER_TOO_SMALL); - truncate_expect(6, 6, STATUS_BUFFER_TOO_SMALL); - truncate_expect(7, 7, STATUS_SOME_NOT_MAPPED); -#undef truncate_expect + status = pRtlUnicodeToUTF8N(buffer, 7, &bytes_out, special_string, sizeof(special_string)); + ok(status == STATUS_SOME_NOT_MAPPED, "status = 0x%lx\n", status); + ok(bytes_out == special_string_len[7], "expected %lu, got %lu\n", special_string_len[7], bytes_out); + ok(memcmp(buffer, special_expected, 7) == 0, "bad conversion\n"); /* conversion behavior with varying input length */ for (input_len = 0; input_len <= sizeof(test_string); input_len++) { @@ -2194,9 +2234,9 @@ static void test_RtlUnicodeToUTF8N(void) test_string, input_len); if (input_len % sizeof(WCHAR) == 0) { ok(status == STATUS_SUCCESS, - "(len %u): status = 0x%x\n", input_len, status); + "(len %u): status = 0x%lx\n", input_len, status); ok(bytes_out == input_len / sizeof(WCHAR), - "(len %u): bytes_out = 0x%x\n", input_len, bytes_out); + "(len %u): bytes_out = 0x%lx\n", input_len, bytes_out); for (i = 0; i < bytes_out; i++) { ok(buffer[i] == test_string[i], "(len %u): buffer[%d] = 0x%x, expected 0x%x\n", @@ -2208,9 +2248,9 @@ static void test_RtlUnicodeToUTF8N(void) } } else { ok(status == STATUS_INVALID_PARAMETER_5, - "(len %u): status = 0x%x\n", input_len, status); + "(len %u): status = 0x%lx\n", input_len, status); ok(bytes_out == 0x55555555, - "(len %u): bytes_out = 0x%x\n", input_len, bytes_out); + "(len %u): bytes_out = 0x%lx\n", input_len, bytes_out); for (i = 0; i < sizeof(buffer); i++) { ok(buffer[i] == 0x55, "(len %u): buffer[%d] = 0x%x\n", input_len, i, buffer[i]); @@ -2226,16 +2266,24 @@ static void test_RtlUnicodeToUTF8N(void) buffer, sizeof(buffer), &bytes_out, unicode_to_utf8[i].unicode, lstrlenW(unicode_to_utf8[i].unicode) * sizeof(WCHAR)); ok(status == unicode_to_utf8[i].status, - "(test %d): status is 0x%x, expected 0x%x\n", + "(test %d): status is 0x%lx, expected 0x%lx\n", i, status, unicode_to_utf8[i].status); ok(bytes_out == strlen(unicode_to_utf8[i].expected), - "(test %d): bytes_out is %u, expected %u\n", + "(test %d): bytes_out is %lu, expected %u\n", i, bytes_out, lstrlenA(unicode_to_utf8[i].expected)); ok(!memcmp(buffer, unicode_to_utf8[i].expected, bytes_out), "(test %d): got \"%.*s\", expected \"%s\"\n", - i, bytes_out, buffer, unicode_to_utf8[i].expected); + i, (int)bytes_out, buffer, unicode_to_utf8[i].expected); ok(buffer[bytes_out] == 0x55, "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]); + memset(buffer, 0x55, sizeof(buffer)); + ret = WideCharToMultiByte( CP_UTF8, 0, unicode_to_utf8[i].unicode, lstrlenW(unicode_to_utf8[i].unicode), + buffer, sizeof(buffer), NULL, NULL ); + ok( ret == strlen(unicode_to_utf8[i].expected), "(test %d): wrong len %u\n", i, ret ); + ok(!memcmp(buffer, unicode_to_utf8[i].expected, ret), + "(test %d): got \"%.*s\", expected \"%s\"\n", + i, ret, buffer, unicode_to_utf8[i].expected); + ok(buffer[ret] == 0x55, "(test %d): behind string: 0x%x\n", i, buffer[ret]); /* same test but include the null terminator */ bytes_out = 0x55555555; @@ -2244,16 +2292,40 @@ static void test_RtlUnicodeToUTF8N(void) buffer, sizeof(buffer), &bytes_out, unicode_to_utf8[i].unicode, (lstrlenW(unicode_to_utf8[i].unicode) + 1) * sizeof(WCHAR)); ok(status == unicode_to_utf8[i].status, - "(test %d): status is 0x%x, expected 0x%x\n", + "(test %d): status is 0x%lx, expected 0x%lx\n", i, status, unicode_to_utf8[i].status); ok(bytes_out == strlen(unicode_to_utf8[i].expected) + 1, - "(test %d): bytes_out is %u, expected %u\n", + "(test %d): bytes_out is %lu, expected %u\n", i, bytes_out, lstrlenA(unicode_to_utf8[i].expected) + 1); ok(!memcmp(buffer, unicode_to_utf8[i].expected, bytes_out), "(test %d): got \"%.*s\", expected \"%s\"\n", - i, bytes_out, buffer, unicode_to_utf8[i].expected); + i, (int)bytes_out, buffer, unicode_to_utf8[i].expected); ok(buffer[bytes_out] == 0x55, "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]); + memset(buffer, 0x55, sizeof(buffer)); + ret = WideCharToMultiByte( CP_UTF8, 0, unicode_to_utf8[i].unicode, -1, buffer, sizeof(buffer), NULL, NULL ); + ok( ret == strlen(unicode_to_utf8[i].expected) + 1, "(test %d): wrong len %u\n", i, ret ); + ok(!memcmp(buffer, unicode_to_utf8[i].expected, ret), + "(test %d): got \"%.*s\", expected \"%s\"\n", + i, ret, buffer, unicode_to_utf8[i].expected); + ok(buffer[ret] == 0x55, "(test %d): behind string: 0x%x\n", i, buffer[ret]); + SetLastError( 0xdeadbeef ); + memset(buffer, 0x55, sizeof(buffer)); + ret = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, unicode_to_utf8[i].unicode, -1, + buffer, sizeof(buffer), NULL, NULL ); + if (unicode_to_utf8[i].status == STATUS_SOME_NOT_MAPPED) + { + ok( ret == 0, "(test %d): wrong len %u\n", i, ret ); + ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "(test %d): wrong error %lu\n", i, GetLastError() ); + ret = strlen(unicode_to_utf8[i].expected) + 1; + } + else + ok( ret == strlen(unicode_to_utf8[i].expected) + 1, "(test %d): wrong len %u\n", i, ret ); + + ok(!memcmp(buffer, unicode_to_utf8[i].expected, ret), + "(test %d): got \"%.*s\", expected \"%s\"\n", + i, ret, buffer, unicode_to_utf8[i].expected); + ok(buffer[ret] == 0x55, "(test %d): behind string: 0x%x\n", i, buffer[ret]); } } @@ -2338,9 +2410,8 @@ static const struct utf8_to_unicode_test utf8_to_unicode[] = { { "-\xEF\xBB\xBF-\xEF\xBF\xBE-", { '-',0xfeff,'-',0xfffe,'-',0 }, STATUS_SUCCESS }, { "\xEF\xBB\xBF-", { 0xfeff,'-',0 }, STATUS_SUCCESS }, { "\xEF\xBF\xBE-", { 0xfffe,'-',0 }, STATUS_SUCCESS }, - /* invalid code point */ - /* 0xffff */ - { "\xEF\xBF\xBF-", { 0xffff,'-',0 }, STATUS_SUCCESS }, + /* invalid code points */ + { "\xEF\xBF\xBD-\xEF\xBF\xBE-\xEF\xBF\xBF-", { 0xfffd,'-',0xfffe,'-',0xffff,'-',0 }, STATUS_SUCCESS }, /* canonically equivalent representations -- no normalization should happen */ { "-\xE1\xB8\x89-", { '-',0x1e09,'-',0 }, STATUS_SUCCESS }, { "-\xC4\x87\xCC\xA7-", { '-',0x0107,0x0327,'-',0 }, STATUS_SUCCESS }, @@ -2365,16 +2436,16 @@ static void unicode_expect_(const WCHAR *out_string, ULONG buflen, ULONG out_cha status = pRtlUTF8ToUnicodeN( out_string ? buffer : NULL, buflen, &bytes_out, in_string, in_chars); - ok_(__FILE__, line)(status == expect_status, "status = 0x%x\n", status); + ok_(__FILE__, line)(status == expect_status, "status = 0x%lx\n", status); ok_(__FILE__, line)(bytes_out == out_chars * sizeof(WCHAR), - "bytes_out = %u, expected %u\n", bytes_out, out_chars * (ULONG)sizeof(WCHAR)); + "bytes_out = %lu, expected %lu\n", bytes_out, out_chars * (ULONG)sizeof(WCHAR)); if (out_string) { for (i = 0; i < bytes_out / sizeof(WCHAR); i++) ok_(__FILE__, line)(buffer[i] == out_string[i], "buffer[%d] = 0x%x, expected 0x%x\n", i, buffer[i], out_string[i]); - for (; i < sizeof(buffer) / sizeof(WCHAR); i++) + for (; i < ARRAY_SIZE(buffer); i++) ok_(__FILE__, line)(buffer[i] == 0x5555, "buffer[%d] = 0x%x, expected 0x5555\n", i, buffer[i]); @@ -2396,53 +2467,53 @@ static void test_RtlUTF8ToUnicodeN(void) const char special_string[] = { 'X',0xc2,0x80,0xF0,0x90,0x80,0x80,0 }; const WCHAR special_expected[] = { 'X',0x80,0xd800,0xdc00,0 }; unsigned int input_len; - const unsigned int test_count = sizeof(utf8_to_unicode) / sizeof(utf8_to_unicode[0]); - unsigned int i; + const unsigned int test_count = ARRAY_SIZE(utf8_to_unicode); + unsigned int i, ret; if (!pRtlUTF8ToUnicodeN) { - skip("RtlUTF8ToUnicodeN unavailable\n"); + win_skip("RtlUTF8ToUnicodeN is not available\n"); return; } /* show that bytes_out is really ULONG */ memset(bytes_out_array, 0x55, sizeof(bytes_out_array)); status = pRtlUTF8ToUnicodeN(NULL, 0, bytes_out_array, empty_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out_array[0] == 0x00000000, "Got 0x%x\n", bytes_out_array[0]); - ok(bytes_out_array[1] == 0x55555555, "Got 0x%x\n", bytes_out_array[1]); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out_array[0] == 0x00000000, "Got 0x%lx\n", bytes_out_array[0]); + ok(bytes_out_array[1] == 0x55555555, "Got 0x%lx\n", bytes_out_array[1]); /* parameter checks */ status = pRtlUTF8ToUnicodeN(NULL, 0, NULL, NULL, 0); - ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status); + ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%lx\n", status); status = pRtlUTF8ToUnicodeN(NULL, 0, NULL, empty_string, 0); - ok(status == STATUS_INVALID_PARAMETER, "status = 0x%x\n", status); + ok(status == STATUS_INVALID_PARAMETER, "status = 0x%lx\n", status); bytes_out = 0x55555555; status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, NULL, 0); - ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%x\n", status); - ok(bytes_out == 0x55555555, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_INVALID_PARAMETER_4, "status = 0x%lx\n", status); + ok(bytes_out == 0x55555555, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, invalid_pointer, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, empty_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, test_string, 0); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == 0, "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == 0, "bytes_out = 0x%lx\n", bytes_out); bytes_out = 0x55555555; status = pRtlUTF8ToUnicodeN(NULL, 0, &bytes_out, empty_string, 1); - ok(status == STATUS_SUCCESS, "status = 0x%x\n", status); - ok(bytes_out == sizeof(WCHAR), "bytes_out = 0x%x\n", bytes_out); + ok(status == STATUS_SUCCESS, "status = 0x%lx\n", status); + ok(bytes_out == sizeof(WCHAR), "bytes_out = 0x%lx\n", bytes_out); /* length output with special chars */ #define length_expect(in_chars, out_chars, expect_status) \ @@ -2498,16 +2569,25 @@ static void test_RtlUTF8ToUnicodeN(void) buffer, sizeof(buffer), &bytes_out, utf8_to_unicode[i].utf8, strlen(utf8_to_unicode[i].utf8)); ok(status == utf8_to_unicode[i].status, - "(test %d): status is 0x%x, expected 0x%x\n", + "(test %d): status is 0x%lx, expected 0x%lx\n", i, status, utf8_to_unicode[i].status); ok(bytes_out == lstrlenW(utf8_to_unicode[i].expected) * sizeof(WCHAR), - "(test %d): bytes_out is %u, expected %u\n", + "(test %d): bytes_out is %lu, expected %lu\n", i, bytes_out, lstrlenW(utf8_to_unicode[i].expected) * (ULONG)sizeof(WCHAR)); ok(!memcmp(buffer, utf8_to_unicode[i].expected, bytes_out), "(test %d): got %s, expected %s\n", i, wine_dbgstr_wn(buffer, bytes_out / sizeof(WCHAR)), wine_dbgstr_w(utf8_to_unicode[i].expected)); - ok(buffer[bytes_out] == 0x5555, - "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]); + ok(buffer[bytes_out / sizeof(WCHAR)] == 0x5555, + "(test %d): behind string: 0x%x\n", i, buffer[bytes_out / sizeof(WCHAR)]); + memset(buffer, 0x55, sizeof(buffer)); + ret = MultiByteToWideChar( CP_UTF8, 0, utf8_to_unicode[i].utf8, strlen(utf8_to_unicode[i].utf8), + buffer, ARRAY_SIZE(buffer) ); + ok( ret == lstrlenW(utf8_to_unicode[i].expected), "(test %d): wrong len %u\n", i, ret ); + ok(!memcmp(buffer, utf8_to_unicode[i].expected, lstrlenW(utf8_to_unicode[i].expected) * sizeof(WCHAR)), + "(test %d): got %s, expected %s\n", + i, wine_dbgstr_wn(buffer, ret), wine_dbgstr_w(utf8_to_unicode[i].expected)); + ok(buffer[ret] == 0x5555, + "(test %d): behind string: 0x%x\n", i, buffer[ret]); /* same test but include the null terminator */ bytes_out = 0x55555555; @@ -2516,19 +2596,347 @@ static void test_RtlUTF8ToUnicodeN(void) buffer, sizeof(buffer), &bytes_out, utf8_to_unicode[i].utf8, strlen(utf8_to_unicode[i].utf8) + 1); ok(status == utf8_to_unicode[i].status, - "(test %d): status is 0x%x, expected 0x%x\n", + "(test %d): status is 0x%lx, expected 0x%lx\n", i, status, utf8_to_unicode[i].status); ok(bytes_out == (lstrlenW(utf8_to_unicode[i].expected) + 1) * sizeof(WCHAR), - "(test %d): bytes_out is %u, expected %u\n", + "(test %d): bytes_out is %lu, expected %lu\n", i, bytes_out, (lstrlenW(utf8_to_unicode[i].expected) + 1) * (ULONG)sizeof(WCHAR)); ok(!memcmp(buffer, utf8_to_unicode[i].expected, bytes_out), "(test %d): got %s, expected %s\n", i, wine_dbgstr_wn(buffer, bytes_out / sizeof(WCHAR)), wine_dbgstr_w(utf8_to_unicode[i].expected)); - ok(buffer[bytes_out] == 0x5555, - "(test %d): behind string: 0x%x\n", i, buffer[bytes_out]); + ok(buffer[bytes_out / sizeof(WCHAR)] == 0x5555, + "(test %d): behind string: 0x%x\n", i, buffer[bytes_out / sizeof(WCHAR)]); + + memset(buffer, 0x55, sizeof(buffer)); + ret = MultiByteToWideChar( CP_UTF8, 0, utf8_to_unicode[i].utf8, -1, buffer, ARRAY_SIZE(buffer) ); + ok( ret == lstrlenW(utf8_to_unicode[i].expected) + 1, "(test %d): wrong len %u\n", i, ret ); + ok(!memcmp(buffer, utf8_to_unicode[i].expected, ret * sizeof(WCHAR)), + "(test %d): got %s, expected %s\n", + i, wine_dbgstr_wn(buffer, ret), wine_dbgstr_w(utf8_to_unicode[i].expected)); + ok(buffer[ret] == 0x5555, + "(test %d): behind string: 0x%x\n", i, buffer[ret]); + + SetLastError( 0xdeadbeef ); + memset(buffer, 0x55, sizeof(buffer)); + ret = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, + utf8_to_unicode[i].utf8, -1, buffer, ARRAY_SIZE(buffer) ); + if (utf8_to_unicode[i].status == STATUS_SOME_NOT_MAPPED) + { + ok( ret == 0, "(test %d): wrong len %u\n", i, ret ); + ok( GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "(test %d): wrong error %lu\n", i, GetLastError() ); + ret = lstrlenW(utf8_to_unicode[i].expected) + 1; + } + else + ok( ret == lstrlenW(utf8_to_unicode[i].expected) + 1, "(test %d): wrong len %u\n", i, ret ); + + ok(!memcmp(buffer, utf8_to_unicode[i].expected, ret * sizeof(WCHAR)), + "(test %d): got %s, expected %s\n", + i, wine_dbgstr_wn(buffer, ret), wine_dbgstr_w(utf8_to_unicode[i].expected)); + ok(buffer[ret] == 0x5555, + "(test %d): behind string: 0x%x\n", i, buffer[ret]); } } +static NTSTATUS WINAPIV fmt( const WCHAR *src, ULONG width, BOOLEAN ignore_inserts, BOOLEAN ansi, + WCHAR *buffer, ULONG size, ULONG *retsize, ... ) +{ + va_list args; + NTSTATUS status; + + *retsize = 0xdeadbeef; + va_start( args, retsize ); + status = pRtlFormatMessage( src, width, ignore_inserts, ansi, FALSE, &args, buffer, size, retsize ); + va_end( args ); + return status; +} + +static void WINAPIV testfmt( const WCHAR *src, const WCHAR *expect, ULONG width, BOOL ansi, ... ) +{ + va_list args; + NTSTATUS status; + WCHAR buffer[128]; + ULONG size = 0xdeadbeef; + + memset( buffer, 0xcc, sizeof(buffer) ); + va_start( args, ansi ); + status = pRtlFormatMessage( src, width, FALSE, ansi, FALSE, &args, buffer, sizeof(buffer), &size ); + va_end( args ); + ok( !status, "%s: failed %lx\n", debugstr_w(src), status ); + ok( !lstrcmpW( buffer, expect ), "%s: got %s expected %s\n", debugstr_w(src), + debugstr_w(buffer), debugstr_w(expect) ); + ok( size == (lstrlenW(expect) + 1) * sizeof(WCHAR), "%s: wrong size %lu\n", debugstr_w(src), size ); +} + +static void testfmt_arg_eaten( const WCHAR *src, ... ) +{ + va_list args; + NTSTATUS status; + WCHAR *arg, buffer[1]; + ULONG size = 0xdeadbeef; + + buffer[0] = 0xcccc; + va_start( args, src ); + status = pRtlFormatMessage( src, 0, FALSE, FALSE, FALSE, &args, buffer, ARRAY_SIZE(buffer), &size ); + ok( status == STATUS_BUFFER_OVERFLOW, "%s: failed %lx\n", debugstr_w(src), status ); + todo_wine + ok( buffer[0] == 0xcccc, "%s: got %x\n", debugstr_w(src), buffer[0] ); + ok( size == 0xdeadbeef, "%s: wrong size %lu\n", debugstr_w(src), size ); + arg = va_arg( args, WCHAR * ); + ok( !wcscmp( L"unused", arg ), "%s: wrong arg %s\n", debugstr_w(src), debugstr_w(arg) ); + va_end( args ); +} + +static void test_RtlFormatMessage(void) +{ + WCHAR buffer[128]; + NTSTATUS status; + ULONG i, size; + + /* basic formats */ + testfmt( L"test", L"test", 0, FALSE ); + testfmt( L"", L"", 0, FALSE ); + testfmt( L"%1", L"test", 0, FALSE, L"test" ); + testfmt( L"%1!s!", L"test", 0, FALSE, L"test" ); + testfmt( L"%1!s!", L"foo", 0, TRUE, "foo" ); + testfmt( L"%1!S!", L"test", 0, FALSE, "test" ); + testfmt( L"%1!S!", L"foo", 0, TRUE, L"foo" ); + testfmt( L"%1!hs!%1!hS!", L"testtest", 0, FALSE, "test" ); + testfmt( L"%1!ls!%1!lS!%1!ws!%1!wS!", L"foofoofoofoo", 0, TRUE, L"foo" ); + testfmt( L"%1!c!", L"a", 0, FALSE, L'a' ); + testfmt( L"%1!c!", L"b", 0, TRUE, 'b' ); + testfmt( L"%1!C!", L"c", 0, FALSE, L'c' ); + testfmt( L"%1!C!", L"d", 0, TRUE, 'd' ); + testfmt( L"%1!hc!", L"e", 0, FALSE, L'e' ); + testfmt( L"%1!hC!", L"f", 0, FALSE, L'f' ); + testfmt( L"%1!lc!", L"g", 0, TRUE, 'g' ); + testfmt( L"%1!lC!", L"h", 0, TRUE, 'h' ); + testfmt( L"%1!wc!", L"i", 0, TRUE, 'i' ); + testfmt( L"%1!wC!", L"j", 0, TRUE, 'j' ); + testfmt( L"%1!04X!", L"BEEF", 0, FALSE, 0xbeef ); + testfmt( L"%1!Saa!", L"testaa", 0, FALSE, "test" ); + testfmt( L"%.%%%Z%n%t%r%!% ", L".%Z\r\n\t\r! ", 0, FALSE ); + testfmt( L"%1!*.*u!,%1!*.*u!", L" 001, 0002", 0, FALSE, 5, 3, 1, 4, 2 ); + testfmt( L"%1!*.*u!,%3!*.*u!", L" 001, 0002", 0, FALSE, 5, 3, 1, 6, 4, 2 ); + testfmt( L"%1", L"(null)", 0, FALSE, NULL ); + testfmt( L"%2", L"(null)", 0, TRUE, "abc", NULL ); + testfmt( L"ab%1!!cd", L"abcd", 0, FALSE, L"hello" ); + testfmt( L"abc%1!#.000000000000000000000000000x!", L"abc0x22", 0, FALSE, 34 ); + testfmt( L"a\r\nb\rc\r\rd\r\r\ne", L"a\r\nb\r\nc\r\n\r\nd\r\n\r\ne", 0, FALSE, NULL ); +#ifdef _WIN64 + testfmt( L"%1!#I64x! %2!x!", L"0x1234 5678", 0, FALSE, (ULONG_PTR)0x1234, 0x5678, 0xbeef ); + testfmt( L"%1!x! %2!#I64x! %3!#I64x! %4!x!", L"dead 0x1111222233334444 0x5555666677778888 beef", + 0, FALSE, 0xdead, 0x1111222233334444ull, 0x5555666677778888ull, 0xbeef ); + testfmt( L"%3!#I64x! %4!#I64x! %3!x! %1!x!", L"0x3 0x4 3 1", 0, FALSE, 0xdead00000001ll, 2, 3ll, 4ll ); + testfmt( L"%2!x! %1!I64x!", L"5678 1234", 0, FALSE, (ULONG_PTR)0x1234, 0x5678, 0xbeef ); + testfmt( L"%2!*.*I64x! %1!u! %4!u! %2!u!", L" 00000000000000d 19 11 17", 0, FALSE, + 19ull, 17ull, 15ull, 13ull, 11ull, 9ull ); + { /* argument array works differently */ + ULONG_PTR args[] = { 19, 17, 15, 13, 11, 9, 7 }; + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%2!*.*I64x! %1!u! %4!u! %2!u!", 0, FALSE, FALSE, TRUE, + (va_list *)args, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L" 00000000000000d 19 13 17" ), "got %s\n", wine_dbgstr_w(buffer) ); + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%1!I64u! %2!u! %4!.*I64x! %5!I64u!", 0, FALSE, FALSE, TRUE, + (va_list *)args, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L"19 17 000000000000b 11" ), "got %s\n", wine_dbgstr_w(buffer) ); + } +#else + fmt( L"%1!#I64x! %2!x!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 0x1234, 0x5678, 0xbeef ); + if (lstrcmpW( buffer, L"0x567800001234 5678" )) + { + testfmt( L"%1!#I64x! %2!x!", L"0x567800001234 beef", 0, FALSE, 0x1234, 0x5678, 0xbeef ); + testfmt( L"%1!x! %2!#I64x! %3!#I64x! %4!x!", L"dead 0x1111222233334444 0x5555666677778888 beef", + 0, FALSE, 0xdead, 0x1111222233334444ull, 0x5555666677778888ull, 0xbeef ); + testfmt( L"%3!#I64x! %4!#I64x! %3!x! %1!x!", L"0x1111222233334444 0x5555666677778888 33334444 1", + 0, FALSE, 1, 2, 3, 4, 0x33334444, 0x11112222, 0x77778888, 0x55556666, 0xbeef, 0xbee2 ); + testfmt( L"%2!x! %1!I64x!", L"5678 1234", 0, FALSE, 0x1234, 0x5678, 0xbeef ); + testfmt( L"%2!*.*I64x! %1!u! %4!u! %2!u!", L" 000090000000b 19 7 15", 0, FALSE, + 19, 17, 15, 13, 11, 9, 7 ); + { /* argument array works differently */ + ULONG_PTR args[] = { 19, 17, 15, 13, 11, 9, 7 }; + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%2!*.*I64x! %1!u! %4!u! %2!u!", 0, FALSE, FALSE, TRUE, + (va_list *)args, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L" d0000000f 19 13 17" ), "got %s\n", wine_dbgstr_w(buffer) ); + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%1!I64u! %2!u! %4!.*I64x! %5!I64u!", 0, FALSE, FALSE, TRUE, + (va_list *)args, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L"19 17 0000b00000000 11" ), "got %s\n", wine_dbgstr_w(buffer) ); + } + } + else win_skip( "I64 support broken\n" ); +#endif + testfmt( L"%1!Ix! %2!QQ!", L"1234 QQ", 0, FALSE, (ULONG_PTR)0x1234 ); + testfmt( L"%1!#llx!%2!#x!%1!#hx!", L"0x1234560x789abc0x3456", 0, FALSE, 0x123456, 0x789abc ); + lstrcpyW( buffer, L"xxxxxxxxxx" ); + fmt( L"ab%0cd", 0, FALSE, FALSE, buffer, sizeof(buffer), &size ); + ok( !memcmp( buffer, L"ab\0xxxxxxx", 10 * sizeof(WCHAR) ), "got %s\n", wine_dbgstr_wn(buffer, 10) ); + + /* max width */ + testfmt( L"%1", L"testing\r\n", 3, FALSE, L"testing" ); + testfmt( L"%1%2%3", L"testing\r\nabcdef\r\nfoobar\r\n", 4, FALSE, L"testing", L"abcdef", L"foobar"); + testfmt( L"%1%2%3%4", L"test\r\nabcd\r\nabcdef\r\n", 4, FALSE, L"test", L"abcd", L"abc", L"def" ); + testfmt( L"%1a\nb%2", L"testing\r\na\r\nbfoo bar\r\n", 3, FALSE, L"testing", L"foo bar" ); + testfmt( L"a%tb%t%t%t%c%r%r%r%r%r%rdefg", L"a\r\nb\r\n\r\n\r\nc\r\r\r\r\r\rdef\r\ng", 3, FALSE ); + testfmt( L"test abcd ", L"test\r\n\r\nabcd\r\n ", 4, FALSE ); + testfmt( L"test abcdef %1 foobar", L"tes\r\nt\r\nabc\r\ndef\r\n\r\nhello\r\nfoo\r\nbar\r\n", 3, FALSE, L"hello" ); + testfmt( L"te st\nabc d\nfoo", L"te st\r\nabc d\r\nfoo", 6, FALSE ); + testfmt( L"te st ab d\nfoo", L"te st\r\n ab\r\n d foo", 7, FALSE ); + testfmt( L"te\tst\t\t\t\tab\t\t\td\nfoo", L"te\tst\t\t\r\n\t\tab\t\t\t\r\nd foo", 7, FALSE ); + testfmt( L"te st\n\n\r\n\nab d\nfoo ", L"te st\r\n ab\r\n d foo\r\n ", 7, FALSE ); + testfmt( L"te st\r\nabc d\n\nfoo\rbar", L"te st abc d foo bar", 0xff, FALSE ); + testfmt( L"te st%r%nabc d%nfoo%rbar", L"te st\r\r\nabc d\r\nfoo\rbar", 0xff, FALSE ); + testfmt( L"\01\02\03\04\a\a\a\a\b\b\b\b\t\t\t\t\v\v\v\v\f\f\f\f\r\r\r\r a", + L"\01\02\r\n\03\04\r\n\a\a\r\n\a\a\r\n\b\b\r\n\b\b\r\n\t\t\r\n\t\t\r\n\v\v\r\n\v\v\r\n\f\f\r\n\f\f\r\n\r\n\r\n\r\n\r\na", 2, FALSE ); + + for (i = 1; i < 0xffff; i++) + { + WCHAR src[] = { i, ' ', i, i, i, i, i, ' ', i, 0 }; + WCHAR expect[16]; + switch (i) + { + case '\t': + lstrcpyW( expect, L"\r\n\r\n\t" ); + break; + case '\r': + case '\n': + case ' ': + lstrcpyW( expect, L"\r\n\r\n " ); + break; + case '%': + lstrcpyW( expect, L" %% \r\nxxxx" ); + break; + default: + swprintf( expect, ARRAY_SIZE(expect), L"%c\r\n%c%c%c%c\r\n%c %c", i, i, i, i, i, i, i ); + break; + } + lstrcpyW( buffer, L"xxxxxxxxxx" ); + fmt( src, 4, FALSE, FALSE, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, expect ), "%04lx: got %s\n", i, debugstr_w(buffer) ); + } + + /* args are not counted the same way with an argument array */ + { + ULONG_PTR args[] = { 6, 4, 2, 5, 3, 1 }; + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%1!*.*u!,%1!*.*u!", 0, FALSE, FALSE, TRUE, (va_list *)args, + buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L" 0002, 00003" ), "got %s\n", wine_dbgstr_w(buffer) ); + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlFormatMessage( L"%1!*.*u!,%4!*.*u!", 0, FALSE, FALSE, TRUE, (va_list *)args, + buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L" 0002, 001" ), "got %s\n", wine_dbgstr_w(buffer) ); + } + + /* buffer overflows */ + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"testing", 0, FALSE, FALSE, buffer, 8, &size ); + ok( status == STATUS_BUFFER_OVERFLOW, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"testxxxxxx" ) || broken(!lstrcmpW( buffer, L"tesxxxxxxx" )), /* winxp */ + "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1", 0, FALSE, FALSE, buffer, 8, &size, L"test" ); + ok( status == STATUS_BUFFER_OVERFLOW, "failed %lx\n", status ); + ok( !memcmp( buffer, L"tes\0xxxxxx", 10 * sizeof(WCHAR) ) || broken(!lstrcmpW( buffer, L"testxxxxxx" )), /* winxp */ + "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!x!", 0, FALSE, FALSE, buffer, 8, &size, 0x12345678 ); + ok( status == STATUS_BUFFER_OVERFLOW, "failed %lx\n", status ); + ok( !memcmp( buffer, L"123\0xxxxxx", 10 * sizeof(WCHAR) ) || broken(!lstrcmpW( buffer, L"1234xxxxxx" )), /* winxp */ + "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!*s!", 0, FALSE, FALSE, buffer, 10, &size, 5, L"abc" ); + ok( status == STATUS_BUFFER_OVERFLOW, "failed %lx\n", status ); + ok( !memcmp( buffer, L" ab\0xxxxx", 10 * sizeof(WCHAR) ) || broken(!lstrcmpW( buffer, L" abcxxxxx" )), /* winxp */ + "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"ab%n", 0, FALSE, FALSE, buffer, 6, &size ); + ok( status == STATUS_BUFFER_OVERFLOW, "failed %lx\n", status ); + ok( !memcmp( buffer, L"abxxxxxxxx", 10 * sizeof(WCHAR) ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + /* ignore inserts */ + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!x!%r%%%n%t", 0, TRUE, FALSE, buffer, sizeof(buffer), &size ); + ok( !lstrcmpW( buffer, L"%1!x!\r%%\r\n\t" ), "got %s\n", wine_dbgstr_w(buffer) ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"ab%0cd", 0, TRUE, FALSE, buffer, sizeof(buffer), &size ); + ok( !status, "failed %lx\n", status ); + ok( !memcmp( buffer, L"ab\0xxxxxxx", 10 * sizeof(WCHAR) ), "got %s\n", wine_dbgstr_wn(buffer, 10) ); + + /* invalid args */ + lstrcpyW( buffer, L"xxxxxxxxxx" ); + size = 0xdeadbeef; + status = pRtlFormatMessage( L"abc%1", 0, FALSE, FALSE, FALSE, NULL, buffer, sizeof(buffer), &size ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abcxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = pRtlFormatMessage( L"abc%1", 0, FALSE, FALSE, TRUE, NULL, buffer, sizeof(buffer), &size ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abcxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = pRtlFormatMessage( L"abc%", 0, FALSE, FALSE, TRUE, NULL, buffer, sizeof(buffer), &size ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abcxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!u! %2!u", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"34 xxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!**u!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"*u" ), "got %s\n", wine_dbgstr_w(buffer) ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"%1!0.3+*u!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"+*u" ), "got %s\n", wine_dbgstr_w(buffer) ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"aa%1!***u!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"aaxxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"abc%1!#.000000000000000000000000000x!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abc0x22" ), "got %s\n", wine_dbgstr_w(buffer) ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"abc%1!#.0000000000000000000000000000x!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, 34 ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abcxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + lstrcpyW( buffer, L"xxxxxxxxxx" ); + status = fmt( L"abc%1!hsaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!", 0, FALSE, FALSE, buffer, sizeof(buffer), &size, "hello" ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + ok( !lstrcmpW( buffer, L"abcxxxxxxx" ), "got %s\n", wine_dbgstr_w(buffer) ); + ok( size == 0xdeadbeef, "wrong size %lu\n", size ); + + /* va_arg is eaten even in case of buffer overflow */ + testfmt_arg_eaten( L"%1!s! %2!s!", L"eaten", L"unused" ); +} + START_TEST(rtlstr) { InitFunctionPtrs(); @@ -2556,13 +2964,11 @@ START_TEST(rtlstr) test_RtlStringFromGUID(); test_RtlIsTextUnicode(); test_RtlCompareUnicodeString(); - if(0) - { - test_RtlUpcaseUnicodeChar(); - test_RtlUpcaseUnicodeString(); - test_RtlDowncaseUnicodeString(); - } + test_RtlUpcaseUnicodeChar(); + test_RtlUpcaseUnicodeString(); + test_RtlDowncaseUnicodeString(); test_RtlHashUnicodeString(); test_RtlUnicodeToUTF8N(); test_RtlUTF8ToUnicodeN(); + test_RtlFormatMessage(); } diff --git a/modules/rostests/winetests/ntdll/string.c b/modules/rostests/winetests/ntdll/string.c index 8fca5d46d5e..3d975d12a10 100644 --- a/modules/rostests/winetests/ntdll/string.c +++ b/modules/rostests/winetests/ntdll/string.c @@ -22,8 +22,16 @@ */ #include +#include +#include -#include "ntdll_test.h" +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winnls.h" +#include "wine/test.h" /* Function ptrs for ntdll calls */ @@ -32,74 +40,136 @@ static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_ static VOID (WINAPI *pRtlFreeAnsiString)(PSTRING); static BOOLEAN (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR); static VOID (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING); +static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR); -static int (WINAPIV *patoi)(const char *); -static long (WINAPIV *patol)(const char *); -static LONGLONG (WINAPIV *p_atoi64)(const char *); -static LPSTR (WINAPIV *p_itoa)(int, LPSTR, INT); -static LPSTR (WINAPIV *p_ltoa)(LONG, LPSTR, INT); -static LPSTR (WINAPIV *p_ultoa)(ULONG, LPSTR, INT); -static LPSTR (WINAPIV *p_i64toa)(LONGLONG, LPSTR, INT); -static LPSTR (WINAPIV *p_ui64toa)(ULONGLONG, LPSTR, INT); +static int (__cdecl *patoi)(const char *); +static LONG (__cdecl *patol)(const char *); +static LONGLONG (__cdecl *p_atoi64)(const char *); +static LPSTR (__cdecl *p_itoa)(int, LPSTR, INT); +static LPSTR (__cdecl *p_ltoa)(LONG, LPSTR, INT); +static LPSTR (__cdecl *p_ultoa)(ULONG, LPSTR, INT); +static LPSTR (__cdecl *p_i64toa)(LONGLONG, LPSTR, INT); +static LPSTR (__cdecl *p_ui64toa)(ULONGLONG, LPSTR, INT); -static int (WINAPIV *p_wtoi)(LPWSTR); -static long (WINAPIV *p_wtol)(LPWSTR); -static LONGLONG (WINAPIV *p_wtoi64)(LPWSTR); -static LPWSTR (WINAPIV *p_itow)(int, LPWSTR, int); -static LPWSTR (WINAPIV *p_ltow)(LONG, LPWSTR, INT); -static LPWSTR (WINAPIV *p_ultow)(ULONG, LPWSTR, INT); -static LPWSTR (WINAPIV *p_i64tow)(LONGLONG, LPWSTR, INT); -static LPWSTR (WINAPIV *p_ui64tow)(ULONGLONG, LPWSTR, INT); +static int (__cdecl *p_wtoi)(LPCWSTR); +static LONG (__cdecl *p_wtol)(LPCWSTR); +static LONGLONG (__cdecl *p_wtoi64)(LPCWSTR); +static LONG (__cdecl *pwcstol)(LPCWSTR,LPWSTR*,INT); +static ULONG (__cdecl *pwcstoul)(LPCWSTR,LPWSTR*,INT); +static LPWSTR (__cdecl *p_itow)(int, LPWSTR, int); +static LPWSTR (__cdecl *p_ltow)(LONG, LPWSTR, INT); +static LPWSTR (__cdecl *p_ultow)(ULONG, LPWSTR, INT); +static LPWSTR (__cdecl *p_i64tow)(LONGLONG, LPWSTR, INT); +static LPWSTR (__cdecl *p_ui64tow)(ULONGLONG, LPWSTR, INT); static LPWSTR (__cdecl *p_wcslwr)(LPWSTR); static LPWSTR (__cdecl *p_wcsupr)(LPWSTR); +static WCHAR (__cdecl *ptowlower)(WCHAR); +static WCHAR (__cdecl *ptowupper)(WCHAR); +static int (__cdecl *p_wcsicmp)(LPCWSTR,LPCWSTR); +static int (__cdecl *p_wcsnicmp)(LPCWSTR,LPCWSTR,int); -static LPWSTR (WINAPIV *p_wcschr)(LPCWSTR, WCHAR); -static LPWSTR (WINAPIV *p_wcsrchr)(LPCWSTR, WCHAR); +static LPWSTR (__cdecl *pwcschr)(LPCWSTR, WCHAR); +static LPWSTR (__cdecl *pwcsrchr)(LPCWSTR, WCHAR); +static void* (__cdecl *pmemchr)(const void*, int, size_t); -static void (__cdecl *p_qsort)(void *,size_t,size_t, int(__cdecl *compar)(const void *, const void *) ); -static void* (__cdecl *p_bsearch)(void *,void*,size_t,size_t, int(__cdecl *compar)(const void *, const void *) ); -static int (WINAPIV *p__snprintf)(char *, size_t, const char *, ...); +static void (__cdecl *pqsort)(void *,size_t,size_t, int(__cdecl *compar)(const void *, const void *) ); +static void* (__cdecl *pbsearch)(void *,void*,size_t,size_t, int(__cdecl *compar)(const void *, const void *) ); +static int (WINAPIV *p_snprintf)(char *, size_t, const char *, ...); +static int (WINAPIV *p_snprintf_s)(char *, size_t, size_t, const char *, ...); +static int (WINAPIV *p_snwprintf)(WCHAR *, size_t, const WCHAR *, ...); +static int (WINAPIV *p_snwprintf_s)(WCHAR *, size_t, size_t, const WCHAR *, ...); +static int (__cdecl *ptolower)(int); +static int (__cdecl *ptoupper)(int); +static int (__cdecl *p_strnicmp)(LPCSTR,LPCSTR,size_t); + +static int (WINAPIV *psscanf)(const char *, const char *, ...); + +static int (__cdecl *piswctype)(WCHAR,unsigned short); +static int (__cdecl *piswalpha)(WCHAR); +static int (__cdecl *piswdigit)(WCHAR); +static int (__cdecl *piswlower)(WCHAR); +static int (__cdecl *piswspace)(WCHAR); +static int (__cdecl *piswxdigit)(WCHAR); + +static int (__cdecl *pisalnum)(int); +static int (__cdecl *pisalpha)(int); +static int (__cdecl *piscntrl)(int); +static int (__cdecl *pisdigit)(int); +static int (__cdecl *pisgraph)(int); +static int (__cdecl *pislower)(int); +static int (__cdecl *pisprint)(int); +static int (__cdecl *pispunct)(int); +static int (__cdecl *pisspace)(int); +static int (__cdecl *pisupper)(int); +static int (__cdecl *pisxdigit)(int); static void InitFunctionPtrs(void) { hntdll = LoadLibraryA("ntdll.dll"); ok(hntdll != 0, "LoadLibrary failed\n"); - if (hntdll) { - pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString"); - pRtlFreeAnsiString = (void *)GetProcAddress(hntdll, "RtlFreeAnsiString"); - pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz"); - pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString"); - - patoi = (void *)GetProcAddress(hntdll, "atoi"); - patol = (void *)GetProcAddress(hntdll, "atol"); - p_atoi64 = (void *)GetProcAddress(hntdll, "_atoi64"); - p_itoa = (void *)GetProcAddress(hntdll, "_itoa"); - p_ltoa = (void *)GetProcAddress(hntdll, "_ltoa"); - p_ultoa = (void *)GetProcAddress(hntdll, "_ultoa"); - p_i64toa = (void *)GetProcAddress(hntdll, "_i64toa"); - p_ui64toa = (void *)GetProcAddress(hntdll, "_ui64toa"); - - p_wtoi = (void *)GetProcAddress(hntdll, "_wtoi"); - p_wtol = (void *)GetProcAddress(hntdll, "_wtol"); - p_wtoi64 = (void *)GetProcAddress(hntdll, "_wtoi64"); - p_itow = (void *)GetProcAddress(hntdll, "_itow"); - p_ltow = (void *)GetProcAddress(hntdll, "_ltow"); - p_ultow = (void *)GetProcAddress(hntdll, "_ultow"); - p_i64tow = (void *)GetProcAddress(hntdll, "_i64tow"); - p_ui64tow = (void *)GetProcAddress(hntdll, "_ui64tow"); - - p_wcslwr = (void *)GetProcAddress(hntdll, "_wcslwr"); - p_wcsupr = (void *)GetProcAddress(hntdll, "_wcsupr"); - - p_wcschr= (void *)GetProcAddress(hntdll, "wcschr"); - p_wcsrchr= (void *)GetProcAddress(hntdll, "wcsrchr"); - p_qsort= (void *)GetProcAddress(hntdll, "qsort"); - p_bsearch= (void *)GetProcAddress(hntdll, "bsearch"); - - p__snprintf = (void *)GetProcAddress(hntdll, "_snprintf"); - } /* if */ +#define X(name) p##name = (void *)GetProcAddress( hntdll, #name ); + X(RtlUnicodeStringToAnsiString); + X(RtlFreeAnsiString); + X(RtlCreateUnicodeStringFromAsciiz); + X(RtlFreeUnicodeString); + X(RtlUpcaseUnicodeChar); + X(atoi); + X(atol); + X(_atoi64); + X(_itoa); + X(_ltoa); + X(_ultoa); + X(_i64toa); + X(_ui64toa); + X(_wtoi); + X(_wtol); + X(_wtoi64); + X(wcstol); + X(wcstoul); + X(_itow); + X(_ltow); + X(_ultow); + X(_i64tow); + X(_ui64tow); + X(_wcslwr); + X(_wcsupr); + X(towlower); + X(towupper); + X(_wcsicmp); + X(_wcsnicmp); + X(wcschr); + X(wcsrchr); + X(memchr); + X(qsort); + X(bsearch); + X(_snprintf); + X(_snprintf_s); + X(_snwprintf); + X(_snwprintf_s); + X(tolower); + X(toupper); + X(_strnicmp); + X(sscanf); + X(iswctype); + X(iswalpha); + X(iswdigit); + X(iswlower); + X(iswspace); + X(iswxdigit); + X(isalnum); + X(isalpha); + X(iscntrl); + X(isdigit); + X(isgraph); + X(islower); + X(isprint); + X(ispunct); + X(isspace); + X(isupper); + X(isxdigit); +#undef X } @@ -212,7 +282,6 @@ static const ulong2str_t ulong2str[] = { {36, 62193781, "111111\0------------------------------------------------------------", 0x77}, {37, 71270178, "111111\0------------------------------------------------------------", 0x77}, }; -#define NB_ULONG2STR (sizeof(ulong2str)/sizeof(*ulong2str)) static void one_itoa_test(int test_num, const ulong2str_t *ulong2str) @@ -245,10 +314,10 @@ static void one_ltoa_test(int test_num, const ulong2str_t *ulong2str) value = ulong2str->value; result = p_ltoa(ulong2str->value, dest_str, ulong2str->base); ok(result == dest_str, - "(test %d): _ltoa(%d, [out], %d) has result %p, expected: %p\n", + "(test %d): _ltoa(%ld, [out], %d) has result %p, expected: %p\n", test_num, value, ulong2str->base, result, dest_str); ok(memcmp(dest_str, ulong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0, - "(test %d): _ltoa(%d, [out], %d) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): _ltoa(%ld, [out], %d) assigns string \"%s\", expected: \"%s\"\n", test_num, value, ulong2str->base, dest_str, ulong2str->Buffer); } @@ -264,10 +333,10 @@ static void one_ultoa_test(int test_num, const ulong2str_t *ulong2str) value = ulong2str->value; result = p_ultoa(ulong2str->value, dest_str, ulong2str->base); ok(result == dest_str, - "(test %d): _ultoa(%u, [out], %d) has result %p, expected: %p\n", + "(test %d): _ultoa(%lu, [out], %d) has result %p, expected: %p\n", test_num, value, ulong2str->base, result, dest_str); ok(memcmp(dest_str, ulong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0, - "(test %d): _ultoa(%u, [out], %d) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): _ultoa(%lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n", test_num, value, ulong2str->base, dest_str, ulong2str->Buffer); } @@ -276,7 +345,7 @@ static void test_ulongtoa(void) { int test_num; - for (test_num = 0; test_num < NB_ULONG2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(ulong2str); test_num++) { if (ulong2str[test_num].mask & 0x01) { one_itoa_test(test_num, &ulong2str[test_num]); } /* if */ @@ -352,10 +421,10 @@ static void one_ltow_test(int test_num, const ulong2str_t *ulong2str) result = p_ltow(value, dest_wstr, ulong2str->base); pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1); ok(result == dest_wstr, - "(test %d): _ltow(%d, [out], %d) has result %p, expected: %p\n", + "(test %d): _ltow(%ld, [out], %d) has result %p, expected: %p\n", test_num, value, ulong2str->base, result, dest_wstr); ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0, - "(test %d): _ltow(%d, [out], %d) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): _ltow(%ld, [out], %d) assigns string \"%s\", expected: \"%s\"\n", test_num, value, ulong2str->base, ansi_str.Buffer, ulong2str->Buffer); pRtlFreeAnsiString(&ansi_str); } @@ -388,10 +457,10 @@ static void one_ultow_test(int test_num, const ulong2str_t *ulong2str) result = p_ultow(value, dest_wstr, ulong2str->base); pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1); ok(result == dest_wstr, - "(test %d): _ultow(%u, [out], %d) has result %p, expected: %p\n", + "(test %d): _ultow(%lu, [out], %d) has result %p, expected: %p\n", test_num, value, ulong2str->base, result, dest_wstr); ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0, - "(test %d): _ultow(%u, [out], %d) assigns string \"%s\", expected: \"%s\"\n", + "(test %d): _ultow(%lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n", test_num, value, ulong2str->base, ansi_str.Buffer, ulong2str->Buffer); pRtlFreeAnsiString(&ansi_str); } @@ -402,7 +471,7 @@ static void test_ulongtow(void) int test_num; LPWSTR result; - for (test_num = 0; test_num < NB_ULONG2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(ulong2str); test_num++) { if (ulong2str[test_num].mask & 0x10) { one_itow_test(test_num, &ulong2str[test_num]); } /* if */ @@ -418,7 +487,7 @@ static void test_ulongtow(void) /* Crashes on XP and W2K3 */ result = p_itow(ulong2str[0].value, NULL, 10); ok(result == NULL, - "(test a): _itow(%d, NULL, 10) has result %p, expected: NULL\n", + "(test a): _itow(%ld, NULL, 10) has result %p, expected: NULL\n", ulong2str[0].value, result); } @@ -426,7 +495,7 @@ static void test_ulongtow(void) /* Crashes on XP and W2K3 */ result = p_ltow(ulong2str[0].value, NULL, 10); ok(result == NULL, - "(test b): _ltow(%d, NULL, 10) has result %p, expected: NULL\n", + "(test b): _ltow(%ld, NULL, 10) has result %p, expected: NULL\n", ulong2str[0].value, result); } @@ -434,7 +503,7 @@ static void test_ulongtow(void) /* Crashes on XP and W2K3 */ result = p_ultow(ulong2str[0].value, NULL, 10); ok(result == NULL, - "(test c): _ultow(%d, NULL, 10) has result %p, expected: NULL\n", + "(test c): _ultow(%ld, NULL, 10) has result %p, expected: NULL\n", ulong2str[0].value, result); } } @@ -588,7 +657,6 @@ static const ulonglong2str_t ulonglong2str[] = { {37, 71270178, "111111\0------------------------------------------------------------", 0x33}, {99, ULL(0x2,0x3c9e468c), "111111\0------------------------------------------------------------", 0x33}, }; -#define NB_ULONGLONG2STR (sizeof(ulonglong2str)/sizeof(*ulonglong2str)) static void one_i64toa_test(int test_num, const ulonglong2str_t *ulonglong2str) @@ -642,7 +710,7 @@ static void test_ulonglongtoa(void) { int test_num; - for (test_num = 0; test_num < NB_ULONGLONG2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(ulonglong2str); test_num++) { if (ulonglong2str[test_num].mask & 0x01) { one_i64toa_test(test_num, &ulonglong2str[test_num]); } /* if */ @@ -746,7 +814,7 @@ static void test_ulonglongtow(void) int test_num; LPWSTR result; - for (test_num = 0; test_num < NB_ULONGLONG2STR; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(ulonglong2str); test_num++) { if (ulonglong2str[test_num].mask & 0x10) { one_i64tow_test(test_num, &ulonglong2str[test_num]); } /* if */ @@ -885,7 +953,6 @@ static const str2long_t str2long[] = { { "", 0 }, /* empty string */ /* { NULL, 0 }, */ /* NULL as string */ }; -#define NB_STR2LONG (sizeof(str2long)/sizeof(*str2long)) static void test_wtoi(void) @@ -894,11 +961,11 @@ static void test_wtoi(void) UNICODE_STRING uni; int result; - for (test_num = 0; test_num < NB_STR2LONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2long); test_num++) { pRtlCreateUnicodeStringFromAsciiz(&uni, str2long[test_num].str); result = p_wtoi(uni.Buffer); ok(result == str2long[test_num].value, - "(test %d): call failed: _wtoi(\"%s\") has result %d, expected: %d\n", + "(test %d): call failed: _wtoi(\"%s\") has result %d, expected: %ld\n", test_num, str2long[test_num].str, result, str2long[test_num].value); pRtlFreeUnicodeString(&uni); } /* for */ @@ -909,10 +976,10 @@ static void test_atoi(void) int test_num; int result; - for (test_num = 0; test_num < NB_STR2LONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2long); test_num++) { result = patoi(str2long[test_num].str); ok(result == str2long[test_num].value, - "(test %d): call failed: _atoi(\"%s\") has result %d, expected: %d\n", + "(test %d): call failed: _atoi(\"%s\") has result %d, expected: %ld\n", test_num, str2long[test_num].str, result, str2long[test_num].value); } } @@ -922,10 +989,10 @@ static void test_atol(void) int test_num; int result; - for (test_num = 0; test_num < NB_STR2LONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2long); test_num++) { result = patol(str2long[test_num].str); ok(result == str2long[test_num].value, - "(test %d): call failed: _atol(\"%s\") has result %d, expected: %d\n", + "(test %d): call failed: _atol(\"%s\") has result %d, expected: %ld\n", test_num, str2long[test_num].str, result, str2long[test_num].value); } } @@ -936,14 +1003,18 @@ static void test_wtol(void) UNICODE_STRING uni; LONG result; - for (test_num = 0; test_num < NB_STR2LONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2long); test_num++) { pRtlCreateUnicodeStringFromAsciiz(&uni, str2long[test_num].str); result = p_wtol(uni.Buffer); ok(result == str2long[test_num].value, - "(test %d): call failed: _wtol(\"%s\") has result %d, expected: %d\n", + "(test %d): call failed: _wtol(\"%s\") has result %ld, expected: %ld\n", test_num, str2long[test_num].str, result, str2long[test_num].value); pRtlFreeUnicodeString(&uni); - } /* for */ + } + result = p_wtol( L"\t\xa0\n 12" ); + ok( result == 12, "got %ld\n", result ); + result = p_wtol( L"\x3000 12" ); + ok( result == 0, "got %ld\n", result ); } @@ -1068,7 +1139,6 @@ static const str2longlong_t str2longlong[] = { { "", 0 }, /* empty string */ /* { NULL, 0 }, */ /* NULL as string */ }; -#define NB_STR2LONGLONG (sizeof(str2longlong)/sizeof(*str2longlong)) static void test_atoi64(void) @@ -1076,7 +1146,7 @@ static void test_atoi64(void) int test_num; LONGLONG result; - for (test_num = 0; test_num < NB_STR2LONGLONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2longlong); test_num++) { result = p_atoi64(str2longlong[test_num].str); if (str2longlong[test_num].overflow) ok(result == str2longlong[test_num].value || @@ -1100,7 +1170,7 @@ static void test_wtoi64(void) UNICODE_STRING uni; LONGLONG result; - for (test_num = 0; test_num < NB_STR2LONGLONG; test_num++) { + for (test_num = 0; test_num < ARRAY_SIZE(str2longlong); test_num++) { pRtlCreateUnicodeStringFromAsciiz(&uni, str2longlong[test_num].str); result = p_wtoi64(uni.Buffer); if (str2longlong[test_num].overflow) @@ -1117,17 +1187,87 @@ static void test_wtoi64(void) wine_dbgstr_longlong(str2longlong[test_num].value)); pRtlFreeUnicodeString(&uni); } + result = p_wtoi64( L"\t\xa0\n 12" ); + ok( result == 12, "got %s\n", wine_dbgstr_longlong(result) ); + result = p_wtoi64( L"\x2002\x2003 12" ); + ok( result == 0, "got %s\n", wine_dbgstr_longlong(result) ); +} + +static void test_wcstol(void) +{ + static const struct { WCHAR str[24]; LONG res; ULONG ures; int base; } tests[] = + { + { L"9", 9, 9, 10 }, + { L" ", 0, 0 }, + { L"-1234", -1234, -1234 }, + { L"\x09\x0a\x0b\x0c\x0d -123", -123, -123 }, + { L"\xa0 +44", 44, 44 }, + { L"\x2002\x2003 +55", 0, 0 }, + { L"\x3000 +66", 0, 0 }, + { { 0x3231 }, 0, 0 }, /* PARENTHESIZED IDEOGRAPH STOCK */ + { { 0x4e00 }, 0, 0 }, /* CJK Ideograph, First */ + { { 0x0bef }, 0, 0 }, /* TAMIL DIGIT NINE */ + { { 0x0e59 }, 9, 9 }, /* THAI DIGIT NINE */ + { { 0xff19 }, 9, 9 }, /* FULLWIDTH DIGIT NINE */ + { { 0x00b9 }, 0, 0 }, /* SUPERSCRIPT ONE */ + { { '-',0x0e50,'x',0xff19,'1' }, -0x91, -0x91 }, + { { '+',0x0e50,0xff17,'1' }, 071, 071 }, + { { 0xff19,'f',0x0e59,0xff46 }, 0x9f9, 0x9f9, 16 }, + { L"2147483647", 2147483647, 2147483647 }, + { L"2147483648", LONG_MAX, 2147483648 }, + { L"4294967295", LONG_MAX, 4294967295 }, + { L"4294967296", LONG_MAX, ULONG_MAX }, + { L"9223372036854775807", LONG_MAX, ULONG_MAX }, + { L"-2147483647", -2147483647, -2147483647 }, + { L"-2147483648", LONG_MIN, LONG_MIN }, + { L"-4294967295", LONG_MIN, 1 }, + { L"-4294967296", LONG_MIN, 1 }, + { L"-9223372036854775807", LONG_MIN, 1 }, + }; + static const WCHAR zeros[] = + { + 0x0660, 0x06f0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0c66, 0x0ce6, + 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x17e0, 0x1810, 0xff10 + }; + unsigned int i; + LONG res; + ULONG ures; + WCHAR *endpos; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + res = pwcstol( tests[i].str, &endpos, tests[i].base ); + ok( res == tests[i].res, "%u: %s res %08lx\n", i, wine_dbgstr_w(tests[i].str), res ); + if (!res) ok( endpos == tests[i].str, "%u: wrong endpos %p/%p\n", i, endpos, tests[i].str ); + ures = pwcstoul( tests[i].str, &endpos, tests[i].base ); + ok( ures == tests[i].ures, "%u: %s res %08lx\n", i, wine_dbgstr_w(tests[i].str), ures ); + } + + /* Test various unicode digits */ + for (i = 0; i < ARRAY_SIZE(zeros); ++i) + { + WCHAR tmp[] = { zeros[i] + 4, zeros[i], zeros[i] + 5, 0 }; + res = pwcstol(tmp, NULL, 0); + ok(res == 405, "with zero = U+%04X: got %ld, expected 405\n", zeros[i], res); + ures = pwcstoul(tmp, NULL, 0); + ok(ures == 405, "with zero = U+%04X: got %lu, expected 405\n", zeros[i], ures); + tmp[1] = zeros[i] + 10; + res = pwcstol(tmp, NULL, 16); + ok(res == 4, "with zero = U+%04X: got %ld, expected 4\n", zeros[i], res); + ures = pwcstoul(tmp, NULL, 16); + ok(ures == 4, "with zero = U+%04X: got %lu, expected 4\n", zeros[i], ures); + } } static void test_wcschr(void) { static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0}; - ok(p_wcschr(teststringW, 'a') == teststringW + 0, + ok(pwcschr(teststringW, 'a') == teststringW + 0, "wcschr should have returned a pointer to the first 'a' character\n"); - ok(p_wcschr(teststringW, 0) == teststringW + 11, + ok(pwcschr(teststringW, 0) == teststringW + 11, "wcschr should have returned a pointer to the null terminator\n"); - ok(p_wcschr(teststringW, 'x') == NULL, + ok(pwcschr(teststringW, 'x') == NULL, "wcschr should have returned NULL\n"); } @@ -1135,11 +1275,11 @@ static void test_wcsrchr(void) { static const WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0}; - ok(p_wcsrchr(teststringW, 'a') == teststringW + 10, + ok(pwcsrchr(teststringW, 'a') == teststringW + 10, "wcsrchr should have returned a pointer to the last 'a' character\n"); - ok(p_wcsrchr(teststringW, 0) == teststringW + 11, + ok(pwcsrchr(teststringW, 0) == teststringW + 11, "wcsrchr should have returned a pointer to the null terminator\n"); - ok(p_wcsrchr(teststringW, 'x') == NULL, + ok(pwcsrchr(teststringW, 'x') == NULL, "wcsrchr should have returned NULL\n"); } @@ -1148,6 +1288,8 @@ static void test_wcslwrupr(void) static WCHAR teststringW[] = {'a','b','r','a','c','a','d','a','b','r','a',0}; static WCHAR emptyW[] = {0}; static const WCHAR constemptyW[] = {0}; + WCHAR buffer[65536]; + int i; if (0) /* crashes on native */ { @@ -1163,6 +1305,48 @@ static void test_wcslwrupr(void) ok(p_wcsupr(emptyW) == emptyW, "p_wcsupr returned different string\n"); ok(p_wcslwr((LPWSTR)constemptyW) == constemptyW, "p_wcslwr returned different string\n"); ok(p_wcsupr((LPWSTR)constemptyW) == constemptyW, "p_wcsupr returned different string\n"); + + for (i = 0; i < 65536; i++) + { + WCHAR lwr = ((i >= 'A' && i <= 'Z') || (i >= 0xc0 && i <= 0xd6) || (i >= 0xd8 && i <= 0xde)) ? i + 32 : i; + WCHAR upr = pRtlUpcaseUnicodeChar( i ); + ok( ptowlower( i ) == lwr, "%04x: towlower got %04x expected %04x\n", i, ptowlower( i ), lwr ); + ok( ptowupper( i ) == upr, "%04x: towupper got %04x expected %04x\n", i, ptowupper( i ), upr ); + } + + for (i = 1; i < 65536; i++) buffer[i - 1] = i; + buffer[65535] = 0; + p_wcslwr( buffer ); + for (i = 1; i < 65536; i++) + ok( buffer[i - 1] == (i >= 'A' && i <= 'Z' ? i + 32 : i), "%04x: got %04x\n", i, buffer[i-1] ); + + for (i = 1; i < 65536; i++) buffer[i - 1] = i; + buffer[65535] = 0; + p_wcsupr( buffer ); + for (i = 1; i < 65536; i++) + ok( buffer[i - 1] == (i >= 'a' && i <= 'z' ? i - 32 : i), "%04x: got %04x\n", i, buffer[i-1] ); +} + +static void test_wcsicmp(void) +{ + WCHAR buf_a[2], buf_b[2]; + int i, j, ret; + + buf_a[1] = buf_b[1] = 0; + for (i = 0; i < 0x300; i++) + { + int lwr_a = (i >= 'A' && i <= 'Z') ? i + 32 : i; + buf_a[0] = i; + for (j = 0; j < 0x300; j++) + { + int lwr_b = (j >= 'A' && j <= 'Z') ? j + 32 : j; + buf_b[0] = j; + ret = p_wcsicmp( buf_a, buf_b ); + ok( ret == lwr_a - lwr_b, "%04x:%04x: strings differ %d\n", i, j, ret ); + ret = p_wcsnicmp( buf_a, buf_b, 2 ); + ok( ret == lwr_a - lwr_b, "%04x:%04x: strings differ %d\n", i, j, ret ); + } + } } static int __cdecl intcomparefunc(const void *a, const void *b) @@ -1193,6 +1377,16 @@ static int __cdecl strcomparefunc(const void *a, const void *b) return lstrcmpA(*p, *q); } +static int __cdecl istrcomparefunc(const void *a, const void *b) +{ + const char * const *p = a; + const char * const *q = b; + + ok (a != b, "must never get the same pointer\n"); + + return lstrcmpiA(*p, *q); +} + static void test_qsort(void) { int arr[5] = { 23, 42, 8, 4, 16 }; @@ -1206,43 +1400,52 @@ static void test_qsort(void) "Sorted", "." }; + const char *strarr2[7] = { + "Hello", + "Wine", + "World", + "!", + "wine", + "Sorted", + "WINE" + }; - p_qsort ((void*)arr, 0, sizeof(int), intcomparefunc); + pqsort ((void*)arr, 0, sizeof(int), intcomparefunc); ok(arr[0] == 23, "badly sorted, nmemb=0, arr[0] is %d\n", arr[0]); ok(arr[1] == 42, "badly sorted, nmemb=0, arr[1] is %d\n", arr[1]); ok(arr[2] == 8, "badly sorted, nmemb=0, arr[2] is %d\n", arr[2]); ok(arr[3] == 4, "badly sorted, nmemb=0, arr[3] is %d\n", arr[3]); ok(arr[4] == 16, "badly sorted, nmemb=0, arr[4] is %d\n", arr[4]); - p_qsort ((void*)arr, 1, sizeof(int), intcomparefunc); + pqsort ((void*)arr, 1, sizeof(int), intcomparefunc); ok(arr[0] == 23, "badly sorted, nmemb=1, arr[0] is %d\n", arr[0]); ok(arr[1] == 42, "badly sorted, nmemb=1, arr[1] is %d\n", arr[1]); ok(arr[2] == 8, "badly sorted, nmemb=1, arr[2] is %d\n", arr[2]); ok(arr[3] == 4, "badly sorted, nmemb=1, arr[3] is %d\n", arr[3]); ok(arr[4] == 16, "badly sorted, nmemb=1, arr[4] is %d\n", arr[4]); - p_qsort ((void*)arr, 5, 0, intcomparefunc); + pqsort ((void*)arr, 5, 0, intcomparefunc); ok(arr[0] == 23, "badly sorted, size=0, arr[0] is %d\n", arr[0]); ok(arr[1] == 42, "badly sorted, size=0, arr[1] is %d\n", arr[1]); ok(arr[2] == 8, "badly sorted, size=0, arr[2] is %d\n", arr[2]); ok(arr[3] == 4, "badly sorted, size=0, arr[3] is %d\n", arr[3]); ok(arr[4] == 16, "badly sorted, size=0, arr[4] is %d\n", arr[4]); - p_qsort ((void*)arr, 5, sizeof(int), intcomparefunc); + pqsort ((void*)arr, 5, sizeof(int), intcomparefunc); ok(arr[0] == 4, "badly sorted, arr[0] is %d\n", arr[0]); ok(arr[1] == 8, "badly sorted, arr[1] is %d\n", arr[1]); ok(arr[2] == 16, "badly sorted, arr[2] is %d\n", arr[2]); ok(arr[3] == 23, "badly sorted, arr[3] is %d\n", arr[3]); ok(arr[4] == 42, "badly sorted, arr[4] is %d\n", arr[4]); - p_qsort ((void*)carr, 5, sizeof(char), charcomparefunc); + pqsort ((void*)carr, 5, sizeof(char), charcomparefunc); ok(carr[0] == 4, "badly sorted, carr[0] is %d\n", carr[0]); ok(carr[1] == 8, "badly sorted, carr[1] is %d\n", carr[1]); ok(carr[2] == 16, "badly sorted, carr[2] is %d\n", carr[2]); ok(carr[3] == 23, "badly sorted, carr[3] is %d\n", carr[3]); ok(carr[4] == 42, "badly sorted, carr[4] is %d\n", carr[4]); - p_qsort ((void*)strarr, 7, sizeof(char*), strcomparefunc); + pqsort ((void*)strarr, 7, sizeof(char*), strcomparefunc); ok(!strcmp(strarr[0],"!"), "badly sorted, strarr[0] is %s\n", strarr[0]); ok(!strcmp(strarr[1],"."), "badly sorted, strarr[1] is %s\n", strarr[1]); ok(!strcmp(strarr[2],"Hello"), "badly sorted, strarr[2] is %s\n", strarr[2]); @@ -1250,6 +1453,15 @@ static void test_qsort(void) ok(!strcmp(strarr[4],"Sorted"), "badly sorted, strarr[4] is %s\n", strarr[4]); ok(!strcmp(strarr[5],"Wine"), "badly sorted, strarr[5] is %s\n", strarr[5]); ok(!strcmp(strarr[6],"World"), "badly sorted, strarr[6] is %s\n", strarr[6]); + + pqsort ((void*)strarr2, 7, sizeof(char*), istrcomparefunc); + ok(!strcmp(strarr2[0], "!"), "badly sorted, strar2r[0] is %s\n", strarr2[0]); + ok(!strcmp(strarr2[1], "Hello"), "badly sorted, strarr2[1] is %s\n", strarr2[1]); + ok(!strcmp(strarr2[2], "Sorted"), "badly sorted, strarr2[2] is %s\n", strarr2[2]); + ok(!strcmp(strarr2[3], "wine"), "badly sorted, strarr2[3] is %s\n", strarr2[3]); + ok(!strcmp(strarr2[4], "WINE"), "badly sorted, strarr2[4] is %s\n", strarr2[4]); + ok(!strcmp(strarr2[5], "Wine"), "badly sorted, strarr2[5] is %s\n", strarr2[5]); + ok(!strcmp(strarr2[6], "World"), "badly sorted, strarr2[6] is %s\n", strarr2[6]); } static void test_bsearch(void) @@ -1258,14 +1470,14 @@ static void test_bsearch(void) int *x, l, i, j; /* just try all array sizes */ - for (j=1;j= 'A' && (char)i <= 'Z' ? i - 'A' + 'a' : i; + ret = ptolower(i); + ok(ret == exp_ret, "tolower(%d) = %d\n", i, ret); + } +} + +static void test_toupper(void) +{ + + int i, ret, exp_ret; + char str[3], *p; + WCHAR wc; + + ok(ptoupper != NULL, "toupper is not available\n"); + + for (i = -512; i < 0xffff; i++) + { + str[0] = i; + str[1] = i >> 8; + str[2] = 0; + p = str; + wc = RtlAnsiCharToUnicodeChar( &p ); + wc = RtlUpcaseUnicodeChar( wc ); + ret = WideCharToMultiByte( CP_ACP, 0, &wc, 1, str, 2, NULL, NULL ); + ok(!ret || ret == 1 || ret == 2, "WideCharToMultiByte returned %d\n", ret); + if (ret == 2) + exp_ret = (unsigned char)str[1] + ((unsigned char)str[0] << 8); + else if (ret == 1) + exp_ret = (unsigned char)str[0]; + else + exp_ret = (WCHAR)i; + + ret = (WCHAR)ptoupper(i); + ok(ret == exp_ret, "toupper(%x) = %x, expected %x\n", i, ret, exp_ret); + } +} + +static void test__strnicmp(void) +{ + BOOL is_win64 = (sizeof(void *) > sizeof(int)); + int ret; + + ok(p_strnicmp != NULL, "_strnicmp is not available\n"); + + ret = p_strnicmp("a", "C", 1); + ok(ret == (is_win64 ? -2 : -1), "_strnicmp returned %d\n", ret); + ret = p_strnicmp("a", "c", 1); + ok(ret == (is_win64 ? -2 : -1), "_strnicmp returned %d\n", ret); + ret = p_strnicmp("C", "a", 1); + ok(ret == (is_win64 ? 2 : 1), "_strnicmp returned %d\n", ret); + ret = p_strnicmp("c", "a", 1); + ok(ret == (is_win64 ? 2 : 1), "_strnicmp returned %d\n", ret); + ret = p_strnicmp("ijk0", "IJK1", 3); + ok(!ret, "_strnicmp returned %d\n", ret); + ret = p_strnicmp("ijk0", "IJK1", 4); + ok(ret == -1, "_strnicmp returned %d\n", ret); + ret = p_strnicmp("ijk\0X", "IJK\0Y", 5); + ok(!ret, "_strnicmp returned %d\n", ret); +} + +static void test_sscanf(void) +{ + double d = 0.0; + float f = 0.0f; + int i = 0; + int ret; + + ret = psscanf("10", "%d", &i); + ok(ret == 1, "ret = %d\n", ret); + ok(i == 10, "i = %d\n", i); + + ret = psscanf("10", "%f", &f); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(f == 0.0f, "f = %f\n", f); + + ret = psscanf("10", "%g", &f); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(f == 0.0f, "f = %f\n", f); + + ret = psscanf("10", "%e", &f); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(f == 0.0f, "f = %f\n", f); + + ret = psscanf("10", "%lf", &d); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(d == 0.0, "d = %lf\n", f); + + ret = psscanf("10", "%lg", &d); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(d == 0.0, "d = %lf\n", f); + + ret = psscanf("10", "%le", &d); + ok(ret == 0 || broken(ret == 1) /* xp/2003 */, "ret = %d\n", ret); + ok(d == 0.0, "d = %lf\n", f); +} + +static const unsigned short wctypes[256] = +{ + /* 00 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0068, 0x0028, 0x0028, 0x0028, 0x0028, 0x0020, 0x0020, + /* 10 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* 20 */ + 0x0048, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 30 */ + 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, + 0x0084, 0x0084, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 40 */ + 0x0010, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + /* 50 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 60 */ + 0x0010, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + /* 70 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0010, 0x0010, 0x0010, 0x0010, 0x0020, + /* 80 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* 90 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* a0 */ + 0x0048, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* b0 */ + 0x0010, 0x0010, 0x0014, 0x0014, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0014, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* c0 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + /* d0 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0010, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0102, + /* e0 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + /* f0 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0010, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102 +}; + +static void test_wctype(void) +{ + int i; + + for (i = 0; i < 65536; i++) + { + unsigned short type = (i < 256 ? wctypes[i] : 0); + ok( piswctype( i, 0xffff ) == type, "%u: wrong type %x\n", i, piswctype( i, 0xffff )); + ok( piswalpha( i ) == (type & (C1_ALPHA|C1_LOWER|C1_UPPER)), "%u: wrong iswalpha\n", i ); + ok( piswdigit( i ) == (type & C1_DIGIT), "%u: wrong iswdigit\n", i ); + ok( piswlower( i ) == (type & C1_LOWER), "%u: wrong iswlower\n", i ); + ok( piswspace( i ) == (type & C1_SPACE), "%u: wrong iswspace\n", i ); + ok( piswxdigit( i ) == (type & C1_XDIGIT), "%u: wrong iswxdigit\n", i ); + } +} + +/* we could reuse wctypes except for TAB, which doesn't have C1_BLANK for some reason... */ +static const unsigned short ctypes[256] = +{ + /* 00 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0028, 0x0028, 0x0028, 0x0028, 0x0028, 0x0020, 0x0020, + /* 10 */ + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, + /* 20 */ + 0x0048, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 30 */ + 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, 0x0084, + 0x0084, 0x0084, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 40 */ + 0x0010, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + /* 50 */ + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, + /* 60 */ + 0x0010, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0182, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + /* 70 */ + 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, + 0x0102, 0x0102, 0x0102, 0x0010, 0x0010, 0x0010, 0x0010, 0x0020 +}; + +static void test_ctype(void) +{ + int i; + + for (i = -1; i < 256; i++) + { + unsigned short type = (i >= 0 ? ctypes[i] : 0); + ok( pisalnum( i ) == (type & (C1_DIGIT|C1_LOWER|C1_UPPER)), "%u: wrong isalnum %x / %x\n", i, pisalnum(i), type ); + ok( pisalpha( i ) == (type & (C1_LOWER|C1_UPPER)), "%u: wrong isalpha %x / %x\n", i, pisalpha(i), type ); + ok( piscntrl( i ) == (type & C1_CNTRL), "%u: wrong iscntrl %x / %x\n", i, piscntrl( i ), type ); + ok( pisdigit( i ) == (type & C1_DIGIT), "%u: wrong isdigit %x / %x\n", i, pisdigit( i ), type ); + ok( pisgraph( i ) == (type & (C1_DIGIT|C1_PUNCT|C1_LOWER|C1_UPPER)), "%u: wrong isgraph %x / %x\n", i, pisgraph( i ), type ); + ok( pislower( i ) == (type & C1_LOWER), "%u: wrong islower %x / %x\n", i, pislower( i ), type ); + ok( pisprint( i ) == (type & (C1_DIGIT|C1_BLANK|C1_PUNCT|C1_LOWER|C1_UPPER)), "%u: wrong isprint %x / %x\n", i, pisprint( i ), type ); + ok( pispunct( i ) == (type & C1_PUNCT), "%u: wrong ispunct %x / %x\n", i, pispunct( i ), type ); + ok( pisspace( i ) == (type & C1_SPACE), "%u: wrong isspace %x / %x\n", i, pisspace( i ), type ); + ok( pisupper( i ) == (type & C1_UPPER), "%u: wrong isupper %x / %x\n", i, pisupper( i ), type ); + ok( pisxdigit( i ) == (type & C1_XDIGIT), "%u: wrong isxdigit %x / %x\n", i, pisxdigit( i ), type ); + } +} + +static void test_memchr(void) +{ + const char s[] = "ab"; + char *r; + + r = pmemchr(s, 'z', 2); + ok(!r, "memchr returned %p, expected NULL\n", r); + + r = pmemchr(s, 'a', 2); + ok(r == s, "memchr returned %p, expected %p\n", r, s); + + r = pmemchr(s, 0x100 + 'a', 2); + ok(r == s, "memchr returned %p, expected %p\n", r, s); + + r = pmemchr(s, -0x100 + 'a', 2); + ok(r == s, "memchr returned %p, expected %p\n", r, s); } START_TEST(string) { InitFunctionPtrs(); - if (p_ultoa) - test_ulongtoa(); - if (p_ui64toa) - test_ulonglongtoa(); - if (p_atoi64) - test_atoi64(); - if (p_ultow) - test_ulongtow(); - if (p_ui64tow) - test_ulonglongtow(); - if (p_wtoi) - test_wtoi(); - if (p_wtol) - test_wtol(); - if (p_wtoi64) - test_wtoi64(); - if (p_wcschr) - test_wcschr(); - if (p_wcsrchr) - test_wcsrchr(); - if (p_wcslwr && p_wcsupr) - test_wcslwrupr(); - if (patoi) - test_atoi(); - if (patol) - test_atol(); - if (p_qsort) - test_qsort(); - if (p_bsearch) - test_bsearch(); - if (p__snprintf) - test__snprintf(); + test_ulongtoa(); + test_ulonglongtoa(); + test_atoi64(); + test_ulongtow(); + test_ulonglongtow(); + test_wtoi(); + test_wtol(); + test_wtoi64(); + test_wcstol(); + test_wcschr(); + test_wcsrchr(); + test_wcslwrupr(); + test_atoi(); + test_atol(); + test_qsort(); + test_bsearch(); + test__snprintf(); + test__snprintf_s(); + test__snwprintf(); + test__snwprintf_s(); + test_printf_format(); + test_tolower(); + test_toupper(); + test__strnicmp(); + test_wcsicmp(); + test_sscanf(); + test_wctype(); + test_ctype(); + test_memchr(); } diff --git a/modules/rostests/winetests/ntdll/sync.c b/modules/rostests/winetests/ntdll/sync.c new file mode 100644 index 00000000000..aa945b445bf --- /dev/null +++ b/modules/rostests/winetests/ntdll/sync.c @@ -0,0 +1,1106 @@ +/* + * Unit tests for NT synchronization objects + * + * Copyright 2020 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "wine/test.h" + +#if defined (__REACTOS__) && (DLL_EXPORT_VERSION < 0x601) +static +NTSTATUS +WINAPI +NtRemoveIoCompletionEx_( + _In_ HANDLE IoCompletionHandle, + _Out_writes_to_(Count, *NumEntriesRemoved) PFILE_IO_COMPLETION_INFORMATION IoCompletionInformation, + _In_ ULONG Count, + _Out_ PULONG NumEntriesRemoved, + _In_opt_ PLARGE_INTEGER Timeout, + _In_ BOOLEAN Alertable) +{ + // HACK! + *NumEntriesRemoved = Count; + return NtRemoveIoCompletion(IoCompletionHandle, + &IoCompletionInformation[0].CompletionKey, + &IoCompletionInformation[0].CompletionValue, + &IoCompletionInformation[0].IoStatusBlock, + Timeout); +} +#define NtRemoveIoCompletionEx NtRemoveIoCompletionEx_ +#endif + +static NTSTATUS (WINAPI *pNtAlertThreadByThreadId)( HANDLE ); +static NTSTATUS (WINAPI *pNtClose)( HANDLE ); +static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const OBJECT_ATTRIBUTES *, EVENT_TYPE, BOOLEAN); +static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); +static NTSTATUS (WINAPI *pNtCreateMutant)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, BOOLEAN ); +static NTSTATUS (WINAPI *pNtCreateSemaphore)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, LONG, LONG ); +static NTSTATUS (WINAPI *pNtOpenEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); +static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); +static NTSTATUS (WINAPI *pNtPulseEvent)( HANDLE, LONG * ); +static NTSTATUS (WINAPI *pNtQueryEvent)( HANDLE, EVENT_INFORMATION_CLASS, void *, ULONG, ULONG * ); +static NTSTATUS (WINAPI *pNtQueryMutant)( HANDLE, MUTANT_INFORMATION_CLASS, void *, ULONG, ULONG * ); +static NTSTATUS (WINAPI *pNtQuerySemaphore)( HANDLE, SEMAPHORE_INFORMATION_CLASS, void *, ULONG, ULONG * ); +static NTSTATUS (WINAPI *pNtQuerySystemTime)( LARGE_INTEGER * ); +static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); +static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, LONG * ); +static NTSTATUS (WINAPI *pNtReleaseSemaphore)( HANDLE, ULONG, ULONG * ); +static NTSTATUS (WINAPI *pNtResetEvent)( HANDLE, LONG * ); +static NTSTATUS (WINAPI *pNtSetEvent)( HANDLE, LONG * ); +static NTSTATUS (WINAPI *pNtWaitForAlertByThreadId)( void *, const LARGE_INTEGER * ); +static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); +static BOOLEAN (WINAPI *pRtlAcquireResourceExclusive)( RTL_RWLOCK *, BOOLEAN ); +static BOOLEAN (WINAPI *pRtlAcquireResourceShared)( RTL_RWLOCK *, BOOLEAN ); +static void (WINAPI *pRtlDeleteResource)( RTL_RWLOCK * ); +static void (WINAPI *pRtlInitializeResource)( RTL_RWLOCK * ); +static void (WINAPI *pRtlInitUnicodeString)( UNICODE_STRING *, const WCHAR * ); +static void (WINAPI *pRtlReleaseResource)( RTL_RWLOCK * ); +static NTSTATUS (WINAPI *pRtlWaitOnAddress)( const void *, const void *, SIZE_T, const LARGE_INTEGER * ); +static void (WINAPI *pRtlWakeAddressAll)( const void * ); +static void (WINAPI *pRtlWakeAddressSingle)( const void * ); + +#define KEYEDEVENT_WAIT 0x0001 +#define KEYEDEVENT_WAKE 0x0002 +#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) + +static void test_event(void) +{ + HANDLE event; + HANDLE event2; + LONG prev_state = 0xdeadbeef; + NTSTATUS status; + UNICODE_STRING str; + OBJECT_ATTRIBUTES attr; + EVENT_BASIC_INFORMATION info; + + pRtlInitUnicodeString( &str, L"\\BaseNamedObjects\\testEvent" ); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + + status = pNtCreateEvent(&event, GENERIC_ALL, &attr, 2, 0); + ok( status == STATUS_INVALID_PARAMETER, "NtCreateEvent failed %08lx\n", status ); + + status = pNtCreateEvent(&event, GENERIC_ALL, &attr, NotificationEvent, 0); + ok( status == STATUS_SUCCESS, "NtCreateEvent failed %08lx\n", status ); + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryEvent(event, EventBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08lx\n", status ); + ok( info.EventType == NotificationEvent && info.EventState == 0, + "NtQueryEvent failed, expected 0 0, got %d %ld\n", info.EventType, info.EventState ); + pNtClose(event); + + status = pNtCreateEvent(&event, GENERIC_ALL, &attr, SynchronizationEvent, 0); + ok( status == STATUS_SUCCESS, "NtCreateEvent failed %08lx\n", status ); + + status = pNtPulseEvent(event, &prev_state); + ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08lx\n", status ); + ok( !prev_state, "prev_state = %lx\n", prev_state ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryEvent(event, EventBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08lx\n", status ); + ok( info.EventType == SynchronizationEvent && info.EventState == 0, + "NtQueryEvent failed, expected 1 0, got %d %ld\n", info.EventType, info.EventState ); + + status = pNtOpenEvent(&event2, GENERIC_ALL, &attr); + ok( status == STATUS_SUCCESS, "NtOpenEvent failed %08lx\n", status ); + + pNtClose(event); + event = event2; + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryEvent(event, EventBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08lx\n", status ); + ok( info.EventType == SynchronizationEvent && info.EventState == 0, + "NtQueryEvent failed, expected 1 0, got %d %ld\n", info.EventType, info.EventState ); + + status = pNtSetEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08lx\n", status ); + ok( !prev_state, "prev_state = %lx\n", prev_state ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryEvent(event, EventBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08lx\n", status ); + ok( info.EventType == SynchronizationEvent && info.EventState == 1, + "NtQueryEvent failed, expected 1 1, got %d %ld\n", info.EventType, info.EventState ); + + status = pNtSetEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08lx\n", status ); + ok( prev_state == 1, "prev_state = %lx\n", prev_state ); + + status = pNtResetEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08lx\n", status ); + ok( prev_state == 1, "prev_state = %lx\n", prev_state ); + + status = pNtResetEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08lx\n", status ); + ok( !prev_state, "prev_state = %lx\n", prev_state ); + + status = pNtPulseEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08lx\n", status ); + ok( !prev_state, "prev_state = %lx\n", prev_state ); + + status = pNtSetEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08lx\n", status ); + ok( !prev_state, "prev_state = %lx\n", prev_state ); + + status = pNtPulseEvent( event, &prev_state ); + ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08lx\n", status ); + ok( prev_state == 1, "prev_state = %lx\n", prev_state ); + + pNtClose(event); +} + +static const WCHAR keyed_nameW[] = L"\\BaseNamedObjects\\WineTestEvent"; + +static DWORD WINAPI keyed_event_thread( void *arg ) +{ + HANDLE handle; + NTSTATUS status; + LARGE_INTEGER timeout; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + ULONG_PTR i; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &str; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &str, keyed_nameW ); + + status = pNtOpenKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr ); + ok( !status, "NtOpenKeyedEvent failed %lx\n", status ); + + for (i = 0; i < 20; i++) + { + if (i & 1) + status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); + else + status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); + ok( status == STATUS_SUCCESS, "%Ii: failed %lx\n", i, status ); + Sleep( 20 - i ); + } + + status = pNtReleaseKeyedEvent( handle, (void *)0x1234, 0, NULL ); + ok( status == STATUS_SUCCESS, "NtReleaseKeyedEvent %lx\n", status ); + + timeout.QuadPart = -10000; + status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + + NtClose( handle ); + return 0; +} + +static void test_keyed_events(void) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + HANDLE handle, event, thread; + NTSTATUS status; + LARGE_INTEGER timeout; + ULONG_PTR i; + + if (!pNtCreateKeyedEvent) + { + win_skip( "Keyed events not supported\n" ); + return; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &str; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &str, keyed_nameW ); + + status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS | SYNCHRONIZE, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + + status = WaitForSingleObject( handle, 1000 ); + ok( status == 0, "WaitForSingleObject %lx\n", status ); + + timeout.QuadPart = -100000; + status = pNtWaitForKeyedEvent( handle, (void *)255, 0, &timeout ); + ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)255, 0, &timeout ); + ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %lx\n", status ); + + status = pNtWaitForKeyedEvent( handle, (void *)254, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)254, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + + status = pNtWaitForKeyedEvent( handle, NULL, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, NULL, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + + status = pNtWaitForKeyedEvent( NULL, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ + "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( NULL, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ + "NtReleaseKeyedEvent %lx\n", status ); + + status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); + ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); + ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %lx\n", status ); + + status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); + ok( status == STATUS_INVALID_HANDLE, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); + ok( status == STATUS_INVALID_HANDLE, "NtReleaseKeyedEvent %lx\n", status ); + + thread = CreateThread( NULL, 0, keyed_event_thread, 0, 0, NULL ); + for (i = 0; i < 20; i++) + { + if (i & 1) + status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); + else + status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); + ok( status == STATUS_SUCCESS, "%Ii: failed %lx\n", i, status ); + Sleep( i ); + } + status = pNtWaitForKeyedEvent( handle, (void *)0x1234, 0, &timeout ); + ok( status == STATUS_SUCCESS, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + + ok( WaitForSingleObject( thread, 30000 ) == 0, "wait failed\n" ); + + NtClose( handle ); + + /* test access rights */ + + status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAIT, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %lx\n", status ); + NtClose( handle ); + + status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAKE, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + NtClose( handle ); + + status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + status = WaitForSingleObject( handle, 1000 ); + ok( status == WAIT_FAILED && GetLastError() == ERROR_ACCESS_DENIED, + "WaitForSingleObject %lx err %lu\n", status, GetLastError() ); + status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + NtClose( handle ); + + /* GENERIC_READ gives wait access */ + status = pNtCreateKeyedEvent( &handle, GENERIC_READ, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %lx\n", status ); + NtClose( handle ); + + /* GENERIC_WRITE gives wake access */ + status = pNtCreateKeyedEvent( &handle, GENERIC_WRITE, &attr, 0 ); + ok( !status, "NtCreateKeyedEvent failed %lx\n", status ); + status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); + ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %lx\n", status ); + + /* it's not an event */ + status = pNtPulseEvent( handle, NULL ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtPulseEvent %lx\n", status ); + + status = pNtCreateEvent( &event, GENERIC_ALL, &attr, NotificationEvent, FALSE ); + ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH /* 7+ */, + "CreateEvent %lx\n", status ); + + NtClose( handle ); + + status = pNtCreateEvent( &event, GENERIC_ALL, &attr, NotificationEvent, FALSE ); + ok( status == 0, "CreateEvent %lx\n", status ); + status = pNtWaitForKeyedEvent( event, (void *)8, 0, &timeout ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtWaitForKeyedEvent %lx\n", status ); + status = pNtReleaseKeyedEvent( event, (void *)8, 0, &timeout ); + ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtReleaseKeyedEvent %lx\n", status ); + NtClose( event ); +} + +static DWORD WINAPI mutant_thread( void *arg ) +{ + MUTANT_BASIC_INFORMATION info; + NTSTATUS status; + HANDLE mutant; + DWORD ret; + + mutant = arg; + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08lx\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + /* abandon mutant */ + + return 0; +} + +static void test_mutant(void) +{ + MUTANT_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + HANDLE mutant; + HANDLE thread; + DWORD ret; + ULONG len; + LONG prev; + + pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\test_mutant"); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE); + ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08lx)\n", status ); + + /* bogus */ + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, + "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */ + "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status ); + status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_HANDLE, + "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08lx\n", status ); + + /* new */ + len = -1; + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + ok( len == sizeof(info), "got %lu\n", len ); + + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08lx\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == -1, "expected -1, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + prev = 0xdeadbeef; + status = pNtReleaseMutant(mutant, &prev); + ok( status == STATUS_SUCCESS, "NtReleaseMutant failed %08lx\n", status ); + ok( prev == -1, "NtReleaseMutant failed, expected -1, got %ld\n", prev ); + + prev = 0xdeadbeef; + status = pNtReleaseMutant(mutant, &prev); + ok( status == STATUS_SUCCESS, "NtReleaseMutant failed %08lx\n", status ); + ok( prev == 0, "NtReleaseMutant failed, expected 0, got %ld\n", prev ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == 1, "expected 1, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + /* abandoned */ + thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL ); + ret = WaitForSingleObject( thread, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08lx\n", ret ); + CloseHandle( thread ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == 1, "expected 0, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState ); + + ret = WaitForSingleObject( mutant, 1000 ); + ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08lx\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08lx\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %ld\n", info.CurrentCount ); + ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); + ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); + + NtClose( mutant ); +} + +static void test_semaphore(void) +{ + SEMAPHORE_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + HANDLE semaphore; + ULONG prev; + ULONG len; + DWORD ret; + + pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\test_semaphore"); + InitializeObjectAttributes(&attr, &str, 0, 0, NULL); + + status = pNtCreateSemaphore(&semaphore, GENERIC_ALL, &attr, 2, 1); + ok( status == STATUS_INVALID_PARAMETER, "Failed to create Semaphore(%08lx)\n", status ); + status = pNtCreateSemaphore(&semaphore, GENERIC_ALL, &attr, 1, 2); + ok( status == STATUS_SUCCESS, "Failed to create Semaphore(%08lx)\n", status ); + + /* bogus */ + status = pNtQuerySemaphore(semaphore, SemaphoreBasicInformation, &info, 0, NULL); + ok( status == STATUS_INFO_LENGTH_MISMATCH, + "Failed to NtQuerySemaphore, expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status ); + status = pNtQuerySemaphore(semaphore, 0x42, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_INFO_CLASS, + "Failed to NtQuerySemaphore, expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status ); + status = pNtQuerySemaphore((HANDLE)0xdeadbeef, SemaphoreBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_INVALID_HANDLE, + "Failed to NtQuerySemaphore, expected STATUS_INVALID_HANDLE, got %08lx\n", status ); + + len = -1; + memset(&info, 0xcc, sizeof(info)); + status = pNtQuerySemaphore(semaphore, SemaphoreBasicInformation, &info, sizeof(info), &len); + ok( status == STATUS_SUCCESS, "NtQuerySemaphore failed %08lx\n", status ); + ok( info.CurrentCount == 1, "expected 1, got %ld\n", info.CurrentCount ); + ok( info.MaximumCount == 2, "expected 2, got %ld\n", info.MaximumCount ); + ok( len == sizeof(info), "got %lu\n", len ); + + ret = WaitForSingleObject( semaphore, 1000 ); + ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08lx\n", ret ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQuerySemaphore(semaphore, SemaphoreBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQuerySemaphore failed %08lx\n", status ); + ok( info.CurrentCount == 0, "expected 0, got %ld\n", info.CurrentCount ); + ok( info.MaximumCount == 2, "expected 2, got %ld\n", info.MaximumCount ); + + prev = 0xdeadbeef; + status = pNtReleaseSemaphore(semaphore, 3, &prev); + ok( status == STATUS_SEMAPHORE_LIMIT_EXCEEDED, "NtReleaseSemaphore failed %08lx\n", status ); + ok( prev == 0xdeadbeef, "NtReleaseSemaphore failed, expected 0xdeadbeef, got %ld\n", prev ); + + prev = 0xdeadbeef; + status = pNtReleaseSemaphore(semaphore, 1, &prev); + ok( status == STATUS_SUCCESS, "NtReleaseSemaphore failed %08lx\n", status ); + ok( prev == 0, "NtReleaseSemaphore failed, expected 0, got %ld\n", prev ); + + prev = 0xdeadbeef; + status = pNtReleaseSemaphore(semaphore, 1, &prev); + ok( status == STATUS_SUCCESS, "NtReleaseSemaphore failed %08lx\n", status ); + ok( prev == 1, "NtReleaseSemaphore failed, expected 1, got %ld\n", prev ); + + prev = 0xdeadbeef; + status = pNtReleaseSemaphore(semaphore, 1, &prev); + ok( status == STATUS_SEMAPHORE_LIMIT_EXCEEDED, "NtReleaseSemaphore failed %08lx\n", status ); + ok( prev == 0xdeadbeef, "NtReleaseSemaphore failed, expected 0xdeadbeef, got %ld\n", prev ); + + memset(&info, 0xcc, sizeof(info)); + status = pNtQuerySemaphore(semaphore, SemaphoreBasicInformation, &info, sizeof(info), NULL); + ok( status == STATUS_SUCCESS, "NtQuerySemaphore failed %08lx\n", status ); + ok( info.CurrentCount == 2, "expected 2, got %ld\n", info.CurrentCount ); + ok( info.MaximumCount == 2, "expected 2, got %ld\n", info.MaximumCount ); + + NtClose( semaphore ); +} + +static void test_wait_on_address(void) +{ + SIZE_T size; + NTSTATUS status; + LARGE_INTEGER start, end, timeout; + DWORD elapsed; + LONG64 address, compare; + + if (!pRtlWaitOnAddress) + { + win_skip("RtlWaitOnAddress not supported, skipping test\n"); + return; + } + + if (0) /* crash on Windows */ + { + pRtlWaitOnAddress(&address, NULL, 8, NULL); + pRtlWaitOnAddress(NULL, &compare, 8, NULL); + pRtlWaitOnAddress(NULL, NULL, 8, NULL); + } + + /* don't crash */ + pRtlWakeAddressSingle(NULL); + pRtlWakeAddressAll(NULL); + + /* invalid values */ + address = 0; + compare = 0; + status = pRtlWaitOnAddress(&address, &compare, 5, NULL); + ok(status == STATUS_INVALID_PARAMETER, "got %lx\n", status); + + /* values match */ + address = 0; + compare = 0; + pNtQuerySystemTime(&start); + timeout.QuadPart = start.QuadPart + 100 * 10000; + status = pRtlWaitOnAddress(&address, &compare, 8, &timeout); + pNtQuerySystemTime(&end); + ok(status == STATUS_TIMEOUT, "got 0x%08lx\n", status); + elapsed = (end.QuadPart - start.QuadPart) / 10000; + ok(90 <= elapsed && elapsed <= 900, "timed out in %lu ms\n", elapsed); + ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); + ok(compare == 0, "got %s\n", wine_dbgstr_longlong(compare)); + + /* different address size */ + for (size = 1; size <= 4; size <<= 1) + { + compare = ~0; + compare <<= size * 8; + + pNtQuerySystemTime(&start); + timeout.QuadPart = -100 * 10000; + status = pRtlWaitOnAddress(&address, &compare, size, &timeout); + pNtQuerySystemTime(&end); + ok(status == STATUS_TIMEOUT, "got 0x%08lx\n", status); + elapsed = (end.QuadPart - start.QuadPart) / 10000; + ok(90 <= elapsed && elapsed <= 900, "timed out in %lu ms\n", elapsed); + + status = pRtlWaitOnAddress(&address, &compare, size << 1, &timeout); + ok(!status, "got 0x%08lx\n", status); + } + address = 0; + compare = 1; + status = pRtlWaitOnAddress(&address, &compare, 8, NULL); + ok(!status, "got 0x%08lx\n", status); + + /* no waiters */ + address = 0; + pRtlWakeAddressSingle(&address); + ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); + pRtlWakeAddressAll(&address); + ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); +} + +static HANDLE thread_ready, thread_done; + +static DWORD WINAPI resource_shared_thread(void *arg) +{ + RTL_RWLOCK *resource = arg; + BOOLEAN ret; + + ret = pRtlAcquireResourceShared(resource, TRUE); + ok(ret == TRUE, "got %u\n", ret); + + SetEvent(thread_ready); + ok(!WaitForSingleObject(thread_done, 1000), "wait failed\n"); + pRtlReleaseResource(resource); + return 0; +} + +static DWORD WINAPI resource_exclusive_thread(void *arg) +{ + RTL_RWLOCK *resource = arg; + BOOLEAN ret; + + ret = pRtlAcquireResourceExclusive(resource, TRUE); + ok(ret == TRUE, "got %u\n", ret); + + SetEvent(thread_ready); + ok(!WaitForSingleObject(thread_done, 1000), "wait failed\n"); + pRtlReleaseResource(resource); + return 0; +} + +static void test_resource(void) +{ + HANDLE thread, thread2; + RTL_RWLOCK resource; + BOOLEAN ret; + + pRtlInitializeResource(&resource); + thread_ready = CreateEventA(NULL, FALSE, FALSE, NULL); + thread_done = CreateEventA(NULL, FALSE, FALSE, NULL); + + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == FALSE, "got %u\n", ret); + pRtlReleaseResource(&resource); + pRtlReleaseResource(&resource); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + pRtlReleaseResource(&resource); + pRtlReleaseResource(&resource); + + /* Do not acquire the resource ourselves, but spawn a shared thread holding it. */ + + thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); + ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == FALSE, "got %u\n", ret); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + SetEvent(thread_done); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + /* Acquire the resource as exclusive, and then spawn a shared thread. */ + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); + ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + pRtlReleaseResource(&resource); + ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); + SetEvent(thread_done); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + /* Acquire the resource as shared, and then spawn an exclusive thread. */ + + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + thread = CreateThread(NULL, 0, resource_exclusive_thread, &resource, 0, NULL); + ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == FALSE, "got %u\n", ret); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + pRtlReleaseResource(&resource); + ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); + SetEvent(thread_done); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + /* Spawn a shared and then exclusive waiter. */ + thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); + ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); + thread2 = CreateThread(NULL, 0, resource_exclusive_thread, &resource, 0, NULL); + ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == FALSE, "got %u\n", ret); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + SetEvent(thread_done); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); + SetEvent(thread_done); + ok(!WaitForSingleObject(thread2, 1000), "wait failed\n"); + CloseHandle(thread2); + + ret = pRtlAcquireResourceExclusive(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + ret = pRtlAcquireResourceShared(&resource, FALSE); + ok(ret == TRUE, "got %u\n", ret); + pRtlReleaseResource(&resource); + + CloseHandle(thread_ready); + CloseHandle(thread_done); + pRtlDeleteResource(&resource); +} + +static DWORD WINAPI tid_alert_thread( void *arg ) +{ + NTSTATUS ret; + + ret = pNtAlertThreadByThreadId( arg ); + ok(!ret, "got %#lx\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, NULL ); + ok(ret == STATUS_ALERTED, "got %#lx\n", ret); + + return 0; +} + +static void test_tid_alert( char **argv ) +{ + LARGE_INTEGER timeout = {{0}}; + char cmdline[MAX_PATH]; + STARTUPINFOA si = {0}; + PROCESS_INFORMATION pi; + HANDLE thread; + NTSTATUS ret; + DWORD tid; + + if (!pNtWaitForAlertByThreadId) + { + win_skip("NtWaitForAlertByThreadId is not available\n"); + return; + } + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#lx\n", ret); + + ret = pNtAlertThreadByThreadId( 0 ); + ok(ret == STATUS_INVALID_CID, "got %#lx\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)0xdeadbeef ); + ok(ret == STATUS_INVALID_CID, "got %#lx\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)GetCurrentThreadId() ); + ok(!ret, "got %#lx\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)GetCurrentThreadId() ); + ok(!ret, "got %#lx\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_ALERTED, "got %#lx\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#lx\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x321, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#lx\n", ret); + + thread = CreateThread( NULL, 0, tid_alert_thread, (HANDLE)(DWORD_PTR)GetCurrentThreadId(), 0, &tid ); + timeout.QuadPart = -1000 * 10000; + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_ALERTED, "got %#lx\n", ret); + + ret = WaitForSingleObject( thread, 100 ); + ok(ret == WAIT_TIMEOUT, "got %ld\n", ret); + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid ); + ok(!ret, "got %#lx\n", ret); + + ret = WaitForSingleObject( thread, 1000 ); + ok(!ret, "got %ld\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid ); + ok(!ret, "got %#lx\n", ret); + + CloseHandle(thread); + + sprintf( cmdline, "%s %s subprocess", argv[0], argv[1] ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); + ok(ret, "failed to create process, error %lu\n", GetLastError()); + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)pi.dwThreadId ); + todo_wine ok(ret == STATUS_ACCESS_DENIED, "got %#lx\n", ret); + ok(!WaitForSingleObject( pi.hProcess, 1000 ), "wait failed\n"); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); +} + +struct test_completion_port_scheduling_param +{ + HANDLE ready, test_ready; + HANDLE port; + int index; +}; + +static DWORD WINAPI test_completion_port_scheduling_thread(void *param) +{ + struct test_completion_port_scheduling_param *p = param; + FILE_IO_COMPLETION_INFORMATION info; + OVERLAPPED_ENTRY overlapped_entry; + OVERLAPPED *overlapped; + IO_STATUS_BLOCK iosb; + ULONG_PTR key, value; + NTSTATUS status; + DWORD ret, err; + ULONG count; + BOOL bret; + + /* both threads are woken when comleption added. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + ret = WaitForSingleObject( p->port, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + + /* if a thread is waiting for completion which is added threads which wait on port handle are not woken. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + if (p->index) + { + bret = GetQueuedCompletionStatus( p->port, &count, &key, &overlapped, INFINITE ); + ok( bret, "got error %lu.\n", GetLastError() ); + } + else + { + ret = WaitForSingleObject( p->port, 100 ); + ok( ret == WAIT_TIMEOUT || broken( !ret ) /* before Win10 1607 */, "got %#lx.\n", ret ); + } + SetEvent( p->test_ready ); + + /* Two threads in GetQueuedCompletionStatus, the second is supposed to start first. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + bret = GetQueuedCompletionStatus( p->port, &count, &key, &overlapped, INFINITE ); + ok( bret, "got error %lu.\n", GetLastError() ); + ok( key == 3 + p->index || broken( p->index && key == 5 ) /* before Win10 */, "got %Iu, expected %u.\n", key, 3 + p->index ); + SetEvent( p->test_ready ); + + /* Port is being closed. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ret = WaitForSingleObject( p->port, INFINITE ); + if (ret == WAIT_FAILED) + skip( "Handle closed before wait started.\n" ); + else + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + + /* Port is being closed. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + status = NtRemoveIoCompletion( p->port, &key, &value, &iosb, NULL ); + if (status == STATUS_INVALID_HANDLE) + skip( "Handle closed before wait started.\n" ); + else + ok( status == STATUS_ABANDONED_WAIT_0, "got %#lx.\n", status ); + + /* Port is being closed. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + count = 0xdeadbeef; + status = NtRemoveIoCompletionEx( p->port, &info, 1, &count, NULL, FALSE ); + ok( count <= 1, "Got unexpected count %lu.\n", count ); + if (status == STATUS_INVALID_HANDLE) + skip( "Handle closed before wait started.\n" ); + else + ok( status == STATUS_ABANDONED_WAIT_0, "got %#lx.\n", status ); + + /* Port is being closed. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + bret = GetQueuedCompletionStatus( p->port, &count, &key, &overlapped, INFINITE ); + err = GetLastError(); + ok( !bret, "got %d.\n", bret ); + if (err == ERROR_INVALID_HANDLE) + skip( "Handle closed before wait started.\n" ); + else + ok( err == ERROR_ABANDONED_WAIT_0, "got error %#lx.\n", err ); + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x601) + /* Port is being closed. */ + ret = WaitForSingleObject( p->ready, INFINITE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + SetEvent( p->test_ready ); + bret = GetQueuedCompletionStatusEx( p->port, &overlapped_entry, 1, &count, INFINITE, TRUE ); + err = GetLastError(); + ok( !bret, "got %d.\n", bret ); + if (err == ERROR_INVALID_HANDLE) + skip( "Handle closed before wait started.\n" ); + else + ok( err == ERROR_ABANDONED_WAIT_0, "got error %#lx.\n", err ); +#endif + + return 0; +} + +static void test_completion_port_scheduling(void) +{ + struct test_completion_port_scheduling_param p[2]; + HANDLE threads[2], port; + OVERLAPPED *overlapped; + unsigned int i, j; + DWORD ret, count; + NTSTATUS status; + ULONG_PTR key; + BOOL bret; + + for (i = 0; i < 2; ++i) + { + p[i].index = 0; + p[i].ready = CreateEventA(NULL, FALSE, FALSE, NULL); + p[i].test_ready = CreateEventA(NULL, FALSE, FALSE, NULL); + threads[i] = CreateThread( NULL, 0, test_completion_port_scheduling_thread, &p[i], 0, NULL ); + ok( !!threads[i], "got error %lu.\n", GetLastError() ); + } + + status = NtCreateIoCompletion( &port, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( !status, "got %#lx.\n", status ); + /* Waking multiple threads directly waiting on port */ + for (i = 0; i < 2; ++i) + { + p[i].index = i; + p[i].port = port; + SetEvent( p[i].ready ); + } + PostQueuedCompletionStatus( port, 0, 1, NULL ); + for (i = 0; i < 2; ++i) WaitForSingleObject( p[i].test_ready, INFINITE ); + bret = GetQueuedCompletionStatus( port, &count, &key, &overlapped, INFINITE ); + ok( bret, "got error %lu.\n", GetLastError() ); + + /* One thread is waiting on port, another in GetQueuedCompletionStatus(). */ + SetEvent( p[1].ready ); + Sleep( 40 ); + SetEvent( p[0].ready ); + Sleep( 10 ); + PostQueuedCompletionStatus( port, 0, 2, NULL ); + for (i = 0; i < 2; ++i) WaitForSingleObject( p[i].test_ready, INFINITE ); + + /* Both threads are waiting in GetQueuedCompletionStatus, LIFO wake up order. */ + SetEvent( p[1].ready ); + Sleep( 40 ); + SetEvent( p[0].ready ); + Sleep( 20 ); + PostQueuedCompletionStatus( port, 0, 3, NULL ); + PostQueuedCompletionStatus( port, 0, 4, NULL ); + PostQueuedCompletionStatus( port, 0, 5, NULL ); + bret = GetQueuedCompletionStatus( p->port, &count, &key, &overlapped, INFINITE ); + ok( bret, "got error %lu.\n", GetLastError() ); + ok( key == 5 || broken( key == 4 ) /* before Win10 */, "got %Iu, expected 5.\n", key ); + + /* Close port handle while threads are waiting on it directly. */ + for (i = 0; i < 2; ++i) SetEvent( p[i].ready ); + Sleep( 20 ); + NtClose( port ); + for (i = 0; i < 2; ++i) WaitForSingleObject( p[i].test_ready, INFINITE ); + + /* Test signaling on port close. */ + for (i = 0; i < 4; ++i) + { + status = NtCreateIoCompletion( &port, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( !status, "got %#lx.\n", status ); + for (j = 0; j < 2; ++j) + { + p[j].port = port; + ret = SignalObjectAndWait( p[j].ready, p[j].test_ready, + INFINITE, FALSE ); + ok( ret == WAIT_OBJECT_0, "got %#lx.\n", ret ); + } + Sleep( 20 ); + status = NtClose( port ); + ok( !status, "got %#lx.\n", status ); + } + + WaitForMultipleObjects( 2, threads, TRUE, INFINITE ); + for (i = 0; i < 2; ++i) + { + CloseHandle( threads[i] ); + CloseHandle( p[i].ready ); + CloseHandle( p[i].test_ready ); + } +} + +START_TEST(sync) +{ + HMODULE module = GetModuleHandleA("ntdll.dll"); + char **argv; + int argc; + + argc = winetest_get_mainargs( &argv ); + + if (argc > 2) return; + + pNtAlertThreadByThreadId = (void *)GetProcAddress(module, "NtAlertThreadByThreadId"); + pNtClose = (void *)GetProcAddress(module, "NtClose"); + pNtCreateEvent = (void *)GetProcAddress(module, "NtCreateEvent"); + pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); + pNtCreateMutant = (void *)GetProcAddress(module, "NtCreateMutant"); + pNtCreateSemaphore = (void *)GetProcAddress(module, "NtCreateSemaphore"); + pNtOpenEvent = (void *)GetProcAddress(module, "NtOpenEvent"); + pNtOpenKeyedEvent = (void *)GetProcAddress(module, "NtOpenKeyedEvent"); + pNtPulseEvent = (void *)GetProcAddress(module, "NtPulseEvent"); + pNtQueryEvent = (void *)GetProcAddress(module, "NtQueryEvent"); + pNtQueryMutant = (void *)GetProcAddress(module, "NtQueryMutant"); + pNtQuerySemaphore = (void *)GetProcAddress(module, "NtQuerySemaphore"); + pNtQuerySystemTime = (void *)GetProcAddress(module, "NtQuerySystemTime"); + pNtReleaseKeyedEvent = (void *)GetProcAddress(module, "NtReleaseKeyedEvent"); + pNtReleaseMutant = (void *)GetProcAddress(module, "NtReleaseMutant"); + pNtReleaseSemaphore = (void *)GetProcAddress(module, "NtReleaseSemaphore"); + pNtResetEvent = (void *)GetProcAddress(module, "NtResetEvent"); + pNtSetEvent = (void *)GetProcAddress(module, "NtSetEvent"); + pNtWaitForAlertByThreadId = (void *)GetProcAddress(module, "NtWaitForAlertByThreadId"); + pNtWaitForKeyedEvent = (void *)GetProcAddress(module, "NtWaitForKeyedEvent"); + pRtlAcquireResourceExclusive = (void *)GetProcAddress(module, "RtlAcquireResourceExclusive"); + pRtlAcquireResourceShared = (void *)GetProcAddress(module, "RtlAcquireResourceShared"); + pRtlDeleteResource = (void *)GetProcAddress(module, "RtlDeleteResource"); + pRtlInitializeResource = (void *)GetProcAddress(module, "RtlInitializeResource"); + pRtlInitUnicodeString = (void *)GetProcAddress(module, "RtlInitUnicodeString"); + pRtlReleaseResource = (void *)GetProcAddress(module, "RtlReleaseResource"); + pRtlWaitOnAddress = (void *)GetProcAddress(module, "RtlWaitOnAddress"); + pRtlWakeAddressAll = (void *)GetProcAddress(module, "RtlWakeAddressAll"); + pRtlWakeAddressSingle = (void *)GetProcAddress(module, "RtlWakeAddressSingle"); + + test_wait_on_address(); + test_event(); + test_mutant(); + test_semaphore(); + test_keyed_events(); + test_resource(); + test_tid_alert( argv ); + test_completion_port_scheduling(); +} diff --git a/modules/rostests/winetests/ntdll/testlist.c b/modules/rostests/winetests/ntdll/testlist.c index 1c9edc99e86..1b5ff4c47ce 100644 --- a/modules/rostests/winetests/ntdll/testlist.c +++ b/modules/rostests/winetests/ntdll/testlist.c @@ -23,8 +23,13 @@ extern void func_rtl(void); extern void func_rtlbitmap(void); extern void func_rtlstr(void); extern void func_string(void); +extern void func_sync(void); +extern void func_thread(void); extern void func_threadpool(void); extern void func_time(void); +extern void func_unwind(void); +extern void func_virtual(void); +extern void func_wow64(void); const struct test winetest_testlist[] = { @@ -35,22 +40,24 @@ const struct test winetest_testlist[] = { "error", func_error }, { "exception", func_exception }, { "file", func_file }, -#ifdef _M_IX86 { "generated", func_generated }, -#endif { "info", func_info }, { "large_int", func_large_int }, { "om", func_om }, { "path", func_path }, { "pipe", func_pipe }, { "port", func_port }, - { "process", func_process }, { "reg", func_reg }, { "rtl", func_rtl }, { "rtlbitmap", func_rtlbitmap }, { "rtlstr", func_rtlstr }, { "string", func_string }, - { "threadpool", func_threadpool}, + { "sync", func_sync }, + { "thread", func_thread }, + { "threadpool", func_threadpool }, { "time", func_time }, + { "unwind", func_unwind }, + { "virtual", func_virtual }, + { "wow64", func_wow64 }, { 0, 0 } }; diff --git a/modules/rostests/winetests/ntdll/thread.c b/modules/rostests/winetests/ntdll/thread.c new file mode 100644 index 00000000000..a023e423bd1 --- /dev/null +++ b/modules/rostests/winetests/ntdll/thread.c @@ -0,0 +1,216 @@ +/* + * Unit test suite for ntdll thread functions + * + * Copyright 2021 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/test.h" + +static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, + HANDLE, PRTL_THREAD_START_ROUTINE, void *, + ULONG, ULONG_PTR, SIZE_T, SIZE_T, PS_ATTRIBUTE_LIST * ); +static int * (CDECL *p_errno)(void); + +static void init_function_pointers(void) +{ + HMODULE hntdll = GetModuleHandleA( "ntdll.dll" ); +#define GET_FUNC(name) p##name = (void *)GetProcAddress( hntdll, #name ); + GET_FUNC( NtCreateThreadEx ); + GET_FUNC( _errno ); +#undef GET_FUNC +} + +static void CALLBACK test_NtCreateThreadEx_proc(void *param) +{ +} + +static void test_dbg_hidden_thread_creation(void) +{ + RTL_USER_PROCESS_PARAMETERS *params; + PS_CREATE_INFO create_info; + PS_ATTRIBUTE_LIST ps_attr; + WCHAR path[MAX_PATH + 4]; + HANDLE process, thread; + UNICODE_STRING imageW; + BOOLEAN dbg_hidden; + NTSTATUS status; + + if (!pNtCreateThreadEx) + { + win_skip( "NtCreateThreadEx is not available.\n" ); + return; + } + + status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc, + NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, 0, 0, 0, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + dbg_hidden = 0xcc; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + ok( !dbg_hidden, "Got unexpected dbg_hidden %#x.\n", dbg_hidden ); + + status = NtResumeThread( thread, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + + status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc, + NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, + 0, 0, 0, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + dbg_hidden = 0xcc; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + ok( dbg_hidden == 1, "Got unexpected dbg_hidden %#x.\n", dbg_hidden ); + + status = NtResumeThread( thread, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + + lstrcpyW( path, L"\\??\\" ); + GetModuleFileNameW( NULL, path + 4, MAX_PATH ); + + RtlInitUnicodeString( &imageW, path ); + + memset( &ps_attr, 0, sizeof(ps_attr) ); + ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME; + ps_attr.Attributes[0].Size = lstrlenW(path) * sizeof(WCHAR); + ps_attr.Attributes[0].ValuePtr = path; + ps_attr.TotalLength = sizeof(ps_attr); + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x601) + status = RtlCreateProcessParametersEx( ¶ms, &imageW, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, PROCESS_PARAMS_FLAG_NORMALIZED ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + /* NtCreateUserProcess() may return STATUS_INVALID_PARAMETER with some uninitialized data in create_info. */ + memset( &create_info, 0, sizeof(create_info) ); + create_info.Size = sizeof(create_info); + + status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, + NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED + | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, params, + &create_info, &ps_attr ); + ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#lx.\n", status ); + status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, + NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, params, + &create_info, &ps_attr ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + status = NtTerminateProcess( process, 0 ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + CloseHandle( process ); + CloseHandle( thread ); +#endif +} + +struct unique_teb_thread_args +{ + TEB *teb; + HANDLE running_event; + HANDLE quit_event; +}; + +static void CALLBACK test_unique_teb_proc(void *param) +{ + struct unique_teb_thread_args *args = param; + args->teb = NtCurrentTeb(); + SetEvent( args->running_event ); + WaitForSingleObject( args->quit_event, INFINITE ); +} + +static void test_unique_teb(void) +{ + HANDLE threads[2], running_events[2]; + struct unique_teb_thread_args args1, args2; + NTSTATUS status; + + if (!pNtCreateThreadEx) + { + win_skip( "NtCreateThreadEx is not available.\n" ); + return; + } + + args1.running_event = running_events[0] = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( args1.running_event != NULL, "CreateEventW failed %lu.\n", GetLastError() ); + + args2.running_event = running_events[1] = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( args2.running_event != NULL, "CreateEventW failed %lu.\n", GetLastError() ); + + args1.quit_event = args2.quit_event = CreateEventW( NULL, TRUE, FALSE, NULL ); + ok( args1.quit_event != NULL, "CreateEventW failed %lu.\n", GetLastError() ); + + status = pNtCreateThreadEx( &threads[0], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_unique_teb_proc, + &args1, 0, 0, 0, 0, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + status = pNtCreateThreadEx( &threads[1], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_unique_teb_proc, + &args2, 0, 0, 0, 0, NULL ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + WaitForMultipleObjects( 2, running_events, TRUE, INFINITE ); + SetEvent( args1.quit_event ); + + WaitForMultipleObjects( 2, threads, TRUE, INFINITE ); + CloseHandle( threads[0] ); + CloseHandle( threads[1] ); + CloseHandle( args1.running_event ); + CloseHandle( args2.running_event ); + CloseHandle( args1.quit_event ); + + ok( NtCurrentTeb() != args1.teb, "Multiple threads have TEB %p.\n", args1.teb ); + ok( NtCurrentTeb() != args2.teb, "Multiple threads have TEB %p.\n", args2.teb ); + ok( args1.teb != args2.teb, "Multiple threads have TEB %p.\n", args1.teb ); +} + +static void test_errno(void) +{ + int val; + + if (!p_errno) + { + win_skip( "_errno not available\n" ); + return; + } + ok( NtCurrentTeb()->Peb->TlsBitmap->Buffer[0] & (1 << 16), "TLS entry 16 not allocated\n" ); + *p_errno() = 0xdead; + val = PtrToLong( TlsGetValue( 16 )); + ok( val == 0xdead, "wrong value %x\n", val ); + *p_errno() = 0xbeef; + val = PtrToLong( TlsGetValue( 16 )); + ok( val == 0xbeef, "wrong value %x\n", val ); +} + +START_TEST(thread) +{ + init_function_pointers(); + + test_dbg_hidden_thread_creation(); + test_unique_teb(); + test_errno(); +} diff --git a/modules/rostests/winetests/ntdll/threadpool.c b/modules/rostests/winetests/ntdll/threadpool.c index 88f4e55fa97..c00d4ea39c3 100644 --- a/modules/rostests/winetests/ntdll/threadpool.c +++ b/modules/rostests/winetests/ntdll/threadpool.c @@ -18,11 +18,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "ntdll_test.h" +#include -#ifdef __REACTOS__ -typedef void (CALLBACK *PTP_IO_CALLBACK)(PTP_CALLBACK_INSTANCE,void*,void*,IO_STATUS_BLOCK*,PTP_IO); -#endif +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/test.h" static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *); @@ -584,7 +587,7 @@ static void test_tp_simple(void) IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; -#ifndef __REACTOS__ +#if !defined(__REACTOS__) || _WIN32_WINNT >= _WIN32_WINNT_WIN7 TP_CALLBACK_ENVIRON_V3 environment3; #endif TP_CLEANUP_GROUP *group; @@ -622,8 +625,8 @@ static void test_tp_simple(void) result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); -#ifndef __REACTOS__ // Windows 7 /* test with environment version 3 */ +#if !defined(__REACTOS__) || _WIN32_WINNT >= _WIN32_WINNT_WIN7 memset(&environment3, 0, sizeof(environment3)); environment3.Version = 3; environment3.Pool = pool; @@ -2039,11 +2042,7 @@ static DWORD WINAPI io_wait_thread(void *arg) static void test_tp_io(void) { TP_CALLBACK_ENVIRON environment = {.Version = 1}; -#ifdef __REACTOS__ OVERLAPPED ovl = {0}, ovl2 = {0}; -#else - OVERLAPPED ovl = {}, ovl2 = {}; -#endif HANDLE client, server, thread; struct io_cb_ctx userdata; char in[1], in2[1]; @@ -2263,11 +2262,7 @@ static void CALLBACK kernel32_io_cb(TP_CALLBACK_INSTANCE *instance, void *userda static void test_kernel32_tp_io(void) { TP_CALLBACK_ENVIRON environment = {.Version = 1}; -#ifdef __REACTOS__ OVERLAPPED ovl = {0}, ovl2 = {0}; -#else - OVERLAPPED ovl = {}, ovl2 = {}; -#endif HANDLE client, server, thread; struct io_cb_ctx userdata; char in[1], in2[1]; diff --git a/modules/rostests/winetests/ntdll/time.c b/modules/rostests/winetests/ntdll/time.c index 92b674f03e8..f2ea90ab4ed 100644 --- a/modules/rostests/winetests/ntdll/time.c +++ b/modules/rostests/winetests/ntdll/time.c @@ -18,84 +18,16 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __REACTOS__ -#define NONAMELESSUNION -#endif -#include "ntdll_test.h" -#ifndef __REACTOS__ +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" #include "ddk/wdm.h" -#else /* FIXME: Inspect */ - -typedef struct _KSYSTEM_TIME { - ULONG LowPart; - LONG High1Time; - LONG High2Time; -} KSYSTEM_TIME, *PKSYSTEM_TIME; - -typedef enum _NT_PRODUCT_TYPE { - NtProductWinNt = 1, - NtProductLanManNt, - NtProductServer -} NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; - -#define PROCESSOR_FEATURE_MAX 64 - -typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE -{ - StandardDesign, - NEC98x86, - EndAlternatives -} ALTERNATIVE_ARCHITECTURE_TYPE; - -#define MAX_WOW64_SHARED_ENTRIES 16 - -typedef struct _KUSER_SHARED_DATA { - ULONG TickCountLowDeprecated; - ULONG TickCountMultiplier; - volatile KSYSTEM_TIME InterruptTime; - volatile KSYSTEM_TIME SystemTime; - volatile KSYSTEM_TIME TimeZoneBias; - USHORT ImageNumberLow; - USHORT ImageNumberHigh; - WCHAR NtSystemRoot[260]; - ULONG MaxStackTraceDepth; - ULONG CryptoExponent; - ULONG TimeZoneId; - ULONG LargePageMinimum; - ULONG Reserved2[7]; - NT_PRODUCT_TYPE NtProductType; - BOOLEAN ProductTypeIsValid; - ULONG NtMajorVersion; - ULONG NtMinorVersion; - BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; - ULONG Reserved1; - ULONG Reserved3; - volatile ULONG TimeSlip; - ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; - LARGE_INTEGER SystemExpirationDate; - ULONG SuiteMask; - BOOLEAN KdDebuggerEnabled; - UCHAR NXSupportPolicy; - volatile ULONG ActiveConsoleId; - volatile ULONG DismountCount; - ULONG ComPlusPackage; - ULONG LastSystemRITEventTickCount; - ULONG NumberOfPhysicalPages; - BOOLEAN SafeBootMode; - ULONG TraceLogging; - ULONGLONG TestRetInstruction; - ULONG SystemCall; - ULONG SystemCallReturn; - ULONGLONG SystemCallPad[3]; - union { - volatile KSYSTEM_TIME TickCount; - volatile ULONG64 TickCountQuad; - } DUMMYUNIONNAME; - ULONG Cookie; - ULONG Wow64SharedInformation[MAX_WOW64_SHARED_ENTRIES]; -} KSHARED_USER_DATA, *PKSHARED_USER_DATA; - -#endif /* !__REACTOS__ */ +#include "intrin.h" +#include "wine/test.h" #define TICKSPERSEC 10000000 #define TICKSPERMSEC 10000 @@ -104,9 +36,14 @@ typedef struct _KUSER_SHARED_DATA { static VOID (WINAPI *pRtlTimeToTimeFields)( const LARGE_INTEGER *liTime, PTIME_FIELDS TimeFields) ; static VOID (WINAPI *pRtlTimeFieldsToTime)( PTIME_FIELDS TimeFields, PLARGE_INTEGER Time) ; static NTSTATUS (WINAPI *pNtQueryPerformanceCounter)( LARGE_INTEGER *counter, LARGE_INTEGER *frequency ); +static NTSTATUS (WINAPI *pNtQuerySystemInformation)( SYSTEM_INFORMATION_CLASS class, + void *info, ULONG size, ULONG *ret_size ); static NTSTATUS (WINAPI *pRtlQueryTimeZoneInformation)( RTL_TIME_ZONE_INFORMATION *); static NTSTATUS (WINAPI *pRtlQueryDynamicTimeZoneInformation)( RTL_DYNAMIC_TIME_ZONE_INFORMATION *); -static ULONG (WINAPI *pNtGetTickCount)(void); +static BOOL (WINAPI *pRtlQueryUnbiasedInterruptTime)( ULONGLONG *time ); + +static BOOL (WINAPI *pRtlQueryPerformanceCounter)(LARGE_INTEGER*); +static BOOL (WINAPI *pRtlQueryPerformanceFrequency)(LARGE_INTEGER*); static const int MonthLengths[2][12] = { @@ -181,77 +118,365 @@ static void test_NtQueryPerformanceCounter(void) NTSTATUS status; status = pNtQueryPerformanceCounter(NULL, NULL); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); status = pNtQueryPerformanceCounter(NULL, &frequency); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); status = pNtQueryPerformanceCounter(&counter, (void *)0xdeadbee0); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); status = pNtQueryPerformanceCounter((void *)0xdeadbee0, &frequency); - ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08x\n", status); + ok(status == STATUS_ACCESS_VIOLATION, "expected STATUS_ACCESS_VIOLATION, got %08lx\n", status); status = pNtQueryPerformanceCounter(&counter, NULL); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); status = pNtQueryPerformanceCounter(&counter, &frequency); - ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); +} + +#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arm64ec__) + +struct hypervisor_shared_data +{ + UINT64 unknown; + UINT64 QpcMultiplier; + UINT64 QpcBias; +}; + +/* 128-bit multiply a by b and return the high 64 bits, same as __umulh */ +static UINT64 multiply_tsc(UINT64 a, UINT64 b) +{ + UINT64 ah = a >> 32, al = (UINT32)a, bh = b >> 32, bl = (UINT32)b, m; + m = (ah * bl) + (bh * al) + ((al * bl) >> 32); + return (ah * bh) + (m >> 32); +} + +static void test_RtlQueryPerformanceCounter(void) +{ + struct hypervisor_shared_data *hsd; + KSHARED_USER_DATA *usd = (void *)0x7ffe0000; + LARGE_INTEGER frequency, counter; + NTSTATUS status; + UINT64 tsc0, tsc1; + ULONG len; + BOOL ret; + + if (!pRtlQueryPerformanceCounter || !pRtlQueryPerformanceFrequency) + { + win_skip( "RtlQueryPerformanceCounter/Frequency not available, skipping tests\n" ); + return; + } + + if (!(usd->QpcBypassEnabled & SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED)) + { + todo_wine win_skip("QpcBypassEnabled is not set, skipping tests\n"); + return; + } + + if ((usd->QpcBypassEnabled & SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_HV_PAGE)) + { + ok( usd->QpcBypassEnabled == (SHARED_GLOBAL_FLAGS_QPC_BYPASS_ENABLED|SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_HV_PAGE|SHARED_GLOBAL_FLAGS_QPC_BYPASS_USE_RDTSCP), + "unexpected QpcBypassEnabled %x, expected 0x83\n", usd->QpcBypassEnabled ); + ok( usd->QpcFrequency == 10000000, "unexpected QpcFrequency %I64d, expected 10000000\n", usd->QpcFrequency ); + ok( !usd->QpcShift, "unexpected QpcShift %d, expected 0\n", usd->QpcShift ); + + hsd = NULL; + status = pNtQuerySystemInformation( SystemHypervisorSharedPageInformation, &hsd, sizeof(void *), &len ); + ok( !status, "NtQuerySystemInformation returned %lx\n", status ); + ok( len == sizeof(void *), "unexpected SystemHypervisorSharedPageInformation length %lu\n", len ); + ok( !!hsd, "unexpected SystemHypervisorSharedPageInformation address %p\n", hsd ); + + tsc0 = __rdtsc(); + ret = pRtlQueryPerformanceCounter( &counter ); + tsc1 = __rdtsc(); + ok( ret, "RtlQueryPerformanceCounter failed\n" ); + + tsc0 = multiply_tsc(tsc0, hsd->QpcMultiplier) + hsd->QpcBias + usd->QpcBias; + tsc1 = multiply_tsc(tsc1, hsd->QpcMultiplier) + hsd->QpcBias + usd->QpcBias; + + ok( tsc0 <= counter.QuadPart, "rdtscp %I64d and RtlQueryPerformanceCounter %I64d are out of order\n", tsc0, counter.QuadPart ); + ok( counter.QuadPart <= tsc1, "RtlQueryPerformanceCounter %I64d and rdtscp %I64d are out of order\n", counter.QuadPart, tsc1 ); + } + else + { + ok( usd->QpcShift == 10, "unexpected QpcShift %d, expected 10\n", usd->QpcShift ); + + tsc0 = __rdtsc(); + ret = pRtlQueryPerformanceCounter( &counter ); + tsc1 = __rdtsc(); + ok( ret, "RtlQueryPerformanceCounter failed\n" ); + + tsc0 += usd->QpcBias; + tsc0 >>= usd->QpcShift; + tsc1 += usd->QpcBias; + tsc1 >>= usd->QpcShift; + + ok( tsc0 <= counter.QuadPart, "rdtscp %I64d and RtlQueryPerformanceCounter %I64d are out of order\n", tsc0, counter.QuadPart ); + ok( counter.QuadPart <= tsc1, "RtlQueryPerformanceCounter %I64d and rdtscp %I64d are out of order\n", counter.QuadPart, tsc1 ); + } + + ret = pRtlQueryPerformanceFrequency( &frequency ); + ok( ret, "RtlQueryPerformanceFrequency failed\n" ); + ok( frequency.QuadPart == usd->QpcFrequency, + "RtlQueryPerformanceFrequency returned %I64d, expected USD QpcFrequency %I64d\n", + frequency.QuadPart, usd->QpcFrequency ); +} +#endif + +#define TIMER_LEEWAY 10 +#define CHECK_CURRENT_TIMER(expected) \ + do { \ + ok(status == STATUS_SUCCESS, "NtSetTimerResolution failed %lx\n", status); \ + ok(cur2 == (expected) || broken(abs((int)((expected) - cur2)) <= TIMER_LEEWAY), "expected new timer resolution %lu, got %lu\n", (expected), cur2); \ + set = cur2; \ + min2 = min + 20000; \ + cur2 = min2 + 1; \ + max2 = cur2 + 1; \ + status = NtQueryTimerResolution(&min2, &max2, &cur2); \ + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed %lx\n", status); \ + ok(min2 == min, "NtQueryTimerResolution() expected min=%lu, got %lu\n", min, min2); \ + ok(max2 == max, "NtQueryTimerResolution() expected max=%lu, got %lu\n", max, max2); \ + ok(cur2 == set, "NtQueryTimerResolution() expected timer resolution %lu, got %lu\n", set, cur2); \ + } while (0) + +static void test_TimerResolution(void) +{ + ULONG min, max, cur, min2, max2, cur2, set; + NTSTATUS status; + + status = NtQueryTimerResolution(NULL, &max, &cur); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(NULL,,) success\n"); + + status = NtQueryTimerResolution(&min, NULL, &cur); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,NULL,) success\n"); + + status = NtQueryTimerResolution(&min, &max, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "NtQueryTimerResolution(,,NULL) success\n"); + + min = 212121; + cur = min + 1; + max = cur + 1; + status = NtQueryTimerResolution(&min, &max, &cur); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%lx)\n", status); + ok(min == 156250 /* 1/64s HPET */ || min == 156001 /* RTC */, + "unexpected minimum timer resolution %lu\n", min); + ok(0 < max, "invalid maximum timer resolution, should be 0 < %lu\n", max); + ok(max <= cur || broken(max - TIMER_LEEWAY <= cur), "invalid timer resolutions, should be %lu <= %lu\n", max, cur); + ok(cur <= min || broken(cur <= min + TIMER_LEEWAY), "invalid timer resolutions, should be %lu <= %lu\n", cur, min); + + status = NtSetTimerResolution(0, FALSE, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution(,,NULL) success\n"); + + /* Nothing happens if that pointer is not good */ + status = NtSetTimerResolution(cur - 1, TRUE, NULL); + ok(status == STATUS_ACCESS_VIOLATION, "NtSetTimerResolution() failed %lx\n", status); + + min2 = min + 10000; + cur2 = min2 + 1; + max2 = cur2 + 1; + status = NtQueryTimerResolution(&min2, &max2, &cur2); + ok(status == STATUS_SUCCESS, "NtQueryTimerResolution() failed (%lx)\n", status); + ok(min2 == min, "NtQueryTimerResolution() expected min=%lu, got %lu\n", min, min2); + ok(max2 == max, "NtQueryTimerResolution() expected max=%lu, got %lu\n", max, max2); + ok(cur2 == cur, "NtQueryTimerResolution() expected timer resolution %lu, got %lu\n", cur, cur2); + + /* 'fails' until the first valid timer resolution request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %lx\n", status); + /* and returns the current timer resolution */ + ok(cur2 == cur, "expected requested timer resolution %lu, got %lu\n", cur, cur2); + + + cur2 = 7654321; + status = NtSetTimerResolution(max - 1, TRUE, &cur2); + CHECK_CURRENT_TIMER(max); + + /* Rescinds our timer resolution request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %lx\n", status); + /* -> the timer resolution was reset to its initial value */ + ok(cur2 == cur, "expected requested timer resolution %lu, got %lu\n", min, cur2); + + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + ok(status == STATUS_TIMER_RESOLUTION_NOT_SET, "NtSetTimerResolution() failed %lx\n", status); + ok(cur2 == cur, "expected requested timer resolution %lu, got %lu\n", cur, cur2); + + cur2 = 7654321; + status = NtSetTimerResolution(min + 1, TRUE, &cur2); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %lx\n", status); + /* This works because: + * - Either cur is the minimum (15.6 ms) resolution already, i.e. the + * closest valid value 'set' is rounded to. + * - Or some other application requested a higher timer resolution, cur, + * and any attempt to lower the resolution has no effect until that + * request is rescinded (hopefully after this test is done). + */ + CHECK_CURRENT_TIMER(cur); + + /* The requested resolution may (win7) or may not be rounded */ + cur2 = 7654321; + set = max < cur ? cur - 1 : max; + status = NtSetTimerResolution(set, TRUE, &cur2); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %lx\n", status); + ok(cur2 <= set || broken(cur2 <= set + TIMER_LEEWAY), "expected new timer resolution %lu <= %lu\n", cur2, set); + trace("timer resolution: %lu(max) <= %lu(cur) <= %lu(prev) <= %lu(min)\n", max, cur2, cur, min); + + cur2 = 7654321; + status = NtSetTimerResolution(cur + 1, TRUE, &cur2); + CHECK_CURRENT_TIMER(cur); /* see min + 1 test */ + + /* Cleanup by rescinding the last request */ + cur2 = 7654321; + status = NtSetTimerResolution(0, FALSE, &cur2); + ok(status == STATUS_SUCCESS, "NtSetTimerResolution() failed %lx\n", status); + ok(cur2 == cur, "expected requested timer resolution %lu, got %lu\n", set, cur2); } static void test_RtlQueryTimeZoneInformation(void) { - RTL_DYNAMIC_TIME_ZONE_INFORMATION tzinfo; + RTL_DYNAMIC_TIME_ZONE_INFORMATION tzinfo, tzinfo2; NTSTATUS status; + ULONG len; /* test RtlQueryTimeZoneInformation returns an indirect string, e.g. @tzres.dll,-32 (Vista or later) */ if (!pRtlQueryTimeZoneInformation || !pRtlQueryDynamicTimeZoneInformation) { - win_skip("Time zone name tests requires Vista or later\n"); + win_skip("Time zone name tests require Vista or later\n"); return; } - memset(&tzinfo, 0, sizeof(tzinfo)); + memset(&tzinfo, 0xcc, sizeof(tzinfo)); status = pRtlQueryDynamicTimeZoneInformation(&tzinfo); ok(status == STATUS_SUCCESS, - "RtlQueryDynamicTimeZoneInformation failed, got %08x\n", status); - todo_wine ok(tzinfo.StandardName[0] == '@', + "RtlQueryDynamicTimeZoneInformation failed, got %08lx\n", status); + ok(tzinfo.StandardName[0] == '@' || + broken(tzinfo.StandardName[0]), /* some win10 2004 */ "standard time zone name isn't an indirect string, got %s\n", wine_dbgstr_w(tzinfo.StandardName)); - todo_wine ok(tzinfo.DaylightName[0] == '@', + ok(tzinfo.DaylightName[0] == '@' || + broken(tzinfo.DaylightName[0]), /* some win10 2004 */ "daylight time zone name isn't an indirect string, got %s\n", wine_dbgstr_w(tzinfo.DaylightName)); - memset(&tzinfo, 0, sizeof(tzinfo)); + memset(&tzinfo2, 0xcc, sizeof(tzinfo2)); + status = pNtQuerySystemInformation( SystemDynamicTimeZoneInformation, &tzinfo2, sizeof(tzinfo2), &len ); + ok( !status, "NtQuerySystemInformation failed %lx\n", status ); + ok( len == sizeof(tzinfo2), "wrong len %lu\n", len ); + ok( !memcmp( &tzinfo, &tzinfo2, sizeof(tzinfo2) ), "tz data is different\n" ); + + memset(&tzinfo, 0xcc, sizeof(tzinfo)); status = pRtlQueryTimeZoneInformation((RTL_TIME_ZONE_INFORMATION *)&tzinfo); ok(status == STATUS_SUCCESS, - "RtlQueryTimeZoneInformation failed, got %08x\n", status); - todo_wine ok(tzinfo.StandardName[0] == '@', + "RtlQueryTimeZoneInformation failed, got %08lx\n", status); + ok(tzinfo.StandardName[0] == '@' || + broken(tzinfo.StandardName[0]), /* some win10 2004 */ "standard time zone name isn't an indirect string, got %s\n", wine_dbgstr_w(tzinfo.StandardName)); - todo_wine ok(tzinfo.DaylightName[0] == '@', + ok(tzinfo.DaylightName[0] == '@' || + broken(tzinfo.DaylightName[0]), /* some win10 2004 */ "daylight time zone name isn't an indirect string, got %s\n", wine_dbgstr_w(tzinfo.DaylightName)); + + memset(&tzinfo, 0xcc, sizeof(tzinfo)); + status = pRtlQueryTimeZoneInformation((RTL_TIME_ZONE_INFORMATION *)&tzinfo); + ok(status == STATUS_SUCCESS, + "RtlQueryTimeZoneInformation failed, got %08lx\n", status); + ok(tzinfo.StandardName[0] == '@' || + broken(tzinfo.StandardName[0]), /* some win10 2004 */ + "standard time zone name isn't an indirect string, got %s\n", + wine_dbgstr_w(tzinfo.StandardName)); + ok(tzinfo.DaylightName[0] == '@' || + broken(tzinfo.DaylightName[0]), /* some win10 2004 */ + "daylight time zone name isn't an indirect string, got %s\n", + wine_dbgstr_w(tzinfo.DaylightName)); + + memset(&tzinfo2, 0xcc, sizeof(tzinfo2)); + status = pNtQuerySystemInformation( SystemCurrentTimeZoneInformation, &tzinfo2, + sizeof(RTL_TIME_ZONE_INFORMATION), &len ); + ok( !status, "NtQuerySystemInformation failed %lx\n", status ); + ok( len == sizeof(RTL_TIME_ZONE_INFORMATION), "wrong len %lu\n", len ); + ok( !memcmp( &tzinfo, &tzinfo2, sizeof(RTL_TIME_ZONE_INFORMATION) ), "tz data is different\n" ); } -static void test_NtGetTickCount(void) +static ULONGLONG read_ksystem_time(volatile KSYSTEM_TIME *time) +{ + ULONGLONG high, low; + do + { + high = time->High1Time; + low = time->LowPart; + } + while (high != time->High2Time); + return high << 32 | low; +} + +static void test_user_shared_data_time(void) { -#ifndef _WIN64 KSHARED_USER_DATA *user_shared_data = (void *)0x7ffe0000; - LONG diff; - int i; + SYSTEM_TIMEOFDAY_INFORMATION timeofday; + ULONGLONG t1, t2, t3; + NTSTATUS status; + int i = 0; - if (!pNtGetTickCount) + i = 0; + do { - win_skip("NtGetTickCount is not available\n"); - return; + t1 = GetTickCount(); + if (user_shared_data->NtMajorVersion <= 5 && user_shared_data->NtMinorVersion <= 1) + t2 = (DWORD)((*(volatile ULONG*)&user_shared_data->TickCountLowDeprecated * (ULONG64)user_shared_data->TickCountMultiplier) >> 24); + else + t2 = (DWORD)((read_ksystem_time(&user_shared_data->TickCount) * user_shared_data->TickCountMultiplier) >> 24); + t3 = GetTickCount(); + } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */ + + ok(t1 <= t2, "USD TickCount / GetTickCount are out of order: %s %s\n", + wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2)); + ok(t2 <= t3, "USD TickCount / GetTickCount are out of order: %s %s\n", + wine_dbgstr_longlong(t2), wine_dbgstr_longlong(t3)); + + i = 0; + do + { + LARGE_INTEGER system_time; + NtQuerySystemTime(&system_time); + t1 = system_time.QuadPart; + t2 = read_ksystem_time(&user_shared_data->SystemTime); + NtQuerySystemTime(&system_time); + t3 = system_time.QuadPart; + } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */ + + /* FIXME: not always in order, but should be close */ + todo_wine_if(t1 > t2 && t1 - t2 < 50 * TICKSPERMSEC) + ok(t1 <= t2, "USD SystemTime / NtQuerySystemTime are out of order %s %s\n", + wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2)); + ok(t2 <= t3, "USD SystemTime / NtQuerySystemTime are out of order %s %s\n", + wine_dbgstr_longlong(t2), wine_dbgstr_longlong(t3)); + + if (!pRtlQueryUnbiasedInterruptTime) + win_skip("skipping RtlQueryUnbiasedInterruptTime tests\n"); + else + { + i = 0; + do + { + pRtlQueryUnbiasedInterruptTime(&t1); + t2 = read_ksystem_time(&user_shared_data->InterruptTime) - user_shared_data->InterruptTimeBias; + pRtlQueryUnbiasedInterruptTime(&t3); + } while(t3 < t1 && i++ < 1); /* allow for wrap, but only once */ + + ok(t1 <= t2, "USD InterruptTime / RtlQueryUnbiasedInterruptTime are out of order %s %s\n", + wine_dbgstr_longlong(t1), wine_dbgstr_longlong(t2)); + ok(t2 <= t3, "USD InterruptTime / RtlQueryUnbiasedInterruptTime are out of order %s %s\n", + wine_dbgstr_longlong(t2), wine_dbgstr_longlong(t3)); } - for (i = 0; i < 5; ++i) - { - diff = (user_shared_data->TickCountQuad * user_shared_data->TickCountMultiplier) >> 24; - diff = pNtGetTickCount() - diff; - ok(diff < 32, "NtGetTickCount - TickCountQuad too high, expected < 32 got %d\n", diff); - Sleep(50); - } -#endif + t1 = read_ksystem_time(&user_shared_data->TimeZoneBias); + status = NtQuerySystemInformation(SystemTimeOfDayInformation, &timeofday, sizeof(timeofday), NULL); + ok(!status, "failed to query time of day, status %#lx\n", status); + ok(timeofday.TimeZoneBias.QuadPart == t1, "got USD bias %I64u, ntdll bias %I64u\n", + t1, timeofday.TimeZoneBias.QuadPart); } START_TEST(time) @@ -260,17 +485,24 @@ START_TEST(time) pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields"); pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime"); pNtQueryPerformanceCounter = (void *)GetProcAddress(mod, "NtQueryPerformanceCounter"); - pNtGetTickCount = (void *)GetProcAddress(mod,"NtGetTickCount"); + pNtQuerySystemInformation = (void *)GetProcAddress(mod, "NtQuerySystemInformation"); pRtlQueryTimeZoneInformation = (void *)GetProcAddress(mod, "RtlQueryTimeZoneInformation"); pRtlQueryDynamicTimeZoneInformation = (void *)GetProcAddress(mod, "RtlQueryDynamicTimeZoneInformation"); + pRtlQueryUnbiasedInterruptTime = (void *)GetProcAddress(mod, "RtlQueryUnbiasedInterruptTime"); + pRtlQueryPerformanceCounter = (void *)GetProcAddress(mod, "RtlQueryPerformanceCounter"); + pRtlQueryPerformanceFrequency = (void *)GetProcAddress(mod, "RtlQueryPerformanceFrequency"); if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime) test_pRtlTimeToTimeFields(); else win_skip("Required time conversion functions are not available\n"); test_NtQueryPerformanceCounter(); - test_NtGetTickCount(); test_RtlQueryTimeZoneInformation(); + test_user_shared_data_time(); +#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arm64ec__) + test_RtlQueryPerformanceCounter(); +#endif + test_TimerResolution(); } diff --git a/modules/rostests/winetests/ntdll/unwind.c b/modules/rostests/winetests/ntdll/unwind.c new file mode 100644 index 00000000000..20610afc6e3 --- /dev/null +++ b/modules/rostests/winetests/ntdll/unwind.c @@ -0,0 +1,3459 @@ +/* + * Unit test suite for exception unwinding + * + * Copyright 2009, 2024 Alexandre Julliard + * Copyright 2020, 2021 Martin Storsjö + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "winreg.h" +#include "winnt.h" +#include "winternl.h" +#include "rtlsupportapi.h" +#include "wine/test.h" + +#ifndef __i386__ + +static void *code_mem; +static HMODULE ntdll; + +static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG_PTR, ULONG_PTR*, UNWIND_HISTORY_TABLE*); +static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionTable)(ULONG_PTR, ULONG_PTR*, ULONG*); +static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); +static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); +static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); +static DWORD (WINAPI *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR); +static void (WINAPI *pRtlGrowFunctionTable)(void*, DWORD); +static void (WINAPI *pRtlDeleteGrowableFunctionTable)(void*); +static NTSTATUS (WINAPI *pRtlVirtualUnwind2)(ULONG,ULONG_PTR,ULONG_PTR,RUNTIME_FUNCTION*,CONTEXT*,BOOLEAN*,void**,ULONG_PTR*,KNONVOLATILE_CONTEXT_POINTERS*,ULONG_PTR*,ULONG_PTR*,PEXCEPTION_ROUTINE*,ULONG); +static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG); + +#ifdef __arm__ + +#define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff) +#define UWOP_THREEBYTES(x) (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff) +#define UWOP_FOURBYTES(x) (((x) >> 24) & 0xff), (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff) + +#define UWOP_ALLOC_SMALL(size) (0x00 | (size/4)) /* Max 0x7f * 4 */ +#define UWOP_SAVE_REGSW(regmask) UWOP_TWOBYTES((0x80 << 8) | (regmask)) +#define UWOP_SET_FP(reg) (0xC0 | reg) +#define UWOP_SAVE_RANGE_4_7_LR(reg,lr) (0xD0 | (reg - 4) | ((lr) ? 0x04 : 0)) +#define UWOP_SAVE_RANGE_4_11_LR(reg,lr)(0xD8 | (reg - 8) | ((lr) ? 0x04 : 0)) +#define UWOP_SAVE_D8_RANGE(reg) (0xE0 | (reg - 8)) +#define UWOP_ALLOC_MEDIUMW(size) UWOP_TWOBYTES((0xE8 << 8) | (size/4)) /* Max 0x3ff * 4 */ +#define UWOP_SAVE_REGS(regmask) UWOP_TWOBYTES((0xEC << 8) | ((regmask) & 0xFF) | (((regmask) & (1<results[i].regs); + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + if (test->unwind_info) + { + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size ); + runtime_func.BeginAddress = code_offset; + if (test->unwind_size) + runtime_func.UnwindData = unwind_offset; + else + memcpy(&runtime_func.UnwindData, test->unwind_info, 4); + } + + for (i = 0; i < test->nb_results; i++) + { + memset( &ctx_ptr, 0, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + memset( &unset_reg64, 0x55, sizeof(unset_reg64) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 4; + + context.Sp = (ULONG_PTR)fake_stack; + context.Lr = (ULONG_PTR)ORIG_LR; + context.R11 = (ULONG_PTR)fake_stack + test->results[i].fp_offset; + orig_fp = context.R11; + orig_pc = (ULONG_PTR)code_mem + code_offset + test->results[i].pc_offset; + + trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i, + (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp ); + + if (test->results[i].handler == -2) orig_pc = context.Lr; + + if (pRtlVirtualUnwind2) + { + CONTEXT new_context = context; + + handler = (void *)0xdeadbeef; + data = (void *)0xdeadbeef; + frame = 0xdeadbeef; + status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc, + test->unwind_info ? &runtime_func : NULL, &new_context, + NULL, &data, &frame, &ctx_ptr, NULL, NULL, &handler, 0 ); + if (test->results[i].handler > 0) + { + ok( !status, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( (char *)handler == (char *)code_mem + 0x200, + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + } + else if (test->results[i].handler < -1) + { + ok( status == STATUS_BAD_FUNCTION_TABLE, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( handler == (void *)0xdeadbeef, "handler set to %p\n", handler ); + ok( data == (void *)0xdeadbeef, "handler data set to %p\n", data ); + } + else + { + ok( !status, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( handler == NULL, "handler %p instead of NULL\n", handler ); + ok( data == NULL, "handler data set to %p\n", data ); + } + } + + data = (void *)0xdeadbeef; + frame = 0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc, + test->unwind_info ? &runtime_func : NULL, + &context, &data, &frame, &ctx_ptr ); + if (test->results[i].handler > 0) + { + ok( (char *)handler == (char *)code_mem + 0x200, + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + } + else + { + ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler ); + ok( data == (test->results[i].handler < -1 ? (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p/%p\n", testnum, i, data, + (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) ); + } + + ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n", + testnum, i, (void *)context.Pc, (void*)test->results[i].pc ); + ok( frame == (test->results[i].frame_offset ? (ULONG)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %x/%x\n", + testnum, i, (int)((char *)frame - (char *)(test->results[i].frame_offset ? fake_stack : NULL)), test->results[i].frame ); + + for (j = 0; j < 47; j++) + { + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (j >= 4 && j <= 11 && (&ctx_ptr.R4)[j - 4]) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", + testnum, i, reg_names_arm[j], (&context.R0)[j] ); + if (k < nb_regs) + ok( (&context.R0)[j] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] ); + } + else if (j == lr && ctx_ptr.Lr) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", + testnum, i, reg_names_arm[j], context.Lr ); + if (k < nb_regs) + ok( context.Lr == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] ); + } + else if (j == sp) + { + if (k < nb_regs) + ok( context.Sp == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_arm[j], (void *)context.Sp, (int)test->results[i].regs[k][1] ); + else if (test->results[i].frame == 0xdeadbeef) + ok( (void *)context.Sp == fake_stack, "%u/%u: wrong sp %p/%p\n", + testnum, i, (void *)context.Sp, fake_stack); + else + ok( context.Sp == frame, "%u/%u: wrong sp %p/%p\n", + testnum, i, (void *)context.Sp, (void *)frame); + } + else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8]) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n", + testnum, i, reg_names_arm[j], context.D[j - d0] ); + if (k < nb_regs) + ok( context.D[j - d0] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %llx/%llx\n", + testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] ); + } + else if (k < nb_regs) + { + if (j <= r12) + ok( (&context.R0)[j] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_arm[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] ); + else if (j == lr) + ok( context.Lr == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_arm[j], (void *)context.Lr, (int)test->results[i].regs[k][1] ); + else + ok( context.D[j - d0] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %llx/%llx\n", + testnum, i, reg_names_arm[j], context.D[j - d0], test->results[i].regs[k][1] ); + } + else + { + ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names_arm[j] ); + if (j == lr) + ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n", + testnum, i, (void *)context.Lr ); + else if (j == r11) + ok( context.R11 == orig_fp, "%u/%u: register fp wrong %p/unset\n", + testnum, i, (void *)context.R11 ); + else if (j < d0) + ok( (&context.R0)[j] == unset_reg, + "%u/%u: register %s wrong %p/unset\n", + testnum, i, reg_names_arm[j], (void *)(&context.R0)[j]); + else + ok( context.D[j - d0] == unset_reg64, + "%u/%u: register %s wrong %llx/unset\n", + testnum, i, reg_names_arm[j], context.D[j - d0]); + } + } + } +} + +#define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff) + +static void test_virtual_unwind_arm(void) +{ + + static const BYTE function_0[] = + { + 0x70, 0xb5, /* 00: push {r4-r6, lr} */ + 0x88, 0xb0, /* 02: sub sp, sp, #32 */ + 0x2d, 0xed, 0x06, 0x8b, /* 04: vpush {d8-d10} */ + 0x00, 0xbf, /* 08: nop */ + 0x2d, 0xed, 0x06, 0x3b, /* 0a: vpush {d3-d5} */ + 0xaf, 0x3f, 0x00, 0x80, /* 0e: nop.w */ + 0x6d, 0xed, 0x06, 0x1b, /* 12: vpush {d17-d19} */ + 0x2d, 0xe9, 0x00, 0x15, /* 16: push.w {r8, r10, r12} */ + 0xeb, 0x46, /* 1a: mov r11, sp */ + 0x00, 0xbf, /* 1c: nop */ + 0xbd, 0xec, 0x06, 0x8b, /* 1e: vpop {d8-d10} */ + 0xdd, 0x46, /* 22: mov sp, r11 */ + 0x08, 0xb0, /* 24: add sp, sp, #32 */ + 0x70, 0xbd, /* 26: pop {r4-r6, pc} */ + }; + + static const DWORD unwind_info_0_header = + (sizeof(function_0)/2) | /* function length */ + (1 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (1 << 23) | /* epilog */ + (5 << 28); /* codes, (sizeof(unwind_info_0)-headers+3)/4 */ + static const DWORD unwind_info_0_epilog0 = + (15 << 0) | /* offset = 0x1e / 2 = 15 */ + (0xE << 20) | /* condition, 0xE = always */ + (13 << 24); /* index, byte offset to epilog opcodes */ + + static const BYTE unwind_info_0[] = + { + DW(unwind_info_0_header), + DW(unwind_info_0_epilog0), + + UWOP_SET_FP(11), /* mov r11, sp */ + UWOP_SAVE_REGSW((1<0 seems to get incorrect handling of the epilogue */ + { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {d8,0x400000000}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, +#endif + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_10[] = + { + 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */ + 0xeb, 0x46, /* 04: mov r11, sp */ + 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */ + 0x84, 0xb0, /* 0a: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0c: nop */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */ + 0xbd, 0xe8, 0x00, 0x48, /* 14: pop.w {r11, lr} */ + 0x70, 0x47, /* 18: bx lr */ + }; + + static const DWORD unwind_info_10_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_10)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) }; + + static const struct results_arm results_10[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, +#if 0 + /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */ + { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, +#endif + { 0x18, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_11[] = + { + 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */ + 0xeb, 0x46, /* 04: mov r11, sp */ + 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */ + 0x84, 0xb0, /* 0a: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0c: nop */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */ + 0xbd, 0xe8, 0x00, 0x88, /* 14: pop.w {r11, pc} */ + }; + + static const DWORD unwind_info_11_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_11)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) }; + + static const struct results_arm results_11[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + }; + + static const BYTE function_12[] = + { + 0x2d, 0xed, 0x0e, 0x8b, /* 00: vpush {d8-d14} */ + 0x84, 0xb0, /* 04: sub sp, sp, #16 */ + 0x00, 0xbf, /* 06: nop */ + 0x04, 0xb0, /* 08: add sp, sp, #16 */ + 0xbd, 0xec, 0x0e, 0x8b, /* 0a: vpop {d8-d14} */ + 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */ + }; + + static const DWORD unwind_info_12_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_12)/2 << 2) | /* FunctionLength */ + (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) }; + + static const struct results_arm results_12[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }}, + { 0x06, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }}, + { 0x0a, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_13[] = + { + 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */ + 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */ + 0x85, 0xb0, /* 08: sub sp, sp, #20 */ + 0x00, 0xbf, /* 0a: nop */ + 0x05, 0xb0, /* 0c: add sp, sp, #20 */ + 0x2d, 0xe8, 0xf0, 0x8f, /* 0e: pop.w {r4-r11, lr} */ + }; + + static const DWORD unwind_info_13_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_13)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) }; + + static const struct results_arm results_13[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0e, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + }; + + static const BYTE function_14[] = + { + 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */ + 0x85, 0xb0, /* 04: sub sp, sp, #20 */ + 0x00, 0xbf, /* 06: nop */ + 0x05, 0xb0, /* 08: add sp, sp, #20 */ + 0x2d, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11, lr} */ + }; + + static const DWORD unwind_info_14_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_14)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_14[] = { DW(unwind_info_14_packed) }; + + static const struct results_arm results_14[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x06, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + }; + + static const BYTE function_15[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb5, /* 02: push {r4,lr} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */ + 0x10, 0xb5, /* 0e: pop {r4} */ + 0x5d, 0xf8, 0x14, 0xfb, /* 10: ldr pc, [sp], #20 */ + }; + + static const DWORD unwind_info_15_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_15)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_15[] = { DW(unwind_info_15_packed) }; + + static const struct results_arm results_15[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0e, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }}, + }; + + static const BYTE function_16[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x2d, 0xe9, 0x00, 0x48, /* 02: push.w {r11,lr} */ + 0xeb, 0x46, /* 06: mov r11, sp */ + 0x00, 0xbf, /* 08: nop */ + 0xbd, 0xe8, 0x10, 0x40, /* 0a: pop.w {r11,lr} */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0x00, 0xf0, 0x00, 0xb8, /* 10: b tailcall */ + }; + + static const DWORD unwind_info_16_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_16)/2 << 2) | /* FunctionLength */ + (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_16[] = { DW(unwind_info_16_packed) }; + + static const struct results_arm results_16[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x10, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_17[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb4, /* 02: push {r4} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */ + 0x10, 0xbc, /* 0e: pop {r4} */ + 0x04, 0xb0, /* 10: add sp, sp, #16 */ + 0x70, 0x47, /* 12: bx lr */ + }; + + static const DWORD unwind_info_17_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_17)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_17[] = { DW(unwind_info_17_packed) }; + + static const struct results_arm results_17[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x10, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_18[] = + { + 0x08, 0xb5, /* 00: push {r3,lr} */ + 0x00, 0xbf, /* 02: nop */ + 0x08, 0xbd, /* 04: pop {r3,pc} */ + }; + + static const DWORD unwind_info_18_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_18)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fcu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_18[] = { DW(unwind_info_18_packed) }; + + static const struct results_arm results_18[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }}, + }; + + static const BYTE function_19[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x14, 0xb4, /* 02: push {r0-r4} */ + 0x00, 0xbf, /* 04: nop */ + 0x1f, 0xbc, /* 06: pop {r0-r4} */ + 0x04, 0xb0, /* 08: add sp, sp, #16 */ + 0x70, 0x47, /* 0a: bx lr */ + }; + + static const DWORD unwind_info_19_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_19)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3ffu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) }; + + static const struct results_arm results_19[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_20[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x14, 0xb4, /* 02: push {r0-r4} */ + 0x00, 0xbf, /* 04: nop */ + 0x04, 0xb0, /* 06: add sp, sp, #16 */ + 0x10, 0xbc, /* 08: pop {r4} */ + 0x04, 0xb0, /* 0a: add sp, sp, #16 */ + 0x70, 0x47, /* 0c: bx lr */ + }; + + static const DWORD unwind_info_20_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_20)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3f7u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) }; + + static const struct results_arm results_20[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_21[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb4, /* 02: push {r4} */ + 0x84, 0xb0, /* 04: sub sp, sp, #16 */ + 0x00, 0xbf, /* 06: nop */ + 0x1f, 0xbc, /* 08: pop {r0-r4} */ + 0x04, 0xb0, /* 0a: add sp, sp, #16 */ + 0x70, 0x47, /* 0c: bx lr */ + }; + + static const DWORD unwind_info_21_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_21)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fbu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_21[] = { DW(unwind_info_21_packed) }; + + static const struct results_arm results_21[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_22[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 04: add sp, sp, #512 */ + 0x10, 0xb5, /* 08: pop {r4} */ + 0x5d, 0xf8, 0x14, 0xfb, /* 0a: ldr pc, [sp], #20 */ + }; + + static const DWORD unwind_info_22_packed = + (2 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_22)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_22[] = { DW(unwind_info_22_packed) }; + + static const struct results_arm results_22[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x02, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x04, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }}, + }; + + static const BYTE function_23[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb5, /* 02: push {r4,lr} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x00, 0xbf, /* 0a: nop */ + }; + + static const DWORD unwind_info_23_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_23)/2 << 2) | /* FunctionLength */ + (3 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_23[] = { DW(unwind_info_23_packed) }; + + static const struct results_arm results_23[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + }; + + static const BYTE function_24[] = + { + 0x2d, 0xe9, 0xfc, 0x48, /* 00: push.w {r2-r7,r11,lr} */ + 0x0d, 0xf1, 0x18, 0x0b, /* 04: add r11, sp, #24 */ + 0x00, 0xbf, /* 08: nop */ + 0x02, 0xb0, /* 0a: add sp, sp, #8 */ + 0xbd, 0xe8, 0x10, 0x48, /* 0c: pop.w {r4-r7,r11,pc} */ + }; + + static const DWORD unwind_info_24_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_24)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0x3f5u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_24[] = { DW(unwind_info_24_packed) }; + + static const struct results_arm results_24[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + }; + + static const BYTE function_25[] = + { + 0x2d, 0xe9, 0xf0, 0x48, /* 00: push.w {r4-r7,r11,lr} */ + 0x0d, 0xf1, 0x10, 0x0b, /* 04: add r11, sp, #16 */ + 0x82, 0xb0, /* 08: sub sp, sp, #8 */ + 0x00, 0xbf, /* 0a: nop */ + 0xbd, 0xe8, 0xfc, 0x48, /* 0c: pop.w {r2-r7,r11,pc} */ + }; + + static const DWORD unwind_info_25_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_25)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0x3f9u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_25[] = { DW(unwind_info_25_packed) }; + + static const struct results_arm results_25[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + }; + + static const BYTE function_26[] = + { + 0x2d, 0xe9, 0x10, 0x08, /* 00: push.w {r4, r11} */ + 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */ + 0x84, 0xb0, /* 08: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0a: nop */ + 0x04, 0xb0, /* 0c: add sp, sp, #16 */ + 0xbd, 0xe8, 0x10, 0x08, /* 0e: pop.w {r4, r11} */ + 0x70, 0x47, /* 12: bx lr */ + }; + + /* C=1, L=0 is disallowed by doc */ + static const DWORD unwind_info_26_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_26)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_26[] = { DW(unwind_info_26_packed) }; + + static const struct results_arm results_26[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_27[] = + { + 0x0e, 0xb4, /* 00: push {r1-r3} */ + 0x00, 0xbf, /* 02: nop */ + 0x03, 0xb0, /* 04: add sp, sp, #12 */ + 0x70, 0x47, /* 06: bx lr */ + }; + + static const DWORD unwind_info_27_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_27)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3f6u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_27[] = { DW(unwind_info_27_packed) }; + + static const struct results_arm results_27[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_28[] = + { + 0x0e, 0xb4, /* 00: push {r1-r3} */ + 0x00, 0xbf, /* 02: nop */ + 0x03, 0xb0, /* 04: add sp, sp, #12 */ + 0x70, 0x47, /* 06: bx lr */ + }; + + static const DWORD unwind_info_28_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_28)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fau << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_28[] = { DW(unwind_info_28_packed) }; + + static const struct results_arm results_28[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_29[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + }; + + static const DWORD unwind_info_29_header = + (sizeof(function_29)/2) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (0 << 23) | /* epilog */ + (1 << 28); /* codes, (sizeof(unwind_info_29)-headers+3)/4 */ + + static const BYTE unwind_info_29[] = + { + DW(unwind_info_29_header), + UWOP_MSFT_OP_CONTEXT, + UWOP_END, + }; + + static const struct results_arm results_29[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, 0x40, 0x38, FALSE, { {r0,0x04}, {r1,0x08}, {r2,0x0c}, {r3,0x10}, {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {r12,0x34}, {sp,0x38}, {lr,0x3c}, + {d0,0x5400000050}, {d1,0x5c00000058}, {d2,0x6400000060}, {d3,0x6c00000068}, {d4,0x7400000070}, {d5,0x7c00000078}, {d6,0x8400000080}, {d7,0x8c00000088}, + {d8,0x9400000090}, {d9,0x9c00000098}, {d10,0xa4000000a0}, {d11,0xac000000a8}, {d12,0xb4000000b0}, {d13,0xbc000000b8}, {d14,0xc4000000c0}, {d15,0xcc000000c8}, + {d16,0xd4000000d0}, {d17,0xdc000000d8}, {d18,0xe4000000e0}, {d19,0xec000000e8}, {d20,0xf4000000f0}, {d21,0xfc000000f8}, {d22,0x10400000100}, {d23,0x10c00000108}, + {d24,0x11400000110}, {d25,0x11c00000118}, {d26,0x12400000120}, {d27,0x12c00000128}, {d28,0x13400000130}, {d29,0x13c00000138}, {d30,0x14400000140}, {d31,0x14c00000148} }}, + }; + + static const BYTE function_30[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + 0x00, 0xbf, /* 04: nop */ + 0x00, 0xbf, /* 06: nop */ + }; + + static const DWORD unwind_info_30_header = + (sizeof(function_30)/2) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (0 << 23) | /* epilog */ + (2 << 28); /* codes, (sizeof(unwind_info_30)-headers+3)/4 */ + + static const BYTE unwind_info_30[] = + { + DW(unwind_info_30_header), + UWOP_ALLOC_SMALL(12), /* sub sp, sp, #12 */ + UWOP_SAVE_REGS((1<> 8) & 0xff), ((x) & 0xff) + +#define UWOP_ALLOC_SMALL(size) (0x00 | (size/16)) +#define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8)) +#define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8)) +#define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1)) +#define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16)) +#define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1)) +#define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8)) +#define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1)) +#define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16) +#define UWOP_SET_FP 0xE1 +#define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8)) +#define UWOP_NOP 0xE3 +#define UWOP_END 0xE4 +#define UWOP_END_C 0xE5 +#define UWOP_SAVE_NEXT 0xE6 +#define UWOP_SAVE_ANY_REG(reg,offset) 0xE7,(reg),(offset) +#define UWOP_TRAP_FRAME 0xE8 +#define UWOP_MACHINE_FRAME 0xE9 +#define UWOP_CONTEXT 0xEA +#define UWOP_EC_CONTEXT 0xEB +#define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC + +struct results_arm64 +{ + int pc_offset; /* pc offset from code start */ + int fp_offset; /* fp offset from stack pointer */ + int handler; /* expect handler to be set? */ + ULONG_PTR pc; /* expected final pc value */ + ULONG_PTR frame; /* expected frame return value */ + int frame_offset; /* whether the frame return value is an offset or an absolute value */ + ULONG_PTR regs[48][2]; /* expected values for registers */ +}; + +struct unwind_test_arm64 +{ + const BYTE *function; + size_t function_size; + const BYTE *unwind_info; + size_t unwind_size; + const struct results_arm64 *results; + unsigned int nb_results; + int unwound_clear; + int last_set_reg_ptr; + int stack_value_index; + ULONG64 stack_value; +}; + +enum regs_arm64 +{ + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, lr, sp, + d0, d1, d2, d3, d4, d5, d6, d7, + d8, d9, d10, d11, d12, d13, d14, d15 +}; + +static const char * const reg_names_arm64[48] = +{ + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp", + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", +}; + +#define ORIG_LR 0xCCCCCCCC + +static void call_virtual_unwind_arm64( void *code_mem, int testnum, const struct unwind_test_arm64 *test ) +{ + static const int code_offset = 1024; + static const int unwind_offset = 2048; + void *data; +#ifdef __x86_64__ + ARM64EC_NT_CONTEXT context, new_context; +#else + ARM64_NT_CONTEXT context, new_context; +#endif + PEXCEPTION_ROUTINE handler; + ARM64_RUNTIME_FUNCTION runtime_func; + KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; + UINT i, j, k; + NTSTATUS status; + ULONG64 fake_stack[256]; + ULONG64 frame, orig_pc, orig_fp, unset_reg, sp_offset = 0, regval, *regptr; + static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs); + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + if (test->unwind_info) + { + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size ); + runtime_func.BeginAddress = code_offset; + if (test->unwind_size) + runtime_func.UnwindData = unwind_offset; + else + memcpy(&runtime_func.UnwindData, test->unwind_info, 4); + } + + for (i = 0; i < test->nb_results; i++) + { +#ifdef __x86_64__ + if (test->results[i].handler == -2) continue; /* skip invalid leaf function test */ +#endif + winetest_push_context( "%u/%u", testnum, i ); + memset( &ctx_ptr, 0x55, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 8; + if (test->stack_value_index != -1) fake_stack[test->stack_value_index] = test->stack_value; + + context.Sp = (ULONG_PTR)fake_stack; + context.Lr = (ULONG_PTR)ORIG_LR; + context.Fp = (ULONG_PTR)fake_stack + test->results[i].fp_offset; + context.ContextFlags = 0xcccc; + if (test->unwound_clear) context.ContextFlags |= CONTEXT_ARM64_UNWOUND_TO_CALL; + + orig_fp = context.Fp; + orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset; + + trace( "pc=%p (%02x) fp=%p sp=%p\n", (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp ); + + if (test->results[i].handler == -2) orig_pc = context.Lr; + + if (pRtlVirtualUnwind2) + { + new_context = context; + handler = (void *)0xdeadbeef; + data = (void *)0xdeadbeef; + frame = 0xdeadbeef; + status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG_PTR)code_mem, orig_pc, + test->unwind_info ? (RUNTIME_FUNCTION *)&runtime_func : NULL, + (CONTEXT *)&new_context, NULL, &data, + &frame, &ctx_ptr, NULL, NULL, &handler, 0 ); + if (test->results[i].handler > 0) + { + ok( !status, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( (char *)handler == (char *)code_mem + 0x200, + "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "wrong handler data %lx\n", *(DWORD *)data ); + } + else if (test->results[i].handler < -1) + { + ok( status == STATUS_BAD_FUNCTION_TABLE, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( handler == (void *)0xdeadbeef, "handler set to %p\n", handler ); + ok( data == (void *)0xdeadbeef, "handler data set to %p\n", data ); + } + else + { + ok( !status, "RtlVirtualUnwind2 failed %lx\n", status ); + ok( handler == NULL, "handler %p instead of NULL\n", handler ); + ok( data == NULL, "handler data set to %p\n", data ); + } + } + + data = (void *)0xdeadbeef; + frame = 0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_pc, + test->unwind_info ? (RUNTIME_FUNCTION *)&runtime_func : NULL, + (CONTEXT *)&context, &data, &frame, &ctx_ptr ); + if (test->results[i].handler > 0) + { + ok( (char *)handler == (char *)code_mem + 0x200, + "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "wrong handler data %lx\n", *(DWORD *)data ); + } + else + { + ok( handler == NULL, "handler %p instead of NULL\n", handler ); + ok( data == (test->results[i].handler < -1 ? (void *)0xdeadbeef : NULL), + "handler data set to %p/%p\n", data, + (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) ); + } + + ok( context.Pc == test->results[i].pc, "wrong pc %p/%p\n", + (void *)context.Pc, (void*)test->results[i].pc ); + ok( frame == (test->results[i].frame_offset ? (ULONG64)fake_stack : 0) + test->results[i].frame, "wrong frame %p/%p\n", + (void *)frame, (char *)(test->results[i].frame_offset ? fake_stack : NULL) + test->results[i].frame ); + if (test->results[i].handler == -2) /* invalid leaf function */ + { + ok( context.ContextFlags == 0xcccc, "wrong flags %lx\n", context.ContextFlags ); + ok( context.Sp == (ULONG_PTR)fake_stack, "wrong sp %p/%p\n", (void *)context.Sp, fake_stack); + } + else + { + if (!test->unwound_clear || i < test->unwound_clear) + ok( context.ContextFlags == (0xcccc | CONTEXT_ARM64_UNWOUND_TO_CALL), + "wrong flags %lx\n", context.ContextFlags ); + else + ok( context.ContextFlags == 0xcccc, + "wrong flags %lx\n", context.ContextFlags ); + + sp_offset = 0; + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + break; + if (test->results[i].regs[k][0] == sp) { + /* If sp is part of the registers list, treat it as an offset + * between the returned frame pointer and the sp register. */ + sp_offset = test->results[i].regs[k][1]; + break; + } + } + ok( frame - sp_offset == context.Sp, "wrong sp %p/%p\n", + (void *)(frame - sp_offset), (void *)context.Sp); + } + +#ifdef __x86_64__ + for (j = 0; j < sizeof(ctx_ptr)/sizeof(void*); j++) + ok( ((void **)&ctx_ptr)[j] == (void *)unset_reg, + "ctx_ptr %u set to %p\n", j, ((void **)&ctx_ptr)[j] ); +#endif + + for (j = 0; j < 48; j++) + { + switch (j) + { +#define GET(i) case i: regval = context.X##i; break + GET(0); GET(1); GET(2); GET(3); GET(4); GET(5); GET(6); GET(7); + GET(8); GET(9); GET(10); GET(11); GET(12); + GET(15); GET(19); GET(20); GET(21); GET(22); GET(25); GET(26); GET(27); +#ifdef __x86_64__ + case x13: case x14: continue; + case x16: regval = context.X16_0 | ((DWORD64)context.X16_1 << 16) | ((DWORD64)context.X16_2 << 32) | ((DWORD64)context.X16_3 << 48); break; + case x17: regval = context.X17_0 | ((DWORD64)context.X17_1 << 16) | ((DWORD64)context.X17_2 << 32) | ((DWORD64)context.X17_3 << 48); break; + case x18: case x23: case x24: case x28: continue; +#else + GET(13); GET(14); GET(16); GET(17); GET(18); GET(23); GET(24); GET(28); +#endif +#undef GET + case x29: regval = context.Fp; break; + case lr: regval = context.Lr; break; + case sp: continue; /* Handling sp separately above */ + default: regval = context.V[j - d0].Low; break; + } + + regptr = NULL; +#ifndef __x86_64__ + if (j >= 19 && j <= 30) regptr = (&ctx_ptr.X19)[j - 19]; + else if (j >= d8 && j <= d15) regptr = (&ctx_ptr.D8)[j - d8]; +#endif + + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (k < nb_regs) + { + ok( regval == test->results[i].regs[k][1], + "register %s wrong %I64x/%I64x\n", reg_names_arm64[j], regval, test->results[i].regs[k][1] ); + if (regptr) + { + if (test->last_set_reg_ptr && j > test->last_set_reg_ptr && j <= 30) + ok( regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] ); + else + { + ok( regptr != (void *)unset_reg, "register %s should have pointer set\n", reg_names_arm64[j] ); + if (regptr != (void *)unset_reg) + ok( *regptr == regval, "register %s should have reg pointer to %I64x / %I64x\n", + reg_names_arm64[j], *regptr, regval ); + } + } + } + else + { + ok( k == nb_regs, "register %s should be set\n", reg_names_arm64[j] ); + ok( !regptr || regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names_arm64[j] ); + if (j == lr) + ok( context.Lr == ORIG_LR, "register lr wrong %I64x/unset\n", context.Lr ); + else if (j == x29) + ok( context.Fp == orig_fp, "register fp wrong %I64x/unset\n", context.Fp ); + else + ok( regval == unset_reg, "register %s wrong %I64x/unset\n", reg_names_arm64[j], regval); + } + } + winetest_pop_context(); + } +} + +#ifndef __REACTOS__ +#define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff) + +static void test_virtual_unwind_arm64(void) +{ + static const BYTE function_0[] = + { + 0xff, 0x83, 0x00, 0xd1, /* 00: sub sp, sp, #32 */ + 0xf3, 0x53, 0x01, 0xa9, /* 04: stp x19, x20, [sp, #16] */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xf3, 0x53, 0x41, 0xa9, /* 0c: ldp x19, x20, [sp, #16] */ + 0xff, 0x83, 0x00, 0x91, /* 10: add sp, sp, #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_0_header = + (sizeof(function_0)/4) | /* function length */ + (1 << 20) | /* X */ + (0 << 21) | /* E */ + (1 << 22) | /* epilog */ + (2 << 27); /* codes */ + static const DWORD unwind_info_0_epilog0 = + (3 << 0) | /* offset */ + (4 << 22); /* index */ + + static const BYTE unwind_info_0[] = + { + DW(unwind_info_0_header), + DW(unwind_info_0_epilog0), + + UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */ + UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */ + UWOP_END, + + UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */ + UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */ + UWOP_END, + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_arm64 results_0[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 1, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + + static const BYTE function_1[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xfe, 0x0b, 0x00, 0xf9, /* 04: str x30, [sp, #16] */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */ + 0xfe, 0x0b, 0x40, 0xf9, /* 14: ldr x30, [sp, #16] */ + 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_1_packed = + (1 << 0) | /* Flag */ + (sizeof(function_1)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_1[] = { DW(unwind_info_1_packed) }; + + static const struct results_arm64 results_1[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_2[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_2_header = + (sizeof(function_2)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_2[] = + { + DW(unwind_info_2_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_MACHINE_FRAME, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + /* Partial prologues with the custom frame opcodes (machine frame, + * context) behave like there's one less instruction to skip, because the + * custom frame is set up externally without an explicit instruction. */ + static const struct results_arm64 results_2[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }}, + { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + { 0x10, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + }; + + static const BYTE function_3[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_3_header = + (sizeof(function_3)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_3[] = + { + DW(unwind_info_3_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_CONTEXT, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results_arm64 results_3[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0 , 0x0108, 0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }}, + { 0x08, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + { 0x10, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + }; + + static const BYTE function_4[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */ + 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */ + 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */ + 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */ + 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */ + 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */ + 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */ + 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */ + 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */ + 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */ + 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */ + 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */ + 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */ + }; + + static const DWORD unwind_info_4_header = + (sizeof(function_4)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (8 << 27); /* codes */ + + static const BYTE unwind_info_4[] = + { + DW(unwind_info_4_header), + + UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */ + UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */ + UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */ + UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */ + UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */ + UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */ + UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */ + UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */ + UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */ + UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */ + UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */ + UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */ + UWOP_SET_FP, /* 0c: mov x29, sp */ + UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */ + UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */ + UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results_arm64 results_4[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x28, 0x00, 0, 0x0018, 0x10220, FALSE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {x29, 0x10}, {-1,-1} }}, + { 0x2c, 0x00, 0, 0x0038, 0x10240, FALSE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x20}, {x24, 0x28}, {x25, 0x30}, {lr, 0x38}, {x29, 0x30}, {-1,-1} }}, + { 0x30, 0x00, 0, 0x0058, 0x10260, FALSE, { {x19, 0x60}, {x20, 0x68}, {x21, 0x70}, {x22, 0x78}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {lr, 0x58}, {x29, 0x50}, {-1,-1} }}, + { 0x34, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x38, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, + { 0x3c, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {-1,-1} }}, + { 0x40, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {d13, 0x08}, {-1,-1} }}, + { 0x44, 0x20, 0, 0x00a8, 0x102b0, FALSE, { {x19, 0xb0}, {x20, 0xb8}, {x21, 0xc0}, {x22, 0xc8}, {x23, 0x90}, {x24, 0x98}, {x25, 0xa0}, {lr, 0xa8}, {x29, 0xa0}, {d8, 0x30}, {d9, 0x38}, {d10, 0x40}, {d11, 0x48}, {d12, 0x10}, {d13, 0x18}, {-1,-1} }}, + }; + + static const BYTE function_5[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */ + 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */ + 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */ + 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */ + 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */ + 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */ + 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */ + 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */ + }; + + static const DWORD unwind_info_5_header = + (sizeof(function_5)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (4 << 27); /* codes */ + + static const BYTE unwind_info_5[] = + { + DW(unwind_info_5_header), + + UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */ + UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */ + UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */ + UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */ + UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */ + UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */ + UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */ + UWOP_END, + UWOP_NOP /* padding */ + }; + + /* Windows seems to only save one register for UWOP_SAVE_NEXT for + * float registers, contrary to what the documentation says. The tests + * for those cases are commented out; they succeed in wine but fail + * on native windows. */ + static const struct results_arm64 results_5[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {-1,-1} }}, + { 0x18, 0x00, 0, 0x38, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {x29, 0x30}, {lr, 0x38}, {-1,-1} }}, + { 0x1c, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, +#if 0 + { 0x20, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {-1,-1} }}, + { 0x28, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {d14, 0x30}, {d15, 0x38}, {-1,-1} }}, +#endif + }; + + static const BYTE function_6[] = + { + 0xf3, 0x53, 0xbd, 0xa9, /* 00: stp x19, x20, [sp, #-48]! */ + 0xf5, 0x0b, 0x00, 0xf9, /* 04: str x21, [sp, #16] */ + 0xe8, 0xa7, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #24] */ + 0xea, 0x17, 0x00, 0xfd, /* 0c: str d10, [sp, #40] */ + 0xff, 0x03, 0x00, 0xd1, /* 10: sub sp, sp, #0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */ + 0xff, 0x03, 0x00, 0x91, /* 18: add sp, sp, #0 */ + 0xea, 0x17, 0x40, 0xfd, /* 1c: ldr d10, [sp, #40] */ + 0xe8, 0xa7, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #24] */ + 0xf5, 0x0b, 0x40, 0xf9, /* 24: ldr x21, [sp, #16] */ + 0xf3, 0x53, 0xc3, 0xa8, /* 28: ldp x19, x20, [sp], #48 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */ + }; + + static const DWORD unwind_info_6_packed = + (1 << 0) | /* Flag */ + (sizeof(function_6)/4 << 2) | /* FunctionLength */ + (2 << 13) | /* RegF */ + (3 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) }; + + static const struct results_arm64 results_6[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }}, + { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_7[] = + { + 0xf3, 0x0f, 0x1d, 0xf8, /* 00: str x19, [sp, #-48]! */ + 0xe8, 0xa7, 0x00, 0x6d, /* 04: stp d8, d9, [sp, #8] */ + 0xea, 0xaf, 0x01, 0x6d, /* 08: stp d10, d11, [sp, #24] */ + 0xff, 0x03, 0x00, 0xd1, /* 0c: sub sp, sp, #0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xff, 0x03, 0x00, 0x91, /* 14: add sp, sp, #0 */ + 0xea, 0xaf, 0x41, 0x6d, /* 18: ldp d10, d11, [sp, #24] */ + 0xe8, 0xa7, 0x40, 0x6d, /* 1c: ldp d8, d9, [sp, #8] */ + 0xf3, 0x07, 0x43, 0xf8, /* 20: ldr x19, [sp], #48 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */ + }; + + static const DWORD unwind_info_7_packed = + (1 << 0) | /* Flag */ + (sizeof(function_7)/4 << 2) | /* FunctionLength */ + (3 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) }; + + static const struct results_arm64 results_7[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_8[] = + { + 0xe8, 0x27, 0xbf, 0x6d, /* 00: stp d8, d9, [sp, #-16]! */ + 0xff, 0x83, 0x00, 0xd1, /* 04: sub sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xff, 0x83, 0x00, 0x91, /* 0c: add sp, sp, #32 */ + 0xe8, 0x27, 0xc1, 0x6c, /* 10: ldp d8, d9, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_8_packed = + (1 << 0) | /* Flag */ + (sizeof(function_8)/4 << 2) | /* FunctionLength */ + (1 << 13) | /* RegF */ + (0 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) }; + + static const struct results_arm64 results_8[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_9[] = + { + 0xf3, 0x0f, 0x1b, 0xf8, /* 00: str x19, [sp, #-80]! */ + 0xe0, 0x87, 0x00, 0xa9, /* 04: stp x0, x1, [sp, #8] */ + 0xe2, 0x8f, 0x01, 0xa9, /* 08: stp x2, x3, [sp, #24] */ + 0xe4, 0x97, 0x02, 0xa9, /* 0c: stp x4, x5, [sp, #40] */ + 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */ + 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */ + 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */ + 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */ + }; + + static const DWORD unwind_info_9_packed = + (1 << 0) | /* Flag */ + (sizeof(function_9)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (1 << 20) | /* H */ + (0 << 21) | /* CR */ + (7 << 23); /* FrameSize */ + + static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) }; + + static const struct results_arm64 results_9[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x28, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x2c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x30, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x34, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_10[] = + { + 0xfe, 0x0f, 0x1f, 0xf8, /* 00: str lr, [sp, #-16]! */ + 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xff, 0x43, 0x00, 0x91, /* 0c: add sp, sp, #16 */ + 0xfe, 0x07, 0x41, 0xf8, /* 10: ldr lr, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_10_packed = + (1 << 0) | /* Flag */ + (sizeof(function_10)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (0 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (2 << 23); /* FrameSize */ + + static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) }; + + static const struct results_arm64 results_10[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_11[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x7b, 0x01, 0xa9, /* 04: stp x21, lr, [sp, #16] */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */ + 0xf5, 0x7b, 0x41, 0xa9, /* 14: ldp x21, lr, [sp, #16] */ + 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_11_packed = + (1 << 0) | /* Flag */ + (sizeof(function_11)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (3 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) }; + + static const struct results_arm64 results_11[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_12[] = + { + 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */ + 0xfd, 0x7b, 0xbe, 0xa9, /* 04: stp x29, lr, [sp, #-32]! */ + 0xfd, 0x03, 0x00, 0x91, /* 08: mov x29, sp */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xbf, 0x03, 0x00, 0x91, /* 10: mov sp, x29 */ + 0xfd, 0x7b, 0xc2, 0xa8, /* 14: ldp x29, lr, [sp], #32 */ + 0xf3, 0x53, 0xc1, 0xa8, /* 18: ldp x19, x20, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_12_packed = + (1 << 0) | /* Flag */ + (sizeof(function_12)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (3 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) }; + + static const struct results_arm64 results_12[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x14, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x18, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x1c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_13[] = + { + 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */ + 0xff, 0x43, 0x08, 0xd1, /* 04: sub sp, sp, #528 */ + 0xfd, 0x7b, 0x00, 0xd1, /* 08: stp x29, lr, [sp] */ + 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xbf, 0x03, 0x00, 0x91, /* 14: mov sp, x29 */ + 0xfd, 0x7b, 0x40, 0xa9, /* 18: ldp x29, lr, [sp] */ + 0xff, 0x43, 0x08, 0x91, /* 1c: add sp, sp, #528 */ + 0xf3, 0x53, 0xc1, 0xa8, /* 20: ldp x19, x20, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */ + }; + + static const DWORD unwind_info_13_packed = + (1 << 0) | /* Flag */ + (sizeof(function_13)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (3 << 21) | /* CR */ + (34 << 23); /* FrameSize */ + + static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) }; + + static const struct results_arm64 results_13[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x14, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x18, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x1c, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }}, + { 0x20, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x24, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_14[] = + { + 0xe6, 0x9f, 0xba, 0xad, /* 00: stp q6, q7, [sp, #-0xb0]! */ + 0xe8, 0x27, 0x01, 0xad, /* 04: stp q8, q9, [sp, #0x20] */ + 0xea, 0x2f, 0x02, 0xad, /* 08: stp q10, q11, [sp, #0x40] */ + 0xec, 0x37, 0x03, 0xad, /* 0c: stp q12, q13, [sp, #0x60] */ + 0xee, 0x3f, 0x04, 0xad, /* 10: stp q14, q15, [sp, #0x80] */ + 0xfd, 0x7b, 0x0a, 0xa9, /* 14: stp x29, x30, [sp, #0xa0] */ + 0xfd, 0x83, 0x02, 0x91, /* 18: add x29, sp, #0xa0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 1c: nop */ + 0xfd, 0x7b, 0x4a, 0xa9, /* 20: ldp x29, x30, [sp, #0xa0] */ + 0xee, 0x3f, 0x44, 0xad, /* 24: ldp q14, q15, [sp, #0x80] */ + 0xec, 0x37, 0x43, 0xad, /* 28: ldp q12, q13, [sp, #0x60] */ + 0xea, 0x2f, 0x42, 0xad, /* 2c: ldp q10, q11, [sp, #0x40] */ + 0xe8, 0x27, 0x41, 0xad, /* 30: ldp q8, q9, [sp, #0x20] */ + 0xe6, 0x9f, 0xc5, 0xac, /* 34: ldp q6, q7, [sp], #0xb0 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 38: ret */ + }; + + static const DWORD unwind_info_14_header = + (sizeof(function_14)/4) | /* function length */ + (0 << 20) | /* X */ + (1 << 21) | /* E */ + (2 << 22) | /* epilog */ + (5 << 27); /* codes */ + + static const BYTE unwind_info_14[] = + { + DW(unwind_info_14_header), + UWOP_ADD_FP(0xa0), /* 18: add x29, sp, #0xa0 */ + UWOP_SAVE_FPLR(0xa0), /* 14: stp x29, x30, [sp, #0xa0] */ + UWOP_SAVE_ANY_REG(0x4e,0x88), /* 10: stp q14, q15, [sp, #0x80] */ + UWOP_SAVE_NEXT, /* 0c: stp q12, q13, [sp, #0x60] */ + UWOP_SAVE_ANY_REG(0x4a,0x84), /* 08: stp q10, q11, [sp, #0x40] */ + UWOP_SAVE_ANY_REG(0x48,0x82), /* 04: stp q8, q9, [sp, #0x20] */ + UWOP_SAVE_ANY_REG(0x66,0x8a), /* 00: stp q6, q7, [sp, #-0xb0]! */ + UWOP_END, + UWOP_NOP /* padding */ + }; + + static const struct results_arm64 results_14[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {-1,-1} }}, + { 0x18, 0x00, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }}, + { 0x1c, 0xa0, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }}, + }; + + static const BYTE function_15[] = + { + 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_15_header = + (sizeof(function_15)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (2 << 27); /* codes */ + + static const BYTE unwind_info_15[] = + { + DW(unwind_info_15_header), + UWOP_END_C, + UWOP_SET_FP, /* mov x29, sp */ + UWOP_SAVE_REGP(19, 0x10), /* stp r19, r20, [sp, #0x10] */ + UWOP_SAVE_FPLR_X(0x20), /* stp r29, lr, [sp,-#0x20]! */ + UWOP_END, + UWOP_NOP, /* padding */ + UWOP_NOP, /* padding */ + }; + + static const struct results_arm64 results_15[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x04, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + }; + + static const BYTE function_16[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_16_header = + (sizeof(function_16)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_16[] = + { + DW(unwind_info_16_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_EC_CONTEXT, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results_arm64 results_16[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0 , 0x00f8, 0x0a8, FALSE, { {x0, 0x80}, {x1, 0x88}, {x2, 0xb8}, {x3, 0xc0}, {x4, 0xc8}, {x5, 0xd0}, {x6, 0x130}, {x7, 0x140}, {x8, 0x78}, {x9, 0x150}, {x10, 0x160}, {x11, 0x170}, {x12, 0x180}, {x13, 0}, {x14, 0}, {x15, 0x190}, {x16, 0x0158014801380128}, {x17, 0x0198018801780168}, {x18, 0}, {x19, 0xd8}, {x20, 0xe0}, {x21, 0xe8}, {x22, 0x0f0}, {x23, 0}, {x24, 0}, {x25, 0xa8}, {x26, 0xb0}, {x27, 0x90}, {x28, 0}, {x29, 0xa0}, {lr, 0x120}, {d0, 0x1a0}, {d1, 0x1b0}, {d2, 0x1c0}, {d3, 0x1d0}, {d4, 0x1e0}, {d5, 0x1f0}, {d6, 0x200}, {d7, 0x210}, {d8, 0x220}, {d9, 0x230}, {d10, 0x240}, {d11, 0x250}, {d12, 0x260}, {d13, 0x270}, {d14, 0x280}, {d15, 0x290}, {-1,-1} }}, + { 0x08, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + { 0x0c, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + { 0x10, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + }; + + static const BYTE function_17[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 0c: ret */ + }; + + static const DWORD unwind_info_17_header = + (sizeof(function_17)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_17[] = + { + DW(unwind_info_17_header), + + UWOP_CLEAR_UNWOUND_TO_CALL, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results_arm64 results_17[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_18[] = + { + 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 08: ret */ + }; + + static const struct results_arm64 results_18[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x08, 0x00, -1, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x0c, 0x00, -2, 0, 0xdeadbeef, FALSE, { {-1,-1} }}, + }; + + static const struct unwind_test_arm64 tests[] = + { +#define TEST(func, unwind, size, results, unwound_clear, last_ptr, stack_value_index, stack_value) \ + { func, sizeof(func), unwind, size, results, ARRAY_SIZE(results), unwound_clear, last_ptr, stack_value_index, stack_value } + TEST(function_0, unwind_info_0, sizeof(unwind_info_0), results_0, 0, 0, -1, 0), + TEST(function_1, unwind_info_1, 0, results_1, 0, 0, -1, 0), + TEST(function_2, unwind_info_2, sizeof(unwind_info_2), results_2, 1, 0, -1, 0), + TEST(function_3, unwind_info_3, sizeof(unwind_info_3), results_3, 2, x28, 0, CONTEXT_ARM64_UNWOUND_TO_CALL), + TEST(function_4, unwind_info_4, sizeof(unwind_info_4), results_4, 0, 0, -1, 0), + TEST(function_5, unwind_info_5, sizeof(unwind_info_5), results_5, 0, 0, -1, 0), + TEST(function_6, unwind_info_6, 0, results_6, 0, 0, -1, 0), + TEST(function_7, unwind_info_7, 0, results_7, 0, 0, -1, 0), + TEST(function_8, unwind_info_8, 0, results_8, 0, 0, -1, 0), + TEST(function_9, unwind_info_9, 0, results_9, 0, 0, -1, 0), + TEST(function_10, unwind_info_10, 0, results_10, 0, 0, -1, 0), + TEST(function_11, unwind_info_11, 0, results_11, 0, 0, -1, 0), + TEST(function_12, unwind_info_12, 0, results_12, 0, 0, -1, 0), + TEST(function_13, unwind_info_13, 0, results_13, 0, 0, -1, 0), + TEST(function_14, unwind_info_14, sizeof(unwind_info_14), results_14, 0, 0, -1, 0), + TEST(function_15, unwind_info_15, sizeof(unwind_info_15), results_15, 0, 0, -1, 0), + TEST(function_16, unwind_info_16, sizeof(unwind_info_16), results_16, 2, x18, 6, CONTEXT_ARM64_UNWOUND_TO_CALL), + TEST(function_17, unwind_info_17, sizeof(unwind_info_17), results_17, 2, 0, -1, 0), + TEST(function_18, NULL, 0, results_18, 0, 0, -1, 0), +#undef TEST + }; + unsigned int i; + +#ifdef __x86_64__ + void *code_mem = NULL; + SIZE_T code_size = 0x10000; + MEM_EXTENDED_PARAMETER param = { 0 }; + + param.Type = MemExtendedParameterAttributeFlags; + param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE; + if (!pNtAllocateVirtualMemoryEx || + pNtAllocateVirtualMemoryEx( GetCurrentProcess(), &code_mem, &code_size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ¶m, 1 )) + return; + trace( "running arm64ec tests\n" ); +#endif + + for (i = 0; i < ARRAY_SIZE(tests); i++) + call_virtual_unwind_arm64( code_mem, i, &tests[i] ); +} + +#endif // __REACTOS__ + +#undef UWOP_ALLOC_SMALL +#undef UWOP_ALLOC_LARGE + +#endif /* __aarch64__ || __x86_64__ */ + +#ifdef __x86_64__ + +#define UWOP_PUSH_NONVOL 0 +#define UWOP_ALLOC_LARGE 1 +#define UWOP_ALLOC_SMALL 2 +#define UWOP_SET_FPREG 3 +#define UWOP_SAVE_NONVOL 4 +#define UWOP_SAVE_NONVOL_FAR 5 +#define UWOP_SAVE_XMM128 8 +#define UWOP_SAVE_XMM128_FAR 9 +#define UWOP_PUSH_MACHFRAME 10 + +struct results_x86 +{ + int rip_offset; /* rip offset from code start */ + int rbp_offset; /* rbp offset from stack pointer */ + int handler; /* expect handler to be set? */ + int rip; /* expected final rip value */ + int frame; /* expected frame return value */ + int regs[8][2]; /* expected values for registers */ +}; + +struct unwind_test_x86 +{ + const BYTE *function; + size_t function_size; + const BYTE *unwind_info; + const struct results_x86 *results; + unsigned int nb_results; + const struct results_x86 *broken_results; +}; + +enum regs +{ + rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, + r8, r9, r10, r11, r12, r13, r14, r15 +}; + +static const char * const reg_names_x86[16] = +{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +}; + +#define UWOP(code,info) (UWOP_##code | ((info) << 4)) + +static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 *test ) +{ + static const int code_offset = 1024; + static const int unwind_offset = 2048; + void *data; + NTSTATUS status; + CONTEXT context; + PEXCEPTION_ROUTINE handler; + RUNTIME_FUNCTION runtime_func; + KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; + UINT i, j, k, broken_k; + ULONG64 fake_stack[256]; + ULONG64 frame, orig_rip, orig_rbp, unset_reg; + void *expected_handler, *broken_handler; + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + if (test->unwind_info) + { + UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8; + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size ); + runtime_func.BeginAddress = code_offset; + runtime_func.EndAddress = code_offset + test->function_size; + runtime_func.UnwindData = unwind_offset; + } + + trace( "code: %p stack: %p\n", code_mem, fake_stack ); + + for (i = 0; i < test->nb_results; i++) + { + memset( &ctx_ptr, 0, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 8; + + context.Rsp = (ULONG_PTR)fake_stack; + context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset; + orig_rbp = context.Rbp; + orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset; + + trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i, + (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp ); + + if (!test->unwind_info) fake_stack[0] = 0x1234; + expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL; + broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL; + + if (pRtlVirtualUnwind2) + { + CONTEXT new_context = context; + + handler = (void *)0xdeadbeef; + data = (void *)0xdeadbeef; + status = pRtlVirtualUnwind2( UNW_FLAG_EHANDLER, (ULONG_PTR)code_mem, orig_rip, + test->unwind_info ? &runtime_func : NULL, &new_context, + NULL, &data, &frame, &ctx_ptr, NULL, NULL, &handler, 0 ); + ok( !status, "RtlVirtualUnwind2 failed %lx\n", status ); + + ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ), + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler ); + if (handler) + ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + else + ok( data == (test->unwind_info ? (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p\n", testnum, i, data ); + } + + data = (void *)0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip, + test->unwind_info ? &runtime_func : NULL, + &context, &data, &frame, &ctx_ptr ); + + expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL; + broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL; + + ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ), + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler ); + if (handler) + ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + else + ok( data == (test->unwind_info ? (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p\n", testnum, i, data ); + + ok( context.Rip == test->results[i].rip + || broken( test->broken_results && context.Rip == test->broken_results[i].rip ), + "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip ); + ok( frame == (ULONG64)fake_stack + test->results[i].frame + || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ), + "%u/%u: wrong frame %p/%p\n", + testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame ); + + for (j = 0; j < 16; j++) + { + static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs); + + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (test->broken_results) + { + for (broken_k = 0; broken_k < nb_regs; broken_k++) + { + if (test->broken_results[i].regs[broken_k][0] == -1) + { + broken_k = nb_regs; + break; + } + if (test->broken_results[i].regs[broken_k][0] == j) + break; + } + } + else + { + broken_k = k; + } + + if (j == rsp) /* rsp is special */ + { + ULONG64 expected_rsp, broken_rsp; + + ok( !ctx_ptr.IntegerContext[j], + "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i ); + expected_rsp = test->results[i].regs[k][1] < 0 + ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1]; + if (test->broken_results) + broken_rsp = test->broken_results[i].regs[k][1] < 0 + ? -test->broken_results[i].regs[k][1] + : (ULONG64)fake_stack + test->broken_results[i].regs[k][1]; + else + broken_rsp = expected_rsp; + + ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ), + "%u/%u: register rsp wrong %p/%p\n", + testnum, i, (void *)context.Rsp, (void *)expected_rsp ); + continue; + } + + if (ctx_ptr.IntegerContext[j]) + { + ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %Ix\n", + testnum, i, reg_names_x86[j], *(&context.Rax + j) ); + ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1] + || broken( broken_k == nb_regs || *(&context.Rax + j) + == test->broken_results[i].regs[broken_k][1] ), + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] ); + } + else + { + ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n", + testnum, i, reg_names_x86[j] ); + if (j == rbp) + ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n", + testnum, i, (void *)context.Rbp ); + else + ok( *(&context.Rax + j) == unset_reg, + "%u/%u: register %s wrong %p/unset\n", + testnum, i, reg_names_x86[j], (void *)*(&context.Rax + j)); + } + } + } +} + +static void test_virtual_unwind_x86(void) +{ + static const BYTE function_0[] = + { + 0xff, 0xf5, /* 00: push %rbp */ + 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */ + 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */ + 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */ + 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */ + 0x90, /* 1c: nop */ + 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */ + 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */ + 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */ + 0x5d, /* 32: pop %rbp */ + 0xc3 /* 33: ret */ + }; + + static const BYTE unwind_info_0[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x1c, /* prolog size */ + 8, /* opcode count */ + (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */ + + 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */ + 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */ + 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */ + 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */ + 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_x86 results_0[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }}, + { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }}, + { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }}, + { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}}, + { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}}, + { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}}, + }; + + static const struct results_x86 broken_results_0[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }}, + { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }}, + { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }}, + { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + /* On Win11 output frame in epilogue corresponds to context->Rsp - 0x8 when fpreg is set. */ + { 0x2b, 0x40, FALSE, 0x128, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}}, + { 0x32, 0x40, FALSE, 0x008, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}}, + { 0x33, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1}}}, + }; + + static const BYTE function_1[] = + { + 0x53, /* 00: push %rbx */ + 0x55, /* 01: push %rbp */ + 0x56, /* 02: push %rsi */ + 0x57, /* 03: push %rdi */ + 0x41, 0x54, /* 04: push %r12 */ + 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */ + 0x90, 0x90, /* 0a: nop; nop */ + 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */ + 0x41, 0x5c, /* 10: pop %r12 */ + 0x5f, /* 12: pop %rdi */ + 0x5e, /* 13: pop %rsi */ + 0x5d, /* 14: pop %rbp */ + 0x5b, /* 15: pop %rbx */ + 0xc3 /* 16: ret */ + }; + + static const BYTE unwind_info_1[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0a, /* prolog size */ + 6, /* opcode count */ + 0, /* frame reg */ + + 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */ + 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */ + 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */ + 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */ + 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_x86 results_1[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, + { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, + { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, + { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, + { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, + { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, + { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, + { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, + { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, + { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, + { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, + { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, + { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + }; + + static const BYTE function_2[] = + { + 0x55, /* 00: push %rbp */ + 0x90, 0x90, /* 01: nop; nop */ + 0x5d, /* 03: pop %rbp */ + 0xc3 /* 04: ret */ + }; + + static const BYTE unwind_info_2[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 2, /* opcode count */ + 0, /* frame reg */ + + 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_x86 results_2[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }}, + }; + + static const BYTE unwind_info_3[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 2, /* opcode count */ + 0, /* frame reg */ + + 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_x86 results_3[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }}, + }; + + static const BYTE function_4[] = + { + 0x55, /* 00: push %rbp */ + 0x5d, /* 01: pop %rbp */ + 0xc3 /* 02: ret */ + }; + + static const BYTE unwind_info_4[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 0, /* opcode count */ + 0, /* frame reg */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results_x86 results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + }; + + static const struct results_x86 broken_results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + }; + +#if 0 + static const BYTE function_5[] = + { + 0x90, /* 00: nop */ + 0x90, /* 01: nop */ + 0xc3 /* 02: ret */ + }; + + static const struct results_x86 results_5[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }}, + { 0x02, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }}, + }; +#endif + + static const struct unwind_test_x86 tests[] = + { + { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0), broken_results_0 }, + { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) }, + { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) }, + { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) }, + + /* Broken before Win10 1809. */ + { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 }, +#if 0 /* crashes before Win10 21H2 */ + { function_5, sizeof(function_5), NULL, results_5, ARRAY_SIZE(results_5) }, +#endif + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + call_virtual_unwind_x86( i, &tests[i] ); +} + +#endif /* __x86_64__ */ + +#ifdef __x86_64__ +#define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->EndAddress = (func)->BeginAddress + (len); } while(0) +#elif defined(__arm__) +#define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 2; (func)->Flag = 1; } while(0) +#else +#define SET_RUNTIME_FUNC_LEN(func,len) do { (func)->FunctionLength = len / 4; (func)->Flag = 1; } while(0) +#endif + +static RUNTIME_FUNCTION * CALLBACK dynamic_unwind_callback( DWORD_PTR pc, PVOID context ) +{ + static const int code_offset = 1024; + static RUNTIME_FUNCTION runtime_func; + (*(DWORD *)context)++; + + runtime_func.BeginAddress = code_offset + 16; + runtime_func.UnwindData = 0; + SET_RUNTIME_FUNC_LEN( &runtime_func, 16 ); + return &runtime_func; +} + +static void test_dynamic_unwind(void) +{ + static const int code_offset = 1024; + char buf[2 * sizeof(RUNTIME_FUNCTION) + 4]; + MEM_EXTENDED_PARAMETER param = { 0 }; + RUNTIME_FUNCTION *runtime_func, *func; + ULONG_PTR table, base, ec_code; + void *growable_table, *ptr; + NTSTATUS status; + SIZE_T size = 0x1000; + DWORD count; + ULONG len, len2; + + if (!pRtlInstallFunctionTableCallback || !pRtlLookupFunctionEntry) + { + win_skip( "Dynamic unwind functions not found\n" ); + return; + } + + /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */ + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->UnwindData = 0; + SET_RUNTIME_FUNC_LEN( runtime_func, 16 ); + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); + + /* Lookup function outside of any function table */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); + ok( !base || broken(base == 0xdeadbeef), + "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base ); + + /* Test with pointer inside of our function */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == runtime_func, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + /* Test RtlDeleteFunctionTable */ + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); + + /* Unaligned RUNTIME_FUNCTION pointer */ + runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3); + runtime_func->BeginAddress = code_offset; + runtime_func->UnwindData = 0; + SET_RUNTIME_FUNC_LEN( runtime_func, 16 ); + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); + + /* Attempt to insert the same entry twice */ + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->UnwindData = 0; + SET_RUNTIME_FUNC_LEN( runtime_func, 16 ); + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); + + /* Empty table */ + ok( pRtlAddFunctionTable( runtime_func, 0, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for empty table\n" ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for empty table\n" ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable succeeded twice for empty table\n" ); + + /* Test RtlInstallFunctionTableCallback with both low bits unset */ + table = (ULONG_PTR)code_mem; + ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), + "RtlInstallFunctionTableCallback returned success for table = %Ix\n", table ); + + /* Test RtlInstallFunctionTableCallback with both low bits set */ + table = (ULONG_PTR)code_mem | 0x3; + ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), + "RtlInstallFunctionTableCallback failed for table = %Ix\n", table ); + + /* Lookup function outside of any function table */ + count = 0; + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); + ok( !base || broken(base == 0xdeadbeef), + "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base ); + ok( !count, + "RtlLookupFunctionEntry issued %ld unexpected calls to dynamic_unwind_callback\n", count ); + + /* Test with pointer inside of our function */ + count = 0; + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL ); + ok( count == 1 || broken(!count), /* win10 arm */ + "RtlLookupFunctionEntry issued %ld calls to dynamic_unwind_callback, expected: 1\n", count ); + if (count) + { + ok( func != NULL && func->BeginAddress == code_offset + 16, + "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base: %Ix / %Ix\n", (ULONG_PTR)code_mem, base ); + } + + /* Clean up again */ + ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), + "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table ); + ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), + "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table ); + + if (!pRtlAddGrowableFunctionTable) + { + win_skip("Growable function tables are not supported.\n"); + return; + } + + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->UnwindData = 0; + SET_RUNTIME_FUNC_LEN( runtime_func, 16 ); + runtime_func++; + runtime_func->BeginAddress = code_offset + 16; + runtime_func->UnwindData = 0; + SET_RUNTIME_FUNC_LEN( runtime_func, 16 ); + runtime_func = (RUNTIME_FUNCTION *)buf; + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem, + (ULONG_PTR)code_mem + code_offset + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + + /* Current count is 0. */ + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 1 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == runtime_func, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + /* Second function is inaccessible yet. */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 2 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == runtime_func + 1, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL ); + ok( func == NULL, "RtlLookupFunctionEntry got %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)code_mem + code_offset + 8, &base, &len ); + ok( func == NULL, "RtlLookupFunctionTable wrong table, got: %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base ); + + base = 0xdeadbeef; + len = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)pRtlLookupFunctionEntry, &base, &len ); + ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"), + "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 ); + ok( func == ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr ); + ok( len == len2 || !ptr, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", len, len2 ); + + pRtlDeleteGrowableFunctionTable( growable_table ); + +#ifndef __REACTOS__ + param.Type = MemExtendedParameterAttributeFlags; + param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE; + ec_code = 0; + if (pNtAllocateVirtualMemoryEx && + !pNtAllocateVirtualMemoryEx( GetCurrentProcess(), (void **)&ec_code, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, ¶m, 1 )) + { + static const BYTE fast_forward[] = { 0x48, 0x8b, 0xc4, 0x48, 0x89, 0x58, 0x20, 0x55, 0x5d, 0xe9 }; + IMAGE_ARM64EC_METADATA *metadata; + ARM64_RUNTIME_FUNCTION *arm64func = (ARM64_RUNTIME_FUNCTION *)buf; + + trace( "running arm64ec tests\n" ); + + if (!memcmp( pRtlLookupFunctionEntry, fast_forward, sizeof(fast_forward) )) + { + ptr = (char *)pRtlLookupFunctionEntry + sizeof(fast_forward); + ptr = (char *)ptr + 4 + *(int *)ptr; + base = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)ptr, &base, &len ); + ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"), + "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 ); + ok( func != ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &len2 ); + metadata = (void *)((IMAGE_LOAD_CONFIG_DIRECTORY *)ptr)->CHPEMetadataPointer; + ok( (char *)func == (char *)base + metadata->ExtraRFETable, + "RtlLookupFunctonTable wrong table, got: %p / %p\n", func, (char *)base + metadata->ExtraRFETable ); + ok( len == metadata->ExtraRFETableSize, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", + len, metadata->ExtraRFETableSize ); + } + + arm64func->BeginAddress = code_offset; + arm64func->Flag = 1; + arm64func->FunctionLength = 4; + arm64func->RegF = 1; + arm64func->RegI = 1; + arm64func->H = 1; + arm64func->CR = 1; + arm64func->FrameSize = 1; + arm64func++; + arm64func->BeginAddress = code_offset + 16; + arm64func->Flag = 1; + arm64func->FunctionLength = 4; + arm64func->RegF = 1; + arm64func->RegI = 1; + arm64func->H = 1; + arm64func->CR = 1; + arm64func->FrameSize = 1; + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, (RUNTIME_FUNCTION *)buf, + 2, 2, ec_code, ec_code + code_offset + 64 ); + ok( !status, "RtlAddGrowableFunctionTable failed %lx\n", status ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( ec_code + code_offset + 8, &base, NULL ); + ok( func == (RUNTIME_FUNCTION *)buf, "RtlLookupFunctionEntry expected func: %p, got: %p\n", + buf, func ); + ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", + ec_code, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( ec_code + code_offset + 16, &base, NULL ); + ok( func == (RUNTIME_FUNCTION *)(buf + sizeof(*arm64func)), + "RtlLookupFunctionEntry expected func: %p, got: %p\n", buf + sizeof(*arm64func), func ); + ok( base == ec_code, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", ec_code, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( ec_code + code_offset + 32, &base, NULL ); + ok( !func, "RtlLookupFunctionEntry got: %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionEntry got: %Ix\n", base ); + + pRtlDeleteGrowableFunctionTable( growable_table ); + VirtualFree( (void *)ec_code, 0, MEM_RELEASE ); + } +#endif // __REACTOS__ +} + + +START_TEST(unwind) +{ + ntdll = GetModuleHandleA("ntdll.dll"); + code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + +#define X(f) p##f = (void*)GetProcAddress(ntdll, #f) + X(NtAllocateVirtualMemoryEx); + X(RtlAddFunctionTable); + X(RtlAddGrowableFunctionTable); + X(RtlDeleteFunctionTable); + X(RtlDeleteGrowableFunctionTable); + X(RtlGrowFunctionTable); + X(RtlInstallFunctionTableCallback); + X(RtlLookupFunctionEntry); + X(RtlLookupFunctionTable); + X(RtlVirtualUnwind2); +#undef X + +#ifdef __arm__ + test_virtual_unwind_arm(); +#elif defined(__aarch64__) + test_virtual_unwind_arm64(); +#elif defined(__x86_64__) + test_virtual_unwind_x86(); +#ifndef __REACTOS__ + test_virtual_unwind_arm64(); +#endif // __REACTOS__ +#endif + + test_dynamic_unwind(); +} + +#else /* !__i386__ */ + +START_TEST(unwind) +{ +} + +#endif /* !__i386__ */ diff --git a/modules/rostests/winetests/ntdll/virtual.c b/modules/rostests/winetests/ntdll/virtual.c new file mode 100644 index 00000000000..f74c078b420 --- /dev/null +++ b/modules/rostests/winetests/ntdll/virtual.c @@ -0,0 +1,3039 @@ +/* + * Unit test suite for the virtual memory APIs. + * + * Copyright 2019 Remi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "wine/test.h" +#include "ddk/wdm.h" + +static unsigned int page_size; + +static DWORD64 (WINAPI *pGetEnabledXStateFeatures)(void); +static NTSTATUS (WINAPI *pRtlCreateUserStack)(SIZE_T, SIZE_T, ULONG, SIZE_T, SIZE_T, INITIAL_TEB *); +static NTSTATUS (WINAPI *pRtlCreateUserThread)(HANDLE, SECURITY_DESCRIPTOR*, BOOLEAN, ULONG, SIZE_T, + SIZE_T, PRTL_THREAD_START_ROUTINE, void*, HANDLE*, CLIENT_ID* ); +static ULONG64 (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64); +static NTSTATUS (WINAPI *pRtlFreeUserStack)(void *); +static void * (WINAPI *pRtlFindExportedRoutineByName)(HMODULE,const char*); +static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); +static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); +static BOOLEAN (WINAPI *pRtlIsEcCode)(const void *); +static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE, PVOID *, SIZE_T *, ULONG, ULONG, + MEM_EXTENDED_PARAMETER *, ULONG); +static NTSTATUS (WINAPI *pNtMapViewOfSectionEx)(HANDLE, HANDLE, PVOID *, const LARGE_INTEGER *, SIZE_T *, + ULONG, ULONG, MEM_EXTENDED_PARAMETER *, ULONG); +static NTSTATUS (WINAPI *pNtSetInformationVirtualMemory)(HANDLE, VIRTUAL_MEMORY_INFORMATION_CLASS, + ULONG_PTR, PMEMORY_RANGE_ENTRY, + PVOID, ULONG); + +static const BOOL is_win64 = sizeof(void*) != sizeof(int); +static BOOL is_wow64; + +static SYSTEM_BASIC_INFORMATION sbi; + +static HANDLE create_target_process(const char *arg) +{ + char **argv; + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + BOOL ret; + STARTUPINFOA si = { 0 }; + si.cb = sizeof(si); + + winetest_get_mainargs(&argv); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "error: %lu\n", GetLastError()); + ret = CloseHandle(pi.hThread); + ok(ret, "error %lu\n", GetLastError()); + return pi.hProcess; +} + +static UINT_PTR get_zero_bits(UINT_PTR p) +{ + UINT_PTR z = 0; + +#ifdef _WIN64 + if (p >= 0xffffffff) + return (~(UINT_PTR)0) >> get_zero_bits(p >> 32); +#endif + + if (p == 0) return 0; + while ((p >> (31 - z)) != 1) z++; + return z; +} + +static UINT_PTR get_zero_bits_mask(ULONG_PTR z) +{ + if (z >= 32) + { + z = get_zero_bits(z); +#ifdef _WIN64 + if (z >= 32) return z; +#endif + } + return (~(UINT32)0) >> z; +} + +static void test_NtAllocateVirtualMemory(void) +{ + void *addr1, *addr2; + NTSTATUS status; + SIZE_T size; + ULONG_PTR zero_bits; + + /* simple allocation should success */ + size = 0x1000; + addr1 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_SUCCESS, "NtAllocateVirtualMemory returned %08lx\n", status); + + /* allocation conflicts because of 64k align */ + size = 0x1000; + addr2 = (char *)addr1 + 0x1000; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_CONFLICTING_ADDRESSES, "NtAllocateVirtualMemory returned %08lx\n", status); + + /* it should conflict, even when zero_bits is explicitly set */ + size = 0x1000; + addr2 = (char *)addr1 + 0x1000; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, 12, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_CONFLICTING_ADDRESSES, "NtAllocateVirtualMemory returned %08lx\n", status); + + /* 1 zero bits should zero 63-31 upper bits */ + size = 0x1000; + addr2 = NULL; + zero_bits = 1; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, zero_bits, &size, + MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY || + broken(status == STATUS_INVALID_PARAMETER_3) /* winxp */, + "NtAllocateVirtualMemory returned %08lx\n", status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)addr2 >> (32 - zero_bits)) == 0, + "NtAllocateVirtualMemory returned address: %p\n", addr2); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory return %08lx, addr2: %p\n", status, addr2); + } + + for (zero_bits = 2; zero_bits <= 20; zero_bits++) + { + size = 0x1000; + addr2 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, zero_bits, &size, + MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY || + broken(zero_bits == 20 && status == STATUS_CONFLICTING_ADDRESSES) /* w1064v1809 */, + "NtAllocateVirtualMemory with %d zero_bits returned %08lx\n", (int)zero_bits, status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)addr2 >> (32 - zero_bits)) == 0, + "NtAllocateVirtualMemory with %d zero_bits returned address %p\n", (int)zero_bits, addr2); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory return %08lx, addr2: %p\n", status, addr2); + } + } + + /* 21 zero bits never succeeds */ + size = 0x1000; + addr2 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, 21, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_NO_MEMORY || status == STATUS_INVALID_PARAMETER, + "NtAllocateVirtualMemory returned %08lx\n", status); + if (status == STATUS_SUCCESS) + { + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory return %08lx, addr2: %p\n", status, addr2); + } + + /* 22 zero bits is invalid */ + size = 0x1000; + addr2 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, 22, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_3 || status == STATUS_INVALID_PARAMETER, + "NtAllocateVirtualMemory returned %08lx\n", status); + + /* zero bits > 31 should be considered as a leading zeroes bitmask on 64bit and WoW64 */ + size = 0x1000; + addr2 = NULL; + zero_bits = 0x1aaaaaaa; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, zero_bits, &size, + MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, + PAGE_READWRITE); + + if (!is_win64 && !is_wow64) + { + ok(status == STATUS_INVALID_PARAMETER_3, "NtAllocateVirtualMemory returned %08lx\n", status); + } + else + { + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY, + "NtAllocateVirtualMemory returned %08lx\n", status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)addr2 & ~get_zero_bits_mask(zero_bits)) == 0 && + ((UINT_PTR)addr2 & ~zero_bits) != 0, /* only the leading zeroes matter */ + "NtAllocateVirtualMemory returned address %p\n", addr2); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory return %08lx, addr2: %p\n", status, addr2); + } + } + + /* AT_ROUND_TO_PAGE flag is not supported for NtAllocateVirtualMemory */ + size = 0x1000; + addr2 = (char *)addr1 + 0x1000; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr2, 0, &size, + MEM_RESERVE | MEM_COMMIT | AT_ROUND_TO_PAGE, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_5 || status == STATUS_INVALID_PARAMETER, + "NtAllocateVirtualMemory returned %08lx\n", status); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory failed\n"); + + /* NtFreeVirtualMemory tests */ + + size = 0x10000; + addr1 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ok(status == STATUS_SUCCESS, "NtAllocateVirtualMemory returned %08lx\n", status); + + size = 2; + addr2 = (char *)addr1 + 0x1fff; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_DECOMMIT); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory failed %lx\n", status); + ok( size == 0x2000, "wrong size %Ix\n", size ); + ok( addr2 == (char *)addr1 + 0x1000, "wrong addr %p\n", addr2 ); + + size = 0; + addr2 = (char *)addr1 + 0x1001; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_DECOMMIT); + ok(status == STATUS_FREE_VM_NOT_AT_BASE, "NtFreeVirtualMemory failed %lx\n", status); + ok( size == 0, "wrong size %Ix\n", size ); + ok( addr2 == (char *)addr1 + 0x1001, "wrong addr %p\n", addr2 ); + + size = 0; + addr2 = (char *)addr1 + 0xffe; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_DECOMMIT); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory failed %lx\n", status); + ok( size == 0 || broken(size == 0x10000) /* <= win10 1709 */, "wrong size %Ix\n", size ); + ok( addr2 == addr1, "wrong addr %p\n", addr2 ); + + size = 0; + addr2 = (char *)addr1 + 0x1001; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_FREE_VM_NOT_AT_BASE, "NtFreeVirtualMemory failed %lx\n", status); + ok( size == 0, "wrong size %Ix\n", size ); + ok( addr2 == (char *)addr1 + 0x1001, "wrong addr %p\n", addr2 ); + + size = 0; + addr2 = (char *)addr1 + 0xfff; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr2, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "NtFreeVirtualMemory failed %lx\n", status); + ok( size == 0x10000, "wrong size %Ix\n", size ); + ok( addr2 == addr1, "wrong addr %p\n", addr2 ); + + /* Placeholder functionality */ + size = 0x10000; + addr1 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS); + ok(!!status, "Unexpected status %08lx.\n", status); +} + +#define check_region_size(p, s) check_region_size_(p, s, __LINE__) +static void check_region_size_(void *p, SIZE_T s, unsigned int line) +{ + MEMORY_BASIC_INFORMATION mbi; + NTSTATUS status; + SIZE_T size; + + memset(&mbi, 0, sizeof(mbi)); + status = NtQueryVirtualMemory( NtCurrentProcess(), p, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok_(__FILE__,line)( !status, "Unexpected return value %08lx\n", status ); + ok_(__FILE__,line)( size == sizeof(mbi), "Unexpected return value.\n"); + ok_(__FILE__,line)( mbi.RegionSize == s, "Unexpected size %Iu, expected %Iu.\n", mbi.RegionSize, s); +} + +static void test_NtAllocateVirtualMemoryEx(void) +{ + MEMORY_BASIC_INFORMATION mbi; + MEM_EXTENDED_PARAMETER ext[2]; + char *p, *p1, *p2, *p3; + void *addresses[16]; + SIZE_T size, size2; + ULONG granularity; + NTSTATUS status; + ULONG_PTR count; + void *addr1; + + if (!pNtAllocateVirtualMemoryEx) + { + win_skip("NtAllocateVirtualMemoryEx() is missing\n"); + return; + } + + size = 0x1000; + addr1 = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + /* specifying a count of >0 with NULL parameters should fail */ + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, NULL, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + /* NULL process handle */ + size = 0x1000; + addr1 = NULL; + status = pNtAllocateVirtualMemoryEx(NULL, &addr1, &size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %08lx.\n", status); + + /* Placeholder functionality */ + size = 0x10000; + addr1 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + memset(addr1, 0xcc, size); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(!status, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READONLY, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + ok(!*(unsigned int *)addr1, "Got %#x.\n", *(unsigned int *)addr1); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_READONLY, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_COMMIT, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(!status, "Unexpected status %08lx.\n", status); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_NOACCESS, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_RESERVE, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x1000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, + MEM_WRITE_WATCH | MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_READONLY, NULL, 0); + ok(!status || broken(status == STATUS_INVALID_PARAMETER) /* Win10 1809, the version where + NtAllocateVirtualMemoryEx is introduced */, "Unexpected status %08lx.\n", status); + + if (!status) + { + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_COMMIT, PAGE_READWRITE, NULL, 0); + ok(!status, "Unexpected status %08lx.\n", status); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr1, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(mbi.AllocationProtect == PAGE_READONLY, "Unexpected protection %#lx.\n", mbi.AllocationProtect); + ok(mbi.State == MEM_COMMIT, "Unexpected state %#lx.\n", mbi.State); + ok(mbi.Type == MEM_PRIVATE, "Unexpected type %#lx.\n", mbi.Type); + ok(mbi.RegionSize == 0x10000, "Unexpected size.\n"); + + size = 0x10000; + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( NtCurrentProcess(), WRITE_WATCH_FLAG_RESET, addr1, size, + addresses, &count, &granularity ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!count, "Unexpected count %u.\n", (unsigned int)count); + *((char *)addr1 + 0x1000) = 1; + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( NtCurrentProcess(), WRITE_WATCH_FLAG_RESET, addr1, size, + addresses, &count, &granularity ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(count == 1, "Unexpected count %u.\n", (unsigned int)count); + ok(addresses[0] == (char *)addr1 + 0x1000, "Unexpected address %p.\n", addresses[0]); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + } + + /* Placeholder region splitting. */ + addr1 = NULL; + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + p = addr1; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + ok(p == addr1, "Unexpected addr %p, expected %p.\n", p, addr1); + + + /* Split in three regions. */ + addr1 = NULL; + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + p = addr1; + p1 = p + size / 2; + p2 = p1 + size / 4; + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == p + size / 2, "Unexpected addr %p, expected %p.\n", p, p + size / 2); + + check_region_size(p, size / 2); + check_region_size(p1, size / 4); + check_region_size(p2, size - size / 2 - size / 4); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p == addr1, "Unexpected addr %p, expected %p.\n", p, addr1); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == p + size / 2, "Unexpected addr %p, expected %p.\n", p1, p + size / 2); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 4, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 4); + + /* Split in two regions, specifying lower part. */ + addr1 = NULL; + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size2 = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&addr1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_INVALID_PARAMETER_3, "Unexpected status %08lx.\n", status); + ok(!size2, "Unexpected size %#Ix.\n", size2); + + p1 = addr1; + p2 = p1 + size / 4; + p3 = p2 + size / 4; + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(p1 == addr1, "Unexpected address.\n"); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + check_region_size(p1, p2 - p1); + check_region_size(p2, p3 - p2); + check_region_size(p3, size - (p3 - p1)); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_INVALID_PARAMETER_4, "Unexpected status %08lx.\n", status); + + size2 = size + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size2 = size - 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + p1 = (char *)addr1 + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + p1 = addr1; + + size2 = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_INVALID_PARAMETER_3, "Unexpected status %08lx.\n", status); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE); + ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + check_region_size(p1, size); + + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + check_region_size(p1, size / 4); + check_region_size(p2, size - size / 4); + + size2 = size - size / 4; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), (void **)&p2, &size2, MEM_RESERVE | MEM_REPLACE_PLACEHOLDER, + PAGE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size2 = size - size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0xc000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 4, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 4); + + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size, MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + + size2 = size / 4; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x4000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + + size2 = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p3, &size2, MEM_RELEASE); + ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); + + /* Split in two regions, specifying second half. */ + addr1 = NULL; + size = 0x10000; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr1, &size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size == 0x10000, "Unexpected size %#Ix.\n", size); + + p1 = addr1; + p2 = p1 + size / 2; + + size2 = size / 2; + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 2, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 2); + check_region_size(p1, size / 2); + check_region_size(p2, size / 2); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p1 == addr1, "Unexpected addr %p, expected %p.\n", p1, addr1); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p2, &size2, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); + ok(p2 == p1 + size / 2, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 2); + + memset( ext, 0, sizeof(ext) ); + ext[0].Type = MemExtendedParameterAttributeFlags; + ext[0].ULong = 0; + ext[1].Type = MemExtendedParameterAttributeFlags; + ext[1].ULong = 0; + size = 0x10000; + addr1 = NULL; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1 ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + NtFreeVirtualMemory( NtCurrentProcess(), &addr1, &size, MEM_DECOMMIT ); + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 2 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset( ext, 0, sizeof(ext) ); + ext[0].Type = MemExtendedParameterAttributeFlags; + ext[0].ULong = MEM_EXTENDED_PARAMETER_EC_CODE; + size = 0x10000; + addr1 = NULL; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1 ); +#ifdef __x86_64__ + if (pRtlGetNativeSystemInformation) + { + SYSTEM_CPU_INFORMATION cpu_info; + + pRtlGetNativeSystemInformation( SystemCpuInformation, &cpu_info, sizeof(cpu_info), NULL ); + if (cpu_info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64) + { + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (pRtlIsEcCode) ok( pRtlIsEcCode( addr1 ), "not EC code %p\n", addr1 ); + size = 0; + NtFreeVirtualMemory( NtCurrentProcess(), &addr1, &size, MEM_RELEASE ); + + size = 0x10000; + addr1 = NULL; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, NULL, 0 ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (pRtlIsEcCode) ok( !pRtlIsEcCode( addr1 ), "EC code %p\n", addr1 ); + size = 0x1000; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1 ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (pRtlIsEcCode) + { + ok( pRtlIsEcCode( addr1 ), "not EC code %p\n", addr1 ); + ok( !pRtlIsEcCode( (char *)addr1 + 0x1000 ), "EC code %p\n", (char *)addr1 + 0x1000 ); + } + size = 0x2000; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, NULL, 0 ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (pRtlIsEcCode) + { + ok( pRtlIsEcCode( addr1 ), "not EC code %p\n", addr1 ); + ok( !pRtlIsEcCode( (char *)addr1 + 0x1000 ), "EC code %p\n", (char *)addr1 + 0x1000 ); + } + + NtFreeVirtualMemory( NtCurrentProcess(), &addr1, &size, MEM_DECOMMIT ); + if (pRtlIsEcCode) ok( pRtlIsEcCode( addr1 ), "not EC code %p\n", addr1 ); + + size = 0x2000; + ext[0].ULong = 0; + status = pNtAllocateVirtualMemoryEx( NtCurrentProcess(), &addr1, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1 ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + if (pRtlIsEcCode) + { + ok( pRtlIsEcCode( addr1 ), "not EC code %p\n", addr1 ); + ok( !pRtlIsEcCode( (char *)addr1 + 0x1000 ), "EC code %p\n", (char *)addr1 + 0x1000 ); + } + + size = 0; + NtFreeVirtualMemory( NtCurrentProcess(), &addr1, &size, MEM_RELEASE ); + return; + } + } +#endif + ok(status == STATUS_INVALID_PARAMETER || status == STATUS_NOT_SUPPORTED, + "Unexpected status %08lx.\n", status); +} + +static void test_NtAllocateVirtualMemoryEx_address_requirements(void) +{ + MEM_EXTENDED_PARAMETER ext[2]; + MEM_ADDRESS_REQUIREMENTS a; + NTSTATUS status; + SYSTEM_INFO si; + SIZE_T size; + void *addr; + + if (!pNtAllocateVirtualMemoryEx) + { + win_skip("NtAllocateVirtualMemoryEx() is missing\n"); + return; + } + + GetSystemInfo(&si); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = 0; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = MemExtendedParameterMax; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&a, 0, sizeof(a)); + ext[0].Type = MemExtendedParameterAddressRequirements; + ext[0].Pointer = &a; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + ext[1] = ext[0]; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 2); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = NULL; + a.Alignment = 0; + + a.HighestEndingAddress = (void *)(0x20001000 + 1); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 2); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20000800 - 1); + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress + 0x1000; + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress; + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + size = 0x40000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)addr & 0xffff), "Unexpected addr %p.\n", addr); + ok((ULONG_PTR)addr + size <= 0x20001000, "Unexpected addr %p.\n", addr); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + + size = 0x40000; + a.HighestEndingAddress = (void *)(0x20001000 - 1); + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 24, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_3 || status == STATUS_INVALID_PARAMETER, + "Unexpected status %08lx.\n", status); + + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0xffffffff, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (is_win64 || is_wow64) + ok(!status || status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + else + ok(status == STATUS_INVALID_PARAMETER_3 || status == STATUS_INVALID_PARAMETER, + "Unexpected status %08lx.\n", status); + + if (!status) + { + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + } + + a.HighestEndingAddress = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status || status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx.\n", status); + if (!status) + { + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + } + + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + a.Alignment = 0x10000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)addr & 0xffff), "Unexpected addr %p.\n", addr); + ok((ULONG_PTR)addr + size < 0x20001000, "Unexpected addr %p.\n", addr); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + a.Alignment = 0x20000000; + size = 0x2000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_NO_MEMORY, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = NULL; + a.Alignment = 0x8000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.Alignment = 0x30000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.Alignment = 0x40000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)addr & 0x3ffff), "Unexpected addr %p.\n", addr); + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)0x20001000; + a.Alignment = 0; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)(0x20001000 - 1); + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)(0x20001000 + 1); + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)0x30000000; + a.HighestEndingAddress = (void *)0x20000000; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)0x20000000; + a.HighestEndingAddress = 0; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(addr >= (void *)0x20000000, "Unexpected addr %p.\n", addr); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (void *)0x20000000; + a.HighestEndingAddress = (void *)0x2fffffff; + size = 0x1000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(!status, "Unexpected status %08lx.\n", status); + ok(addr >= (void *)0x20000000 && addr < (void *)0x30000000, "Unexpected addr %p.\n", addr); + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(!status, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = (char *)si.lpMaximumApplicationAddress + 1; + a.HighestEndingAddress = 0; + size = 0x10000; + addr = NULL; + status = pNtAllocateVirtualMemoryEx(NtCurrentProcess(), &addr, &size, MEM_RESERVE, + PAGE_EXECUTE_READWRITE, ext, 1); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); +} + +struct test_stack_size_thread_args +{ + DWORD expect_committed; + DWORD expect_reserved; +}; + +static void DECLSPEC_NOINLINE force_stack_grow(void) +{ + volatile int buffer[0x2000]; + int i; + + for (i = 0; i < ARRAY_SIZE(buffer); i++) buffer[i] = 0xdeadbeef; + (void)buffer[0]; +} + +static void DECLSPEC_NOINLINE force_stack_grow_small(void) +{ + volatile int buffer[0x400]; + int i; + + for (i = 0; i < ARRAY_SIZE(buffer); i++) buffer[i] = 0xdeadbeef; + (void)buffer[0]; +} + +static DWORD WINAPI test_stack_size_thread(void *ptr) +{ + struct test_stack_size_thread_args *args = ptr; + MEMORY_BASIC_INFORMATION mbi; + NTSTATUS status; + SIZE_T size, guard_size; + DWORD committed, reserved; + void *addr; + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + reserved = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->DeallocationStack; + todo_wine ok( committed == args->expect_committed || broken(committed == 0x1000), "unexpected stack committed size %lx, expected %lx\n", committed, args->expect_committed ); + ok( reserved == args->expect_reserved, "unexpected stack reserved size %lx, expected %lx\n", reserved, args->expect_reserved ); + + addr = (char *)NtCurrentTeb()->DeallocationStack; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack ); + ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#lx, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE ); + ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr ); + todo_wine ok( mbi.State == MEM_RESERVE, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_RESERVE ); + todo_wine ok( mbi.Protect == 0, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, 0 ); + ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#lx, expected %#x\n", mbi.Type, MEM_PRIVATE ); + + + force_stack_grow(); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + reserved = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->DeallocationStack; + todo_wine ok( committed == 0x9000, "unexpected stack committed size %lx, expected 9000\n", committed ); + ok( reserved == args->expect_reserved, "unexpected stack reserved size %lx, expected %lx\n", reserved, args->expect_reserved ); + + + /* reserved area shrinks whenever stack grows */ + + addr = (char *)NtCurrentTeb()->DeallocationStack; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack ); + ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#lx, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE ); + ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr ); + todo_wine ok( mbi.State == MEM_RESERVE, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_RESERVE ); + todo_wine ok( mbi.Protect == 0, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, 0 ); + ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#lx, expected %#x\n", mbi.Type, MEM_PRIVATE ); + + guard_size = reserved - committed - mbi.RegionSize; + ok( guard_size == 0x1000 || guard_size == 0x2000 || guard_size == 0x3000, "unexpected guard_size %I64x, expected 1000, 2000 or 3000\n", (UINT64)guard_size ); + + /* the commit area is initially preceded by guard pages */ + + addr = (char *)NtCurrentTeb()->DeallocationStack + mbi.RegionSize; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack ); + ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#lx, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE ); + ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr ); + ok( mbi.RegionSize == guard_size, "unexpected RegionSize %I64x, expected 3000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE|PAGE_GUARD ); + ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#lx, expected %#x\n", mbi.Type, MEM_PRIVATE ); + + addr = (char *)NtCurrentTeb()->Tib.StackLimit; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.AllocationBase == NtCurrentTeb()->DeallocationStack, "unexpected AllocationBase %p, expected %p\n", mbi.AllocationBase, NtCurrentTeb()->DeallocationStack ); + ok( mbi.AllocationProtect == PAGE_READWRITE, "unexpected AllocationProtect %#lx, expected %#x\n", mbi.AllocationProtect, PAGE_READWRITE ); + ok( mbi.BaseAddress == addr, "unexpected BaseAddress %p, expected %p\n", mbi.BaseAddress, addr ); + ok( mbi.RegionSize == committed, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)committed ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + ok( mbi.Type == MEM_PRIVATE, "unexpected Type %#lx, expected %#x\n", mbi.Type, MEM_PRIVATE ); + + return 0; +} + +static DWORD WINAPI test_stack_growth_thread(void *ptr) +{ + MEMORY_BASIC_INFORMATION mbi; + NTSTATUS status; + SIZE_T size, guard_size; + DWORD committed; + void *addr; + DWORD prot; + void *tmp; + + test_stack_size_thread( ptr ); + if (!is_win64) return 0; + + addr = (char *)NtCurrentTeb()->DeallocationStack; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + + guard_size = (char *)NtCurrentTeb()->Tib.StackLimit - (char *)NtCurrentTeb()->DeallocationStack - mbi.RegionSize; + ok( guard_size == 0x1000 || guard_size == 0x2000 || guard_size == 0x3000, "unexpected guard_size %I64x, expected 1000, 2000 or 3000\n", (UINT64)guard_size ); + + /* setting a guard page shrinks stack automatically */ + + addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000; + size = 0x1000; + status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD ); + ok( !status, "NtAllocateVirtualMemory returned %08lx\n", status ); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x6000, "unexpected stack committed size %lx, expected 6000\n", committed ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)addr - 0x2000, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.RegionSize == 0x2000, "unexpected RegionSize %I64x, expected 2000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#lx, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) ); + + addr = (char *)NtCurrentTeb()->Tib.StackLimit; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + todo_wine ok( mbi.RegionSize == 0x6000, "unexpected RegionSize %I64x, expected 6000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + + /* guard pages are restored as the stack grows back */ + + addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x4000; + tmp = (char *)addr - guard_size - 0x1000; + size = 0x1000; + status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD ); + ok( !status, "NtAllocateVirtualMemory returned %08lx\n", status ); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x1000, "unexpected stack committed size %lx, expected 1000\n", committed ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), tmp, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + todo_wine ok( mbi.RegionSize == guard_size + 0x1000, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)(guard_size + 0x1000) ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + force_stack_grow_small(); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x2000, "unexpected stack committed size %lx, expected 2000\n", committed ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), tmp, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)tmp + 0x1000, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.RegionSize == guard_size, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)guard_size ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#lx, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) ); + + + /* forcing stack limit over guard pages still shrinks the stack on page fault */ + + addr = (char *)tmp + guard_size + 0x1000; + size = 0x1000; + status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD ); + ok( !status, "NtAllocateVirtualMemory returned %08lx\n", status ); + + NtCurrentTeb()->Tib.StackLimit = (char *)tmp; + + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)tmp + 0x1000, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + todo_wine ok( mbi.RegionSize == guard_size + 0x1000, "unexpected RegionSize %I64x, expected %I64x\n", (UINT64)mbi.RegionSize, (UINT64)(guard_size + 0x1000) ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#lx, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) ); + + force_stack_grow_small(); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x2000, "unexpected stack committed size %lx, expected 2000\n", committed ); + + + /* it works with NtProtectVirtualMemory as well */ + + force_stack_grow(); + + addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000; + size = 0x1000; + status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE | PAGE_GUARD, &prot ); + ok( !status, "NtProtectVirtualMemory returned %08lx\n", status ); + todo_wine ok( prot == PAGE_READWRITE, "unexpected prot %#lx, expected %#x\n", prot, PAGE_READWRITE ); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x6000, "unexpected stack committed size %lx, expected 6000\n", committed ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)addr - 0x2000, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + todo_wine ok( mbi.RegionSize == 0x2000, "unexpected RegionSize %I64x, expected 2000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + ok( mbi.RegionSize == 0x1000, "unexpected RegionSize %I64x, expected 1000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + ok( mbi.Protect == (PAGE_READWRITE|PAGE_GUARD), "unexpected Protect %#lx, expected %#x\n", mbi.Protect, (PAGE_READWRITE|PAGE_GUARD) ); + + addr = (char *)NtCurrentTeb()->Tib.StackLimit; + status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &mbi, sizeof(mbi), &size ); + ok( !status, "NtQueryVirtualMemory returned %08lx\n", status ); + todo_wine ok( mbi.RegionSize == 0x6000, "unexpected RegionSize %I64x, expected 6000\n", (UINT64)mbi.RegionSize ); + ok( mbi.State == MEM_COMMIT, "unexpected State %#lx, expected %#x\n", mbi.State, MEM_COMMIT ); + todo_wine ok( mbi.Protect == PAGE_READWRITE, "unexpected Protect %#lx, expected %#x\n", mbi.Protect, PAGE_READWRITE ); + + + /* clearing the guard pages doesn't change StackLimit back */ + + force_stack_grow(); + + addr = (char *)NtCurrentTeb()->Tib.StackLimit + 0x2000; + size = 0x1000; + status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE | PAGE_GUARD, &prot ); + ok( !status, "NtProtectVirtualMemory returned %08lx\n", status ); + todo_wine ok( prot == PAGE_READWRITE, "unexpected prot %#lx, expected %#x\n", prot, PAGE_READWRITE ); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x6000, "unexpected stack committed size %lx, expected 6000\n", committed ); + + status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &prot ); + ok( !status, "NtProtectVirtualMemory returned %08lx\n", status ); + ok( prot == (PAGE_READWRITE | PAGE_GUARD), "unexpected prot %#lx, expected %#x\n", prot, (PAGE_READWRITE | PAGE_GUARD) ); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x6000, "unexpected stack committed size %lx, expected 6000\n", committed ); + + /* and as we messed with it and it now doesn't fault, it doesn't grow back either */ + + force_stack_grow(); + + committed = (char *)NtCurrentTeb()->Tib.StackBase - (char *)NtCurrentTeb()->Tib.StackLimit; + todo_wine ok( committed == 0x6000, "unexpected stack committed size %lx, expected 6000\n", committed ); + + ExitThread(0); +} + +static DWORD WINAPI test_stack_size_dummy_thread(void *ptr) +{ + return 0; +} + +static void test_RtlCreateUserStack(void) +{ + IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); + struct test_stack_size_thread_args args; + SIZE_T default_commit = nt->OptionalHeader.SizeOfStackCommit; + SIZE_T default_reserve = nt->OptionalHeader.SizeOfStackReserve; + INITIAL_TEB stack = {0}; + unsigned int i; + NTSTATUS ret; + HANDLE thread; + CLIENT_ID id; + + struct + { + SIZE_T commit, reserve, commit_align, reserve_align, expect_commit, expect_reserve; + } + tests[] = + { + { 0, 0, 1, 1, default_commit, default_reserve}, + { 0x2000, 0, 1, 1, 0x2000, default_reserve}, + { 0x4000, 0, 1, 1, 0x4000, default_reserve}, + { 0, 0x200000, 1, 1, default_commit, 0x200000}, + { 0x4000, 0x200000, 1, 1, 0x4000, 0x200000}, + {0x100000, 0x100000, 1, 1, 0x100000, 0x100000}, + { 0x20000, 0x20000, 1, 1, 0x20000, 0x100000}, + + { 0, 0x110000, 1, 1, default_commit, 0x110000}, + { 0, 0x110000, 1, 0x40000, default_commit, 0x140000}, + { 0, 0x140000, 1, 0x40000, default_commit, 0x140000}, + { 0x11000, 0x140000, 1, 0x40000, 0x11000, 0x140000}, + { 0x11000, 0x140000, 0x4000, 0x40000, 0x14000, 0x140000}, + { 0, 0, 0x4000, 0x400000, + (default_commit + 0x3fff) & ~0x3fff, + (default_reserve + 0x3fffff) & ~0x3fffff}, + }; + + if (!pRtlCreateUserStack) + { + win_skip("RtlCreateUserStack() is missing\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + memset(&stack, 0xcc, sizeof(stack)); + ret = pRtlCreateUserStack(tests[i].commit, tests[i].reserve, 0, + tests[i].commit_align, tests[i].reserve_align, &stack); + ok(!ret, "%u: got status %#lx\n", i, ret); + ok(!stack.OldStackBase, "%u: got OldStackBase %p\n", i, stack.OldStackBase); + ok(!stack.OldStackLimit, "%u: got OldStackLimit %p\n", i, stack.OldStackLimit); + ok(!((ULONG_PTR)stack.DeallocationStack & (page_size - 1)), + "%u: got unaligned memory %p\n", i, stack.DeallocationStack); + ok((ULONG_PTR)stack.StackBase - (ULONG_PTR)stack.DeallocationStack == tests[i].expect_reserve, + "%u: got reserve %#Ix\n", i, (ULONG_PTR)stack.StackBase - (ULONG_PTR)stack.DeallocationStack); + todo_wine ok((ULONG_PTR)stack.StackBase - (ULONG_PTR)stack.StackLimit == tests[i].expect_commit, + "%u: got commit %#Ix\n", i, (ULONG_PTR)stack.StackBase - (ULONG_PTR)stack.StackLimit); + pRtlFreeUserStack(stack.DeallocationStack); + } + + ret = pRtlCreateUserStack(0x11000, 0x110000, 0, 1, 0, &stack); + ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + + ret = pRtlCreateUserStack(0x11000, 0x110000, 0, 0, 1, &stack); + ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + + args.expect_committed = 0x4000; + args.expect_reserved = default_reserve; + thread = CreateThread(NULL, 0x3f00, test_stack_growth_thread, &args, 0, NULL); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + args.expect_committed = default_commit < 0x2000 ? 0x2000 : default_commit; + args.expect_reserved = 0x400000; + thread = CreateThread(NULL, 0x3ff000, test_stack_growth_thread, &args, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + if (is_win64) + { + thread = CreateThread(NULL, 0x80000000, test_stack_size_dummy_thread, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + ok(thread != NULL, "CreateThread with huge stack failed\n"); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + } + + args.expect_committed = default_commit < 0x2000 ? 0x2000 : default_commit; + args.expect_reserved = 0x100000; + for (i = 0; i < 32; i++) + { + ULONG mask = ~0u >> i; + NTSTATUS expect_ret = STATUS_SUCCESS; + + if (i == 12) expect_ret = STATUS_CONFLICTING_ADDRESSES; + else if (i >= 13) expect_ret = STATUS_INVALID_PARAMETER; + ret = pRtlCreateUserStack( args.expect_committed, args.expect_reserved, i, 0x1000, 0x1000, &stack ); + ok( ret == expect_ret || ret == STATUS_NO_MEMORY || + (ret == STATUS_INVALID_PARAMETER_3 && expect_ret == STATUS_INVALID_PARAMETER) || + broken( i == 1 && ret == STATUS_INVALID_PARAMETER_3 ), /* win7 */ + "%u: got %lx / %lx\n", i, ret, expect_ret ); + if (!ret) pRtlFreeUserStack( stack.DeallocationStack ); + ret = pRtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, i, + args.expect_reserved, args.expect_committed, + (void *)test_stack_size_thread, &args, &thread, &id ); + ok( ret == expect_ret || ret == STATUS_NO_MEMORY || + (ret == STATUS_INVALID_PARAMETER_3 && expect_ret == STATUS_INVALID_PARAMETER) || + broken( i == 1 && ret == STATUS_INVALID_PARAMETER_3 ), /* win7 */ + "%u: got %lx / %lx\n", i, ret, expect_ret ); + if (!ret) + { + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + } + + if (mask <= 31) continue; + if (!is_win64 && !is_wow64) expect_ret = STATUS_INVALID_PARAMETER_3; + ret = pRtlCreateUserStack( args.expect_committed, args.expect_reserved, mask, 0x1000, 0x1000, &stack ); + ok( ret == expect_ret || ret == STATUS_NO_MEMORY || + (ret == STATUS_INVALID_PARAMETER_3 && expect_ret == STATUS_INVALID_PARAMETER), + "%08lx: got %lx / %lx\n", mask, ret, expect_ret ); + if (!ret) pRtlFreeUserStack( stack.DeallocationStack ); + ret = pRtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, mask, + args.expect_reserved, args.expect_committed, + (void *)test_stack_size_thread, &args, &thread, &id ); + ok( ret == expect_ret || ret == STATUS_NO_MEMORY || + (ret == STATUS_INVALID_PARAMETER_3 && expect_ret == STATUS_INVALID_PARAMETER), + "%08lx: got %lx / %lx\n", mask, ret, expect_ret ); + if (!ret) + { + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + } + } +} + +static void test_NtMapViewOfSection(void) +{ + static const char testfile[] = "testfile.xxx"; + static const char data[] = "test data for NtMapViewOfSection"; + char buffer[sizeof(data)]; + HANDLE file, mapping, process; + void *ptr, *ptr2; + BOOL ret; + DWORD status, written; + SIZE_T size, result; + LARGE_INTEGER offset; + ULONG_PTR zero_bits; + + if (!pIsWow64Process || !pIsWow64Process(NtCurrentProcess(), &is_wow64)) is_wow64 = FALSE; + + file = CreateFileA(testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to create test file\n"); + WriteFile(file, data, sizeof(data), &written, NULL); + SetFilePointer(file, 4096, NULL, FILE_BEGIN); + SetEndOfFile(file); + + /* read/write mapping */ + + mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, 4096, NULL); + ok(mapping != 0, "CreateFileMapping failed\n"); + + process = create_target_process("sleep"); + ok(process != NULL, "Can't start process\n"); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NULL, &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_INVALID_HANDLE, "NtMapViewOfSection returned %08lx\n", status); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "NtMapViewOfSection returned %08lx\n", status); + ok(!((ULONG_PTR)ptr & 0xffff), "returned memory %p is not aligned to 64k\n", ptr); + + ret = ReadProcessMemory(process, ptr, buffer, sizeof(buffer), &result); + ok(ret, "ReadProcessMemory failed\n"); + ok(result == sizeof(buffer), "ReadProcessMemory didn't read all data (%Ix)\n", result); + ok(!memcmp(buffer, data, sizeof(buffer)), "Wrong data read\n"); + + /* 1 zero bits should zero 63-31 upper bits */ + ptr2 = NULL; + size = 0; + zero_bits = 1; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, zero_bits, 0, &offset, &size, 1, MEM_TOP_DOWN, PAGE_READWRITE); + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY, + "NtMapViewOfSection returned %08lx\n", status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)ptr2 >> (32 - zero_bits)) == 0, + "NtMapViewOfSection returned address: %p\n", ptr2); + + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + } + + for (zero_bits = 2; zero_bits <= 20; zero_bits++) + { + ptr2 = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, zero_bits, 0, &offset, &size, 1, MEM_TOP_DOWN, PAGE_READWRITE); + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY, + "NtMapViewOfSection with %d zero_bits returned %08lx\n", (int)zero_bits, status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)ptr2 >> (32 - zero_bits)) == 0, + "NtMapViewOfSection with %d zero_bits returned address %p\n", (int)zero_bits, ptr2); + + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + } + } + + /* 21 zero bits never succeeds */ + ptr2 = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 21, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_NO_MEMORY || status == STATUS_INVALID_PARAMETER, + "NtMapViewOfSection returned %08lx\n", status); + + /* 22 zero bits is invalid */ + ptr2 = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 22, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_4 || status == STATUS_INVALID_PARAMETER, + "NtMapViewOfSection returned %08lx\n", status); + + /* zero bits > 31 should be considered as a leading zeroes bitmask on 64bit and WoW64 */ + ptr2 = NULL; + size = 0; + zero_bits = 0x1aaaaaaa; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, zero_bits, 0, &offset, &size, 1, MEM_TOP_DOWN, PAGE_READWRITE); + + if (!is_win64 && !is_wow64) + { + ok(status == STATUS_INVALID_PARAMETER_4, "NtMapViewOfSection returned %08lx\n", status); + } + else + { + ok(status == STATUS_SUCCESS || status == STATUS_NO_MEMORY, + "NtMapViewOfSection returned %08lx\n", status); + if (status == STATUS_SUCCESS) + { + ok(((UINT_PTR)ptr2 & ~get_zero_bits_mask(zero_bits)) == 0 && + ((UINT_PTR)ptr2 & ~zero_bits) != 0, /* only the leading zeroes matter */ + "NtMapViewOfSection returned address %p\n", ptr2); + + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + } + } + + /* mapping at the same page conflicts */ + ptr2 = ptr; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %08lx\n", status); + + /* offset has to be aligned */ + ptr2 = ptr; + size = 0; + offset.QuadPart = 1; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %08lx\n", status); + + /* ptr has to be aligned */ + ptr2 = (char *)ptr + 42; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %08lx\n", status); + + /* still not 64k aligned */ + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %08lx\n", status); + + /* when an address is passed, it has to satisfy the provided number of zero bits */ + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + zero_bits = get_zero_bits(((UINT_PTR)ptr2) >> 1); + status = NtMapViewOfSection(mapping, process, &ptr2, zero_bits, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_INVALID_PARAMETER_4 || status == STATUS_INVALID_PARAMETER, + "NtMapViewOfSection returned %08lx\n", status); + + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + zero_bits = get_zero_bits((UINT_PTR)ptr2); + status = NtMapViewOfSection(mapping, process, &ptr2, zero_bits, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_MAPPED_ALIGNMENT, "NtMapViewOfSection returned %08lx\n", status); + + if (!is_win64 && !is_wow64) + { + /* new memory region conflicts with previous mapping */ + ptr2 = ptr; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + ok(status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %08lx\n", status); + + ptr2 = (char *)ptr + 42; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + ok(status == STATUS_CONFLICTING_ADDRESSES, "NtMapViewOfSection returned %08lx\n", status); + + /* in contrary to regular NtMapViewOfSection, only 4kb align is enforced */ + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "NtMapViewOfSection returned %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x1000, + "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + /* the address is rounded down if not on a page boundary */ + ptr2 = (char *)ptr + 0x1001; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "NtMapViewOfSection returned %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x1000, + "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + ptr2 = (char *)ptr + 0x2000; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "NtMapViewOfSection returned %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x2000, + "expected address %p, got %p\n", (char *)ptr + 0x2000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + } + else + { + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, process, &ptr2, 0, 0, &offset, + &size, 1, AT_ROUND_TO_PAGE, PAGE_READWRITE); + todo_wine + ok(status == STATUS_INVALID_PARAMETER_9 || status == STATUS_INVALID_PARAMETER, + "NtMapViewOfSection returned %08lx\n", status); + } + + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + NtClose(mapping); + + CloseHandle(file); + DeleteFileA(testfile); + + /* test zero_bits > 31 with a 64-bit DLL file image mapping */ + if (is_win64) + { + file = CreateFileA("c:\\windows\\system32\\version.dll", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n"); + + mapping = CreateFileMappingA(file, NULL, PAGE_READONLY|SEC_IMAGE, 0, 0, NULL); + ok(mapping != 0, "CreateFileMapping failed\n"); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + zero_bits = 0x7fffffff; + status = NtMapViewOfSection(mapping, process, &ptr, zero_bits, 0, &offset, &size, 1, 0, PAGE_READONLY); + + ok(status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, "NtMapViewOfSection returned %08lx\n", status); + ok(!((ULONG_PTR)ptr & 0xffff), "returned memory %p is not aligned to 64k\n", ptr); + ok(((UINT_PTR)ptr & ~get_zero_bits_mask(zero_bits)) == 0, "NtMapViewOfSection returned address %p\n", ptr); + + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + NtClose(mapping); + CloseHandle(file); + } + + TerminateProcess(process, 0); + CloseHandle(process); +} + +static void test_NtMapViewOfSectionEx(void) +{ + static const char testfile[] = "testfile.xxx"; + static const char data[] = "test data for NtMapViewOfSectionEx"; + char buffer[sizeof(data)]; + MEM_EXTENDED_PARAMETER ext[2]; + MEM_ADDRESS_REQUIREMENTS a; + SYSTEM_INFO si; + HANDLE file, mapping, process; + DWORD status, written; + SIZE_T size, result; + LARGE_INTEGER offset; + void *ptr, *ptr2; + BOOL ret; + + if (!pNtMapViewOfSectionEx) + { + win_skip("NtMapViewOfSectionEx() is not supported.\n"); + return; + } + + if (!pIsWow64Process || !pIsWow64Process(NtCurrentProcess(), &is_wow64)) is_wow64 = FALSE; + GetSystemInfo(&si); + + file = CreateFileA(testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to create test file\n"); + WriteFile(file, data, sizeof(data), &written, NULL); + SetFilePointer(file, 0x40000, NULL, FILE_BEGIN); + SetEndOfFile(file); + + /* read/write mapping */ + + mapping = CreateFileMappingA(file, NULL, PAGE_READWRITE, 0, 0x40000, NULL); + ok(mapping != 0, "CreateFileMapping failed\n"); + + process = create_target_process("sleep"); + ok(process != NULL, "Can't start process\n"); + + ptr = NULL; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, NULL, &ptr, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %08lx\n", status); + + ptr = NULL; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx\n", status); + ok(!((ULONG_PTR)ptr & 0xffff), "returned memory %p is not aligned to 64k\n", ptr); + + ret = ReadProcessMemory(process, ptr, buffer, sizeof(buffer), &result); + ok(ret, "ReadProcessMemory failed\n"); + ok(result == sizeof(buffer), "ReadProcessMemory didn't read all data (%Ix)\n", result); + ok(!memcmp(buffer, data, sizeof(buffer)), "Wrong data read\n"); + + /* mapping at the same page conflicts */ + ptr2 = ptr; + size = 0; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx\n", status); + + /* offset has to be aligned */ + ptr2 = ptr; + size = 0; + offset.QuadPart = 1; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_MAPPED_ALIGNMENT, "Unexpected status %08lx\n", status); + + /* ptr has to be aligned */ + ptr2 = (char *)ptr + 42; + size = 0; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_MAPPED_ALIGNMENT, "Unexpected status %08lx\n", status); + + /* still not 64k aligned */ + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, 0, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_MAPPED_ALIGNMENT, "Unexpected status %08lx\n", status); + + if (!is_win64 && !is_wow64) + { + /* new memory region conflicts with previous mapping */ + ptr2 = ptr; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx\n", status); + + ptr2 = (char *)ptr + 42; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_CONFLICTING_ADDRESSES, "Unexpected status %08lx\n", status); + + /* in contrary to regular NtMapViewOfSection, only 4kb align is enforced */ + ptr2 = (char *)ptr + 0x1000; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x1000, + "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + /* the address is rounded down if not on a page boundary */ + ptr2 = (char *)ptr + 0x1001; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x1000, + "expected address %p, got %p\n", (char *)ptr + 0x1000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + ptr2 = (char *)ptr + 0x2000; + size = 0x1000; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx\n", status); + ok((char *)ptr2 == (char *)ptr + 0x2000, + "expected address %p, got %p\n", (char *)ptr + 0x2000, ptr2); + status = NtUnmapViewOfSection(process, ptr2); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + } + else + { + ptr2 = (char *)ptr + 0x1000; + size = 0; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx(mapping, process, &ptr2, &offset, &size, AT_ROUND_TO_PAGE, PAGE_READWRITE, NULL, 0); + todo_wine + ok(status == STATUS_INVALID_PARAMETER_9 || status == STATUS_INVALID_PARAMETER, + "NtMapViewOfSection returned %08lx\n", status); + } + + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + /* extended parameters */ + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = 0; + size = 0x1000; + ptr = NULL; + offset.QuadPart = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = MemExtendedParameterMax; + size = 0x1000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + memset(&a, 0, sizeof(a)); + ext[0].Type = MemExtendedParameterAddressRequirements; + ext[0].Pointer = &a; + size = 0x1000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(!status, "Unexpected status %08lx.\n", status); + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + ext[1] = ext[0]; + size = 0x1000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 2 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.LowestStartingAddress = NULL; + a.Alignment = 0; + a.HighestEndingAddress = (void *)(0x20001000 + 1); + size = 0x10000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 2); + size = 0x10000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (void *)(0x20000800 - 1); + size = 0x10000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress + 0x1000; + size = 0x10000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = (char *)si.lpMaximumApplicationAddress; + size = 0x10000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(!status, "Unexpected status %08lx.\n", status); + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + a.HighestEndingAddress = (void *)(0x20001000 - 1); + size = 0x40000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(!status, "Unexpected status %08lx.\n", status); + ok(!((ULONG_PTR)ptr & 0xffff), "Unexpected addr %p.\n", ptr); + ok((ULONG_PTR)ptr + size <= 0x20001000, "Unexpected addr %p.\n", ptr); + status = NtUnmapViewOfSection(process, ptr); + ok(status == STATUS_SUCCESS, "NtUnmapViewOfSection returned %08lx\n", status); + + size = 0x40000; + a.HighestEndingAddress = (void *)(0x20001000 - 1); + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + a.HighestEndingAddress = NULL; + a.Alignment = 0x30000; + size = 0x1000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %08lx.\n", status); + + for (a.Alignment = 1; a.Alignment; a.Alignment *= 2) + { + size = 0x1000; + ptr = NULL; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READWRITE, ext, 1 ); + ok(status == STATUS_INVALID_PARAMETER, "Align %Ix unexpected status %08lx.\n", a.Alignment, status); + } + + NtClose(mapping); + + CloseHandle(file); + DeleteFileA(testfile); + + file = CreateFileA( "c:\\windows\\system32\\version.dll", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + memset(&ext, 0, sizeof(ext)); + ext[0].Type = MemExtendedParameterImageMachine; + ext[0].ULong = 0; + ptr = NULL; + size = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, ext, 1 ); + if (status != STATUS_INVALID_PARAMETER) + { + ok(status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, "NtMapViewOfSection returned %08lx\n", status); + NtUnmapViewOfSection(process, ptr); + + ext[1].Type = MemExtendedParameterImageMachine; + ext[1].ULong = 0; + ptr = NULL; + size = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, ext, 2 ); + ok(status == STATUS_INVALID_PARAMETER, "NtMapViewOfSection returned %08lx\n", status); + + ext[0].ULong = IMAGE_FILE_MACHINE_R3000; + ext[1].ULong = IMAGE_FILE_MACHINE_R4000; + ptr = NULL; + size = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, ext, 2 ); + ok(status == STATUS_INVALID_PARAMETER, "NtMapViewOfSection returned %08lx\n", status); + + ptr = NULL; + size = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, ext, 1 ); + ok(status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status); + } + else win_skip( "MemExtendedParameterImageMachine not supported\n" ); + + NtClose(mapping); + CloseHandle(file); + + TerminateProcess(process, 0); + CloseHandle(process); +} + +#define SUPPORTED_XSTATE_FEATURES ((1 << XSTATE_LEGACY_FLOATING_POINT) | (1 << XSTATE_LEGACY_SSE) | (1 << XSTATE_AVX)) + +static void test_user_shared_data(void) +{ + struct old_xstate_configuration + { + ULONG64 EnabledFeatures; + ULONG Size; + ULONG OptimizedSave:1; + ULONG CompactionEnabled:1; + XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]; + }; + + static const ULONG feature_offsets[] = + { + 0, + 160, /*offsetof(XMM_SAVE_AREA32, XmmRegisters)*/ + 512 /* sizeof(XMM_SAVE_AREA32) */ + offsetof(XSTATE, YmmContext), + }; + static const ULONG feature_sizes[] = + { + 160, + 256, /*sizeof(M128A) * 16 */ + sizeof(YMMCONTEXT), + }; + const KSHARED_USER_DATA *user_shared_data = (void *)0x7ffe0000; + XSTATE_CONFIGURATION xstate = user_shared_data->XState; + ULONG64 feature_mask; + unsigned int i; + + ok(user_shared_data->NumberOfPhysicalPages == sbi.MmNumberOfPhysicalPages, + "Got number of physical pages %#lx, expected %#lx.\n", + user_shared_data->NumberOfPhysicalPages, sbi.MmNumberOfPhysicalPages); + +#if defined(__i386__) || defined(__x86_64__) + ok(user_shared_data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE] /* Supported since Pentium CPUs. */, + "_RDTSC not available.\n"); +#endif + ok(user_shared_data->ActiveProcessorCount == NtCurrentTeb()->Peb->NumberOfProcessors + || broken(!user_shared_data->ActiveProcessorCount) /* before Win7 */, + "Got unexpected ActiveProcessorCount %lu.\n", user_shared_data->ActiveProcessorCount); + ok(user_shared_data->ActiveGroupCount == 1 + || broken(!user_shared_data->ActiveGroupCount) /* before Win7 */, + "Got unexpected ActiveGroupCount %u.\n", user_shared_data->ActiveGroupCount); + + if (!pRtlGetEnabledExtendedFeatures) + { + win_skip("RtlGetEnabledExtendedFeatures is not available.\n"); + return; + } + + feature_mask = pRtlGetEnabledExtendedFeatures(~(ULONG64)0); + if (!feature_mask) + { + skip("XState features are not available.\n"); + return; + } + + if (!xstate.EnabledFeatures) + { + struct old_xstate_configuration *xs_old + = (struct old_xstate_configuration *)((char *)user_shared_data + 0x3e0); + + ok(feature_mask == xs_old->EnabledFeatures, "Got unexpected xs_old->EnabledFeatures %s.\n", + wine_dbgstr_longlong(xs_old->EnabledFeatures)); + win_skip("Old structure layout.\n"); + return; + } + + trace("XState EnabledFeatures %#I64x, EnabledSupervisorFeatures %#I64x, EnabledVolatileFeatures %I64x.\n", + xstate.EnabledFeatures, xstate.EnabledSupervisorFeatures, xstate.EnabledVolatileFeatures); + feature_mask = pRtlGetEnabledExtendedFeatures(0); + ok(!feature_mask, "Got unexpected feature_mask %s.\n", wine_dbgstr_longlong(feature_mask)); + feature_mask = pRtlGetEnabledExtendedFeatures(~(ULONG64)0); + ok(feature_mask == (xstate.EnabledFeatures | xstate.EnabledSupervisorFeatures), "Got unexpected feature_mask %s.\n", + wine_dbgstr_longlong(feature_mask)); + feature_mask = pGetEnabledXStateFeatures(); + ok(feature_mask == (xstate.EnabledFeatures | xstate.EnabledSupervisorFeatures), "Got unexpected feature_mask %s.\n", + wine_dbgstr_longlong(feature_mask)); + ok((xstate.EnabledFeatures & SUPPORTED_XSTATE_FEATURES) == SUPPORTED_XSTATE_FEATURES, + "Got unexpected EnabledFeatures %s.\n", wine_dbgstr_longlong(xstate.EnabledFeatures)); + ok((xstate.EnabledVolatileFeatures & SUPPORTED_XSTATE_FEATURES) == (xstate.EnabledFeatures & SUPPORTED_XSTATE_FEATURES), + "Got unexpected EnabledVolatileFeatures %s.\n", wine_dbgstr_longlong(xstate.EnabledVolatileFeatures)); + ok(xstate.Size >= 512 + sizeof(XSTATE), "Got unexpected Size %lu.\n", xstate.Size); + if (xstate.CompactionEnabled) + ok(xstate.OptimizedSave, "Got zero OptimizedSave with compaction enabled.\n"); + ok(!xstate.AlignedFeatures, "Got unexpected AlignedFeatures %s.\n", + wine_dbgstr_longlong(xstate.AlignedFeatures)); + ok(xstate.AllFeatureSize >= 512 + sizeof(XSTATE) + || !xstate.AllFeatureSize /* win8 on CPUs without XSAVEC */, + "Got unexpected AllFeatureSize %lu.\n", xstate.AllFeatureSize); + + for (i = 0; i < ARRAY_SIZE(feature_sizes); ++i) + { + ok(xstate.AllFeatures[i] == feature_sizes[i] + || !xstate.AllFeatures[i] /* win8+ on CPUs without XSAVEC */, + "Got unexpected AllFeatures[%u] %lu, expected %lu.\n", i, + xstate.AllFeatures[i], feature_sizes[i]); + ok(xstate.Features[i].Size == feature_sizes[i], "Got unexpected Features[%u].Size %lu, expected %lu.\n", i, + xstate.Features[i].Size, feature_sizes[i]); + ok(xstate.Features[i].Offset == feature_offsets[i], "Got unexpected Features[%u].Offset %lu, expected %lu.\n", + i, xstate.Features[i].Offset, feature_offsets[i]); + } +} + +static void perform_relocations( void *module, INT_PTR delta ) +{ + IMAGE_NT_HEADERS *nt; + IMAGE_BASE_RELOCATION *rel, *end; + const IMAGE_DATA_DIRECTORY *relocs; + const IMAGE_SECTION_HEADER *sec; + ULONG protect_old[96], i; + + nt = RtlImageNtHeader( module ); + relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (!relocs->VirtualAddress || !relocs->Size) return; + sec = IMAGE_FIRST_SECTION( nt ); + for (i = 0; i < nt->FileHeader.NumberOfSections; i++) + { + void *addr = (char *)module + sec[i].VirtualAddress; + SIZE_T size = sec[i].SizeOfRawData; + NtProtectVirtualMemory( NtCurrentProcess(), &addr, + &size, PAGE_READWRITE, &protect_old[i] ); + } + rel = (IMAGE_BASE_RELOCATION *)((char *)module + relocs->VirtualAddress); + end = (IMAGE_BASE_RELOCATION *)((char *)rel + relocs->Size); + while (rel && rel < end - 1 && rel->SizeOfBlock) + rel = LdrProcessRelocationBlock( (char *)module + rel->VirtualAddress, + (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT), + (USHORT *)(rel + 1), delta ); + for (i = 0; i < nt->FileHeader.NumberOfSections; i++) + { + void *addr = (char *)module + sec[i].VirtualAddress; + SIZE_T size = sec[i].SizeOfRawData; + NtProtectVirtualMemory( NtCurrentProcess(), &addr, + &size, protect_old[i], &protect_old[i] ); + } +} + + +static void test_syscalls(void) +{ + HMODULE module = GetModuleHandleW( L"ntdll.dll" ); + HANDLE handle; + NTSTATUS status; + NTSTATUS (WINAPI *pNtClose)(HANDLE); + WCHAR path[MAX_PATH]; + HANDLE file, mapping; + INT_PTR delta; + void *ptr; + + /* initial image */ + pNtClose = (void *)GetProcAddress( module, "NtClose" ); + handle = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( handle != 0, "CreateEventWfailed %lu\n", GetLastError() ); + status = pNtClose( handle ); + ok( !status, "NtClose failed %lx\n", status ); + status = pNtClose( handle ); + ok( status == STATUS_INVALID_HANDLE, "NtClose failed %lx\n", status ); + + /* syscall thunk copy */ + ptr = VirtualAlloc( NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ok( ptr != NULL, "VirtualAlloc failed\n" ); + memcpy( ptr, pNtClose, 32 ); + pNtClose = ptr; + handle = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( handle != 0, "CreateEventWfailed %lu\n", GetLastError() ); + status = pNtClose( handle ); + ok( !status, "NtClose failed %lx\n", status ); + status = pNtClose( handle ); + ok( status == STATUS_INVALID_HANDLE, "NtClose failed %lx\n", status ); + VirtualFree( ptr, 0, MEM_FREE ); + + /* new mapping */ + GetModuleFileNameW( module, path, MAX_PATH ); + file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "can't open %s: %lu\n", wine_dbgstr_w(path), GetLastError() ); + mapping = CreateFileMappingW( file, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0, NULL ); + ok( mapping != NULL, "CreateFileMappingW failed err %lu\n", GetLastError() ); + ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 ); + ok( ptr != NULL, "MapViewOfFile failed err %lu\n", GetLastError() ); + CloseHandle( mapping ); + delta = (char *)ptr - (char *)module; + + if (memcmp( ptr, module, 0x1000 )) + { + skip( "modules are not identical (non-PE build?)\n" ); + UnmapViewOfFile( ptr ); + CloseHandle( file ); + return; + } + perform_relocations( ptr, delta ); + pNtClose = (void *)GetProcAddress( module, "NtClose" ); + + if (pRtlFindExportedRoutineByName) + { + void *func = pRtlFindExportedRoutineByName( module, "NtClose" ); + ok( func == (void *)pNtClose, "wrong ptr %p / %p\n", func, pNtClose ); + func = pRtlFindExportedRoutineByName( ptr, "NtClose" ); + ok( (char *)func - (char *)pNtClose == delta, "wrong ptr %p / %p\n", func, pNtClose ); + } + else win_skip( "RtlFindExportedRoutineByName not supported\n" ); + + if (!memcmp( pNtClose, (char *)pNtClose + delta, 32 )) + { + pNtClose = (void *)((char *)pNtClose + delta); + handle = CreateEventW( NULL, FALSE, FALSE, NULL ); + ok( handle != 0, "CreateEventWfailed %lu\n", GetLastError() ); + status = pNtClose( handle ); + ok( !status, "NtClose failed %lx\n", status ); + status = pNtClose( handle ); + ok( status == STATUS_INVALID_HANDLE, "NtClose failed %lx\n", status ); + } + else + { +#ifdef __i386__ + NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, void *, ULONG, ULONG *); + PROCESS_BASIC_INFORMATION pbi; + void *exec_mem, *va_ptr; + ULONG size; + BOOL ret; + + exec_mem = VirtualAlloc( NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ok( !!exec_mem, "got NULL.\n" ); + + /* NtQueryInformationProcess is special. */ + pNtQueryInformationProcess = (void *)GetProcAddress( module, "NtQueryInformationProcess" ); + va_ptr = RtlImageRvaToVa( RtlImageNtHeader(module), module, + (char *)pNtQueryInformationProcess - (char *)module, NULL ); + ok( !!va_ptr, "offset not found %p / %p\n", pNtQueryInformationProcess, module ); + ret = SetFilePointer( file, (char *)va_ptr - (char *)module, NULL, FILE_BEGIN ); + ok( ret, "got %d, err %lu.\n", ret, GetLastError() ); + ret = ReadFile( file, exec_mem, 32, NULL, NULL ); + ok( ret, "got %d, err %lu.\n", ret, GetLastError() ); + if (!memcmp( exec_mem, pNtQueryInformationProcess, 5 )) + { + pNtQueryInformationProcess = exec_mem; + /* The thunk still works without relocation. */ + status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &size ); + ok( !status, "got %#lx.\n", status ); + ok( size == sizeof(pbi), "got %lu.\n", size ); + ok( pbi.PebBaseAddress == NtCurrentTeb()->Peb, "got %p, %p.\n", pbi.PebBaseAddress, NtCurrentTeb()->Peb ); + } + else + ok( 0, "file on disk doesn't match syscall %x / %x\n", + *(UINT *)pNtQueryInformationProcess, *(UINT *)exec_mem ); + + VirtualFree( exec_mem, 0, MEM_RELEASE ); +#elif defined __x86_64__ + ok( 0, "syscall thunk relocated\n" ); +#else + skip( "syscall thunk relocated\n" ); +#endif + } + CloseHandle( file ); + UnmapViewOfFile( ptr ); +} + +static void test_NtFreeVirtualMemory(void) +{ + void *addr1, *addr; + NTSTATUS status; + SIZE_T size; + + size = 0x10000; + addr1 = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr1, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0; + status = NtFreeVirtualMemory(NULL, &addr1, &size, MEM_RELEASE); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %08lx.\n", status); + + addr = (char *)addr1 + 0x1000; + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_FREE_VM_NOT_AT_BASE, "Unexpected status %08lx.\n", status); + + size = 0x11000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + addr = (char *)addr1 + 0x1001; + size = 0xffff; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + ok(size == 0xffff, "Unexpected size %p.\n", (void *)size); + ok(addr == (char *)addr1 + 0x1001, "Got addr %p, addr1 %p.\n", addr, addr1); + + size = 0xfff; + addr = (char *)addr1 + 0x1001; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *(volatile char *)addr1 = 1; + *((volatile char *)addr1 + 0x2000) = 1; + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); + ok(addr == (char *)addr1 + 0x1000, "Got addr %p, addr1 %p.\n", addr, addr1); + + size = 0xfff; + addr = (char *)addr1 + 1; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *((volatile char *)addr1 + 0x2000) = 1; + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); + ok(addr == addr1, "Got addr %p, addr1 %p.\n", addr, addr1); + + size = 0x1000; + addr = addr1; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(addr == addr1, "Unexpected addr %p, addr1 %p.\n", addr, addr1); + ok(size == 0x1000, "Unexpected size %p.\n", (void *)size); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_DECOMMIT); + ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + size = 0x10000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_UNABLE_TO_FREE_VM, "Unexpected status %08lx.\n", status); + + size = 0; + addr = (char *)addr1 + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); + + size = 0x1000; + addr = (char *)addr1 + 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_DECOMMIT); + ok(status == STATUS_MEMORY_NOT_ALLOCATED, "Unexpected status %08lx.\n", status); + + size = 0; + addr = (char *)addr1 + 0x2000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + size = 0x1000; + status = NtFreeVirtualMemory(NtCurrentProcess(), &addr1, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); +} + +static void test_prefetch(void) +{ + NTSTATUS status; + MEMORY_RANGE_ENTRY entries[2] = {{ 0 }}; + ULONG reservedarg = 0; + char stackmem[] = "Test stack mem"; + static char testmem[] = "Test memory range data"; + + if (!pNtSetInformationVirtualMemory) + { + skip("no NtSetInformationVirtualMemory in ntdll\n"); + return; + } + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), -1, 1, entries, NULL, 32); + ok( status == STATUS_INVALID_PARAMETER_2, + "NtSetInformationVirtualMemory unexpected status on invalid info class (1): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), -1, 0, NULL, NULL, 0); + ok( status == STATUS_INVALID_PARAMETER_2 || (is_wow64 && status == STATUS_INVALID_PARAMETER_3), + "NtSetInformationVirtualMemory unexpected status on invalid info class (2): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), -1, 1, NULL, NULL, 32); + ok( status == STATUS_INVALID_PARAMETER_2 || (is_wow64 && status == STATUS_ACCESS_VIOLATION), + "NtSetInformationVirtualMemory unexpected status on invalid info class (3): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, NULL, 0 ); + ok( status == STATUS_INVALID_PARAMETER_5 || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on NULL info data (1): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, NULL, NULL, 0 ); + ok( status == STATUS_INVALID_PARAMETER_5 || (is_wow64 && status == STATUS_ACCESS_VIOLATION), + "NtSetInformationVirtualMemory unexpected status on NULL info data (2): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 0, NULL, NULL, 0 ); + ok( status == STATUS_INVALID_PARAMETER_5 || (is_wow64 && status == STATUS_INVALID_PARAMETER_3), + "NtSetInformationVirtualMemory unexpected status on NULL info data (3): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) * 2 ); + ok( status == STATUS_INVALID_PARAMETER_6, + "NtSetInformationVirtualMemory unexpected status on extended info data (1): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 0, NULL, &reservedarg, sizeof(reservedarg) * 2 ); + ok( status == STATUS_INVALID_PARAMETER_6 || (is_wow64 && status == STATUS_INVALID_PARAMETER_3), + "NtSetInformationVirtualMemory unexpected status on extended info data (2): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) / 2 ); + ok( status == STATUS_INVALID_PARAMETER_6, + "NtSetInformationVirtualMemory unexpected status on shrunk info data (1): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 0, NULL, &reservedarg, sizeof(reservedarg) / 2 ); + ok( status == STATUS_INVALID_PARAMETER_6 || (is_wow64 && status == STATUS_INVALID_PARAMETER_3), + "NtSetInformationVirtualMemory unexpected status on shrunk info data (2): %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 0, NULL, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_INVALID_PARAMETER_3, + "NtSetInformationVirtualMemory unexpected status on 0 entries: %08lx\n", status); + + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, NULL, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_ACCESS_VIOLATION, + "NtSetInformationVirtualMemory unexpected status on NULL entries: %08lx\n", status); + + entries[0].VirtualAddress = NULL; + entries[0].NumberOfBytes = 0; + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_INVALID_PARAMETER_4 || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 1 empty entry: %08lx\n", status); + + entries[0].VirtualAddress = NULL; + entries[0].NumberOfBytes = page_size; + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_SUCCESS || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 1 NULL address entry: %08lx\n", status); + + entries[0].VirtualAddress = (void *)((ULONG_PTR)testmem & -(ULONG_PTR)page_size); + entries[0].NumberOfBytes = page_size; + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_SUCCESS || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 1 page-aligned entry: %08lx\n", status); + + entries[0].VirtualAddress = testmem; + entries[0].NumberOfBytes = sizeof(testmem); + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_SUCCESS || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 1 entry: %08lx\n", status); + + entries[0].VirtualAddress = NULL; + entries[0].NumberOfBytes = page_size; + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 1, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_SUCCESS || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 1 unmapped entry: %08lx\n", status); + + entries[0].VirtualAddress = (void *)((ULONG_PTR)testmem & -(ULONG_PTR)page_size); + entries[0].NumberOfBytes = page_size; + entries[1].VirtualAddress = (void *)((ULONG_PTR)stackmem & -(ULONG_PTR)page_size); + entries[1].NumberOfBytes = page_size; + status = pNtSetInformationVirtualMemory( NtCurrentProcess(), VmPrefetchInformation, + 2, entries, &reservedarg, sizeof(reservedarg) ); + ok( status == STATUS_SUCCESS || + broken( is_wow64 && status == STATUS_INVALID_PARAMETER_6 ) /* win10 1507 */, + "NtSetInformationVirtualMemory unexpected status on 2 page-aligned entries: %08lx\n", status); +} + +static void test_query_region_information(void) +{ + MEMORY_REGION_INFORMATION info; + LARGE_INTEGER offset; + SIZE_T len, size; + NTSTATUS status; + HANDLE mapping; + void *ptr; + + size = 0x10000; + ptr = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size, MEM_RESERVE, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + +#ifdef _WIN64 + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, PartitionId), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, + FIELD_OFFSET(MEMORY_REGION_INFORMATION, RegionSize), &len); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %08lx.\n", status); +#endif + + len = 0; + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == size, "Unexpected region size.\n"); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + /* Committed size */ + size = 0x10000; + ptr = NULL; + status = NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == size, "Unexpected region size.\n"); + + size = 0; + status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + /* Pagefile mapping */ + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL); + ok(mapping != 0, "CreateFileMapping failed\n"); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READONLY, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == 4096, "Unexpected region size.\n"); + + status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + NtClose(mapping); +} + +static void test_query_image_information(void) +{ + MEMORY_IMAGE_INFORMATION info; + IMAGE_NT_HEADERS *nt; + LARGE_INTEGER offset; + SIZE_T len, size; + NTSTATUS status; + HANDLE mapping, file; + void *ptr; + + /* virtual allocation */ + + size = 0x8000; + ptr = NULL; + status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0, &size, MEM_RESERVE, PAGE_READWRITE ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + + len = 0xdead; + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), ptr, MemoryImageInformation, + &info, sizeof(info), &len ); + if (status == STATUS_INVALID_INFO_CLASS) + { + win_skip( "MemoryImageInformation not supported\n" ); + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + return; + } + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( len == sizeof(info), "wrong len %Ix\n", len ); + ok( !info.ImageBase, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( !info.SizeOfImage, "wrong size %Ix/%Ix\n", info.SizeOfImage, size ); + ok( !info.ImageFlags, "wrong flags %lx\n", info.ImageFlags ); + + len = 0xdead; + status = NtQueryVirtualMemory( NtCurrentProcess(), ptr, MemoryImageInformation, + &info, sizeof(info) + 2, &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( len == sizeof(info), "wrong len %Ix\n", len ); + + len = 0xdead; + status = NtQueryVirtualMemory( NtCurrentProcess(), ptr, MemoryImageInformation, + &info, sizeof(info) - 1, &len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %08lx\n", status ); + ok( len == 0xdead, "wrong len %Ix\n", len ); + + len = 0xdead; + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + size, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_INVALID_ADDRESS, "Unexpected status %08lx\n", status ); + ok( len == 0xdead || broken(len == sizeof(info)), "wrong len %Ix\n", len ); + + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( !info.ImageBase, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( !info.SizeOfImage, "wrong size %Ix/%Ix\n", info.SizeOfImage, size ); + ok( !info.ImageFlags, "wrong flags %lx\n", info.ImageFlags ); + + size = 0; + NtFreeVirtualMemory( NtCurrentProcess(), &ptr, &size, MEM_RELEASE ); + + /* mapped dll */ + + ptr = GetModuleHandleA( "ntdll.dll" ); + nt = RtlImageNtHeader( ptr ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( info.ImageBase == ptr, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( info.SizeOfImage == nt->OptionalHeader.SizeOfImage, "wrong size %Ix/%x\n", + info.SizeOfImage, (UINT)nt->OptionalHeader.SizeOfImage ); + ok( !info.ImagePartialMap, "wrong partial map\n" ); + ok( !info.ImageNotExecutable, "wrong not executable\n" ); + ok( info.ImageSigningLevel == 0 || info.ImageSigningLevel == 12, + "wrong signing level %u\n", info.ImageSigningLevel ); + + /* image mapping */ + + file = CreateFileA( "c:\\windows\\system32\\kernel32.dll", GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0, 0 ); + mapping = CreateFileMappingA( file, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY ); + ok( status == STATUS_IMAGE_NOT_AT_BASE, "Unexpected status %08lx\n", status ); + NtClose( mapping ); + + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( info.ImageBase == ptr, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( info.SizeOfImage == size, "wrong size %Ix/%Ix\n", info.SizeOfImage, size ); + ok( !info.ImagePartialMap, "wrong partial map\n" ); + ok( !info.ImageNotExecutable, "wrong not executable\n" ); + ok( info.ImageSigningLevel == 0 || info.ImageSigningLevel == 12, + "wrong signing level %u\n", info.ImageSigningLevel ); + + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + + /* partial image mapping */ + + file = CreateFileA( "c:\\windows\\system32\\kernel32.dll", GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0, 0 ); + mapping = CreateFileMappingA( file, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0x4000, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY ); + ok( status == STATUS_IMAGE_NOT_AT_BASE, "Unexpected status %08lx\n", status ); + todo_wine + ok( size == 0x4000, "wrong size %Ix\n", size ); + NtClose( mapping ); + + nt = RtlImageNtHeader( ptr ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( info.ImageBase == ptr, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( info.SizeOfImage == nt->OptionalHeader.SizeOfImage, "wrong size %Ix/%x\n", + info.SizeOfImage, (UINT)nt->OptionalHeader.SizeOfImage ); + todo_wine + ok( info.ImagePartialMap, "wrong partial map\n" ); + ok( !info.ImageNotExecutable, "wrong not executable\n" ); + ok( info.ImageSigningLevel == 0 || info.ImageSigningLevel == 12, + "wrong signing level %u\n", info.ImageSigningLevel ); + + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + + file = CreateFileA( "c:\\windows\\system32\\kernel32.dll", GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, 0, 0 ); + mapping = CreateFileMappingA( file, NULL, SEC_IMAGE | PAGE_READONLY, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + ptr = NULL; + size = 0x5000; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY ); + ok( status == STATUS_IMAGE_NOT_AT_BASE, "Unexpected status %08lx\n", status ); + todo_wine + ok( size == 0x5000, "wrong size %Ix\n", size ); + NtClose( mapping ); + + nt = RtlImageNtHeader( ptr ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( info.ImageBase == ptr, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( info.SizeOfImage == nt->OptionalHeader.SizeOfImage, "wrong size %Ix/%x\n", + info.SizeOfImage, (UINT)nt->OptionalHeader.SizeOfImage ); + todo_wine + ok( info.ImagePartialMap, "wrong partial map\n" ); + ok( !info.ImageNotExecutable, "wrong not executable\n" ); + ok( info.ImageSigningLevel == 0 || info.ImageSigningLevel == 12, + "wrong signing level %u\n", info.ImageSigningLevel ); + + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + + /* non-image mapping */ + + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 0x10000, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + NtClose( mapping ); + + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( !info.ImageBase, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( !info.SizeOfImage, "wrong size %Ix/%Ix\n", info.SizeOfImage, size ); + ok( !info.ImageFlags, "wrong flags %lx\n", info.ImageFlags ); + + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + + /* pagefile mapping */ + + mapping = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0x10000, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READONLY ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + NtClose( mapping ); + + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryVirtualMemory( NtCurrentProcess(), (char *)ptr + 0x1234, MemoryImageInformation, + &info, sizeof(info), &len ); + ok( status == STATUS_SUCCESS, "Unexpected status %08lx\n", status ); + ok( !info.ImageBase, "wrong image base %p/%p\n", info.ImageBase, ptr ); + ok( !info.SizeOfImage, "wrong size %Ix/%Ix\n", info.SizeOfImage, size ); + ok( !info.ImageFlags, "wrong flags %lx\n", info.ImageFlags ); + + NtUnmapViewOfSection( NtCurrentProcess(), ptr ); + NtClose( file ); +} + +static int *write_addr; +static int got_exception; + +static LONG CALLBACK exec_write_handler( EXCEPTION_POINTERS *ptrs ) +{ + MANAGE_WRITES_TO_EXECUTABLE_MEMORY mem = { .Version = 2, .ThreadAllowWrites = 1 }; + EXCEPTION_RECORD *rec = ptrs->ExceptionRecord; + NTSTATUS status; + + got_exception++; + ok( rec->ExceptionCode == STATUS_IN_PAGE_ERROR, "wrong exception %lx\n", rec->ExceptionCode ); + ok( rec->NumberParameters == 3, "wrong params %lx\n", rec->NumberParameters ); + ok( rec->ExceptionInformation[0] == 1, "not write access %Ix\n", rec->ExceptionInformation[0] ); + ok( (int *)rec->ExceptionInformation[1] == write_addr, + "wrong address %p / %p\n", (void *)rec->ExceptionInformation[1], write_addr ); + ok( rec->ExceptionInformation[2] == STATUS_EXECUTABLE_MEMORY_WRITE, "wrong status %Ix\n", + rec->ExceptionInformation[2] ); + + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( !status, "NtSetInformationThread failed %lx\n", status ); + *write_addr = 0; /* make the page dirty to prevent further exceptions */ + mem.ThreadAllowWrites = 0; + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( !status, "NtSetInformationThread failed %lx\n", status ); + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void test_exec_memory_writes(void) +{ + NTSTATUS status; + void *ptr, *handler; + MANAGE_WRITES_TO_EXECUTABLE_MEMORY mem = { .Version = 2 }; + MEMORY_RANGE_ENTRY range; + ULONG flag, len, granularity; + ULONG_PTR count; + void *addresses[4]; + DWORD old_prot; + WCHAR path[MAX_PATH]; + HANDLE file; + IO_STATUS_BLOCK io; + + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); +#ifdef __aarch64__ + ok( !status, "NtSetInformationProcess failed %lx\n", status ); +#else + if (!status) + { + SYSTEM_CPU_INFORMATION info; + ULONG len; + + RtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len ); + ok (info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64, "succeeded on non-ARM64\n" ); + mem.ProcessEnableWriteExceptions = 1; + NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + skip( "skipping test on ARM64EC\n" ); + return; + } + ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_SUPPORTED, + "NtSetInformationProcess failed %lx\n", status ); +#endif + if (status) return; + handler = RtlAddVectoredExceptionHandler( TRUE, exec_write_handler ); + + /* test anon mapping */ + + ptr = VirtualAlloc( NULL, page_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + write_addr = (int *)ptr + 3; + + mem.ProcessEnableWriteExceptions = 1; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( !status, "NtSetInformationProcess failed %lx\n", status ); + + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 0, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + VirtualProtect( ptr, page_size, PAGE_EXECUTE_READWRITE, &old_prot ); + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + /* no longer failing on dirty page */ + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 0, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + /* setting permissions resets protection */ + VirtualProtect( ptr, page_size, PAGE_EXECUTE_READWRITE, &old_prot ); + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + /* clearing dirty state also resets protection */ + range.VirtualAddress = ptr; + range.NumberOfBytes = 1; + flag = 0; + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( !status, "NtSetInformationVirtualMemory failed %lx\n", status ); + + /* making page dirty is not allowed */ + flag = 1; + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( status == STATUS_INVALID_PARAMETER_5, "NtSetInformationVirtualMemory failed %lx\n", status ); + + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + GetModuleFileNameW( 0, path, MAX_PATH ); + file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "can't open %s: %lu\n", debugstr_w(path), GetLastError() ); + /* reading into protected page crashes on Windows */ + if (0) VirtualProtect( ptr, page_size, PAGE_EXECUTE_READWRITE, &old_prot ); + status = NtReadFile( file, 0, NULL, NULL, &io, write_addr, 8, NULL, NULL ); + ok( !status, "NtReadFile failed %lx\n", status ); + CloseHandle( file ); + + VirtualFree( ptr, 0, MEM_RELEASE ); + + /* test PE mapping */ + + ptr = GetModuleHandleA( NULL ); + write_addr = (int *)ptr + 3; + VirtualProtect( ptr, page_size, PAGE_EXECUTE_WRITECOPY, &old_prot ); + + got_exception = 0; + *write_addr = 0; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + got_exception = 0; + *write_addr = 0; + ok( got_exception == 0, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + VirtualProtect( ptr, page_size, PAGE_EXECUTE_WRITECOPY, &old_prot ); + got_exception = 0; + *write_addr = 0; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + range.VirtualAddress = write_addr; + range.NumberOfBytes = 1; + flag = 0; + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( !status, "NtSetInformationVirtualMemory failed %lx\n", status ); + got_exception = 0; + *write_addr = 0; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + /* test interactions with write watches */ + + ptr = VirtualAlloc( NULL, page_size, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE ); + write_addr = (int *)ptr + 3; + + VirtualProtect( ptr, page_size, PAGE_EXECUTE_READWRITE, &old_prot ); + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( GetCurrentProcess(), 0, ptr, page_size, addresses, &count, &granularity ); + ok( !status, "NtGetWriteWatch failed %lx\n", status ); + ok( count == 1, "got count %Iu\n", count ); + ok( addresses[0] == ptr, "wrong ptr %p / %p\n", addresses[0], ptr ); + + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 0, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( GetCurrentProcess(), WRITE_WATCH_FLAG_RESET, + ptr, page_size, addresses, &count, &granularity ); + ok( !status, "NtGetWriteWatch failed %lx\n", status ); + ok( count == 1, "got count %Iu\n", count ); + ok( addresses[0] == ptr, "wrong ptr %p / %p\n", addresses[0], ptr ); + + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( GetCurrentProcess(), 0, ptr, page_size, addresses, &count, &granularity ); + ok( !status, "NtGetWriteWatch failed %lx\n", status ); + ok( count == 1, "got count %Iu\n", count ); + ok( addresses[0] == ptr, "wrong ptr %p / %p\n", addresses[0], ptr ); + + range.VirtualAddress = ptr; + range.NumberOfBytes = 1; + flag = 0; + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( !status, "NtSetInformationVirtualMemory failed %lx\n", status ); + + count = ARRAY_SIZE(addresses); + status = NtGetWriteWatch( GetCurrentProcess(), 0, ptr, page_size, addresses, &count, &granularity ); + ok( !status, "NtGetWriteWatch failed %lx\n", status ); + ok( count == 0, "got count %Iu\n", count ); + + got_exception = 0; + *write_addr = 0x123456; + ok( got_exception == 1, "wrong number of exceptions %u\n", got_exception ); + write_addr++; + + /* test some invalid calls */ + + VirtualFree( ptr, 0, MEM_RELEASE ); + flag = 0; + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( status == STATUS_MEMORY_NOT_ALLOCATED, "NtSetInformationVirtualMemory failed %lx\n", status ); + + mem.ProcessEnableWriteExceptions = 0; + NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + + status = pNtSetInformationVirtualMemory( GetCurrentProcess(), VmPageDirtyStateInformation, + 1, &range, &flag, sizeof(flag) ); + ok( status == STATUS_NOT_SUPPORTED, "NtSetInformationVirtualMemory failed %lx\n", status ); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem), &len ); + ok( status == STATUS_INVALID_INFO_CLASS, "NtQueryInformationProcess failed %lx\n", status ); + + mem.ProcessEnableWriteExceptions = 1; + mem.ThreadAllowWrites = 1; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( status == STATUS_INVALID_PARAMETER, "NtSetInformationProcess failed %lx\n", status ); + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( status == STATUS_INVALID_PARAMETER, "NtSetInformationThread failed %lx\n", status ); + mem.ProcessEnableWriteExceptions = 0; + mem.ThreadAllowWrites = 0; + mem.Version = 3; + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( status == STATUS_REVISION_MISMATCH, "NtSetInformationThread failed %lx\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) ); + ok( status == STATUS_REVISION_MISMATCH, "NtSetInformationProcess failed %lx\n", status ); + mem.Version = 2; + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) - 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationThread failed %lx\n", status ); + status = NtSetInformationThread( GetCurrentThread(), ThreadManageWritesToExecutableMemory, + &mem, sizeof(mem) + 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationThread failed %lx\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) - 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %lx\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessManageWritesToExecutableMemory, + &mem, sizeof(mem) + 1 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "NtSetInformationProcess failed %lx\n", status ); + + RtlRemoveVectoredExceptionHandler( handler ); +} + +START_TEST(virtual) +{ + HMODULE mod; + + int argc; + char **argv; + argc = winetest_get_mainargs(&argv); + + if (argc >= 3) + { + if (!strcmp(argv[2], "sleep")) + { + Sleep(5000); /* spawned process runs for at most 5 seconds */ + return; + } + return; + } + + mod = GetModuleHandleA("kernel32.dll"); + pIsWow64Process = (void *)GetProcAddress(mod, "IsWow64Process"); + pGetEnabledXStateFeatures = (void *)GetProcAddress(mod, "GetEnabledXStateFeatures"); + mod = GetModuleHandleA("ntdll.dll"); + pRtlCreateUserStack = (void *)GetProcAddress(mod, "RtlCreateUserStack"); + pRtlCreateUserThread = (void *)GetProcAddress(mod, "RtlCreateUserThread"); + pRtlFreeUserStack = (void *)GetProcAddress(mod, "RtlFreeUserStack"); + pRtlFindExportedRoutineByName = (void *)GetProcAddress(mod, "RtlFindExportedRoutineByName"); + pRtlGetEnabledExtendedFeatures = (void *)GetProcAddress(mod, "RtlGetEnabledExtendedFeatures"); + pRtlGetNativeSystemInformation = (void *)GetProcAddress(mod, "RtlGetNativeSystemInformation"); + pRtlIsEcCode = (void *)GetProcAddress(mod, "RtlIsEcCode"); + pNtAllocateVirtualMemoryEx = (void *)GetProcAddress(mod, "NtAllocateVirtualMemoryEx"); + pNtMapViewOfSectionEx = (void *)GetProcAddress(mod, "NtMapViewOfSectionEx"); + pNtSetInformationVirtualMemory = (void *)GetProcAddress(mod, "NtSetInformationVirtualMemory"); + + NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL); + trace("system page size %#lx\n", sbi.PageSize); + page_size = sbi.PageSize; + if (!pIsWow64Process || !pIsWow64Process(NtCurrentProcess(), &is_wow64)) is_wow64 = FALSE; + + test_NtAllocateVirtualMemory(); + test_NtAllocateVirtualMemoryEx(); + test_NtAllocateVirtualMemoryEx_address_requirements(); + test_NtFreeVirtualMemory(); + test_RtlCreateUserStack(); + test_NtMapViewOfSection(); + test_NtMapViewOfSectionEx(); + test_prefetch(); + test_user_shared_data(); + test_syscalls(); + test_query_region_information(); + test_query_image_information(); + test_exec_memory_writes(); +} diff --git a/modules/rostests/winetests/ntdll/wow64.c b/modules/rostests/winetests/ntdll/wow64.c new file mode 100644 index 00000000000..ffaba7f4bdb --- /dev/null +++ b/modules/rostests/winetests/ntdll/wow64.c @@ -0,0 +1,3228 @@ +/* + * Unit test suite Wow64 functions + * + * Copyright 2021 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" +#include "winuser.h" +#include "ddk/wdm.h" +#include "wine/test.h" + +#ifdef __REACTOS__ +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define wcsicmp _wcsicmp +#if defined(_MSC_VER) && defined(_M_AMD64) +USHORT __readsegfs(void); +USHORT __readsegss(void); +#endif // _M_AMD64 +#endif + +static NTSTATUS (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*); +static NTSTATUS (WINAPI *pNtQuerySystemInformationEx)(SYSTEM_INFORMATION_CLASS,void*,ULONG,void*,ULONG,ULONG*); +static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*); +static void (WINAPI *pRtlOpenCrossProcessEmulatorWorkConnection)(HANDLE,HANDLE*,void**); +static void * (WINAPI *pRtlFindExportedRoutineByName)(HMODULE,const char *); +static USHORT (WINAPI *pRtlWow64GetCurrentMachine)(void); +static NTSTATUS (WINAPI *pRtlWow64GetProcessMachines)(HANDLE,WORD*,WORD*); +static NTSTATUS (WINAPI *pRtlWow64GetSharedInfoProcess)(HANDLE,BOOLEAN*,WOW64INFO*); +static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE,WOW64_CONTEXT*); +static NTSTATUS (WINAPI *pRtlWow64IsWowGuestMachineSupported)(USHORT,BOOLEAN*); +static NTSTATUS (WINAPI *pNtMapViewOfSectionEx)(HANDLE,HANDLE,PVOID*,const LARGE_INTEGER*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG); +#ifdef _WIN64 +static NTSTATUS (WINAPI *pKiUserExceptionDispatcher)(EXCEPTION_RECORD*,CONTEXT*); +static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*); +static NTSTATUS (WINAPI *pRtlWow64GetThreadSelectorEntry)(HANDLE,THREAD_DESCRIPTOR_INFORMATION*,ULONG,ULONG*); +static CROSS_PROCESS_WORK_ENTRY * (WINAPI *pRtlWow64PopAllCrossProcessWorkFromWorkList)(CROSS_PROCESS_WORK_HDR*,BOOLEAN*); +static CROSS_PROCESS_WORK_ENTRY * (WINAPI *pRtlWow64PopCrossProcessWorkFromFreeList)(CROSS_PROCESS_WORK_HDR*); +static BOOLEAN (WINAPI *pRtlWow64PushCrossProcessWorkOntoFreeList)(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*); +static BOOLEAN (WINAPI *pRtlWow64PushCrossProcessWorkOntoWorkList)(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*,void**); +static BOOLEAN (WINAPI *pRtlWow64RequestCrossProcessHeavyFlush)(CROSS_PROCESS_WORK_HDR*); +static void (WINAPI *pProcessPendingCrossProcessEmulatorWork)(void); +#else +static NTSTATUS (WINAPI *pNtWow64AllocateVirtualMemory64)(HANDLE,ULONG64*,ULONG64,ULONG64*,ULONG,ULONG); +static NTSTATUS (WINAPI *pNtWow64GetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*); +static NTSTATUS (WINAPI *pNtWow64IsProcessorFeaturePresent)(ULONG); +static NTSTATUS (WINAPI *pNtWow64QueryInformationProcess64)(HANDLE,PROCESSINFOCLASS,void*,ULONG,ULONG*); +static NTSTATUS (WINAPI *pNtWow64ReadVirtualMemory64)(HANDLE,ULONG64,void*,ULONG64,ULONG64*); +static NTSTATUS (WINAPI *pNtWow64WriteVirtualMemory64)(HANDLE,ULONG64,const void *,ULONG64,ULONG64*); +#endif + +static BOOL is_win64 = sizeof(void *) > sizeof(int); +static BOOL is_wow64; +static BOOL old_wow64; /* Wine old-style wow64 */ +static void *code_mem; + +#ifdef __i386__ +static USHORT current_machine = IMAGE_FILE_MACHINE_I386; +static USHORT native_machine = IMAGE_FILE_MACHINE_I386; +#elif defined __x86_64__ +static USHORT current_machine = IMAGE_FILE_MACHINE_AMD64; +static USHORT native_machine = IMAGE_FILE_MACHINE_AMD64; +#elif defined __arm__ +static USHORT current_machine = IMAGE_FILE_MACHINE_ARMNT; +static USHORT native_machine = IMAGE_FILE_MACHINE_ARMNT; +#elif defined __aarch64__ +static USHORT current_machine = IMAGE_FILE_MACHINE_ARM64; +static USHORT native_machine = IMAGE_FILE_MACHINE_ARM64; +#else +static USHORT current_machine; +static USHORT native_machine; +#endif + +struct arm64ec_shared_info +{ + ULONG Wow64ExecuteFlags; + USHORT NativeMachineType; + USHORT EmulatedMachineType; + ULONGLONG SectionHandle; + ULONGLONG CrossProcessWorkList; + ULONGLONG unknown; +}; + +#if !defined(__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) +static BOOL is_machine_32bit( USHORT machine ) +{ + return machine == IMAGE_FILE_MACHINE_I386 || machine == IMAGE_FILE_MACHINE_ARMNT; +} +#endif // !defined(__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) + +static void init(void) +{ + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + + if (!IsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + + if (is_wow64) + { + TEB64 *teb64 = ULongToPtr( NtCurrentTeb()->GdiBatchCount ); + + if (teb64) + { + PEB64 *peb64 = ULongToPtr(teb64->Peb); + old_wow64 = !peb64->LdrData; + } + } + +#define GET_PROC(func) p##func = (void *)GetProcAddress( ntdll, #func ) + GET_PROC( NtMapViewOfSectionEx ); + GET_PROC( NtQuerySystemInformation ); + GET_PROC( NtQuerySystemInformationEx ); + GET_PROC( RtlGetNativeSystemInformation ); + GET_PROC( RtlOpenCrossProcessEmulatorWorkConnection ); + GET_PROC( RtlFindExportedRoutineByName ); + GET_PROC( RtlWow64GetCurrentMachine ); + GET_PROC( RtlWow64GetProcessMachines ); + GET_PROC( RtlWow64GetSharedInfoProcess ); + GET_PROC( RtlWow64GetThreadContext ); + GET_PROC( RtlWow64IsWowGuestMachineSupported ); +#ifdef _WIN64 + GET_PROC( KiUserExceptionDispatcher ); + GET_PROC( RtlWow64GetCpuAreaInfo ); + GET_PROC( RtlWow64GetThreadSelectorEntry ); + GET_PROC( RtlWow64PopAllCrossProcessWorkFromWorkList ); + GET_PROC( RtlWow64PopCrossProcessWorkFromFreeList ); + GET_PROC( RtlWow64PushCrossProcessWorkOntoFreeList ); + GET_PROC( RtlWow64PushCrossProcessWorkOntoWorkList ); + GET_PROC( RtlWow64RequestCrossProcessHeavyFlush ); + GET_PROC( ProcessPendingCrossProcessEmulatorWork ); +#else + GET_PROC( NtWow64AllocateVirtualMemory64 ); + GET_PROC( NtWow64GetNativeSystemInformation ); + GET_PROC( NtWow64IsProcessorFeaturePresent ); + GET_PROC( NtWow64QueryInformationProcess64 ); + GET_PROC( NtWow64ReadVirtualMemory64 ); + GET_PROC( NtWow64WriteVirtualMemory64 ); +#endif +#undef GET_PROC + + if (pNtQuerySystemInformationEx) + { + SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION machines[8]; + HANDLE process = GetCurrentProcess(); + NTSTATUS status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, + sizeof(process), machines, sizeof(machines), NULL ); + if (!status) + for (int i = 0; machines[i].Machine; i++) + trace( "machine %04x kernel %u user %u native %u process %u wow64 %u\n", + machines[i].Machine, machines[i].KernelMode, machines[i].UserMode, + machines[i].Native, machines[i].Process, machines[i].WoW64Container ); + } + + if (pRtlGetNativeSystemInformation) + { + SYSTEM_CPU_INFORMATION info; + ULONG len; + + pRtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len ); + switch (info.ProcessorArchitecture) + { + case PROCESSOR_ARCHITECTURE_ARM64: + native_machine = IMAGE_FILE_MACHINE_ARM64; + break; + case PROCESSOR_ARCHITECTURE_AMD64: + native_machine = IMAGE_FILE_MACHINE_AMD64; + break; + } + } + + trace( "current %04x native %04x\n", current_machine, native_machine ); + + if (native_machine == IMAGE_FILE_MACHINE_AMD64) + code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); +} + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) +static BOOL create_process_machine( char *cmdline, DWORD flags, USHORT machine, PROCESS_INFORMATION *pi ) +{ + struct _PROC_THREAD_ATTRIBUTE_LIST *list; + STARTUPINFOEXA si = {{ sizeof(si) }}; + SIZE_T size = 1024; + BOOL ret; + + si.lpAttributeList = list = malloc( size ); + InitializeProcThreadAttributeList( list, 1, 0, &size ); + UpdateProcThreadAttribute( list, 0, PROC_THREAD_ATTRIBUTE_MACHINE_TYPE, + &machine, sizeof(machine), NULL, NULL ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, + EXTENDED_STARTUPINFO_PRESENT | flags, NULL, NULL, &si.StartupInfo, pi ); + DeleteProcThreadAttributeList( list ); + free( list ); + return ret; +} + +static void test_process_architecture( HANDLE process, USHORT expect_machine, USHORT expect_native ) +{ + SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION machines[8]; + NTSTATUS status; + ULONG i, len; + + len = 0xdead; + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, sizeof(process), + machines, sizeof(machines), &len ); + ok( !status, "failed %lx\n", status ); + ok( !(len & 3), "wrong len %lx\n", len ); + len /= sizeof(machines[0]); + for (i = 0; i < len - 1; i++) + { + if (machines[i].Process) + ok( machines[i].Machine == expect_machine, "wrong process machine %x\n", machines[i].Machine); + else + ok( machines[i].Machine != expect_machine, "wrong machine %x\n", machines[i].Machine); + + if (machines[i].Native) + ok( machines[i].Machine == expect_native, "wrong native machine %x\n", machines[i].Machine); + else + ok( machines[i].Machine != expect_native, "wrong machine %x\n", machines[i].Machine); + + if (machines[i].WoW64Container) + ok( is_machine_32bit( machines[i].Machine ) && !is_machine_32bit( native_machine ), + "wrong wow64 %x\n", machines[i].Machine); + } + ok( !*(DWORD *)&machines[i], "missing terminating null\n" ); + + len = i * sizeof(machines[0]); + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, sizeof(process), + machines, len, &len ); + ok( status == STATUS_BUFFER_TOO_SMALL, "failed %lx\n", status ); + ok( len == (i + 1) * sizeof(machines[0]), "wrong len %lu\n", len ); + + if (pRtlWow64GetProcessMachines) + { + USHORT current = 0xdead, native = 0xbeef; + status = pRtlWow64GetProcessMachines( process, ¤t, &native ); + ok( !status, "failed %lx\n", status ); + if (expect_machine == expect_native) + ok( current == 0, "wrong current machine %x / %x\n", current, expect_machine ); + else + ok( current == expect_machine, "wrong current machine %x / %x\n", current, expect_machine ); + ok( native == expect_native, "wrong native machine %x / %x\n", native, expect_native ); + } +} + +static void test_process_machine( HANDLE process, HANDLE thread, + USHORT expect_machine, USHORT expect_image ) +{ + PROCESS_BASIC_INFORMATION basic; + SECTION_IMAGE_INFORMATION image; + IMAGE_DOS_HEADER dos; + IMAGE_NT_HEADERS nt; + PEB peb; + ULONG len; + SIZE_T size; + NTSTATUS status; + void *entry_point = NULL; + void *win32_entry = NULL; + + status = NtQueryInformationProcess( process, ProcessBasicInformation, &basic, sizeof(basic), &len ); + ok( !status, "ProcessBasicInformation failed %lx\n", status ); + if (ReadProcessMemory( process, basic.PebBaseAddress, &peb, sizeof(peb), &size ) && + ReadProcessMemory( process, peb.ImageBaseAddress, &dos, sizeof(dos), &size ) && + ReadProcessMemory( process, (char *)peb.ImageBaseAddress + dos.e_lfanew, &nt, sizeof(nt), &size )) + { + ok( nt.FileHeader.Machine == expect_machine, "wrong nt machine %x / %x\n", + nt.FileHeader.Machine, expect_machine ); + entry_point = (char *)peb.ImageBaseAddress + nt.OptionalHeader.AddressOfEntryPoint; + } + + status = NtQueryInformationProcess( process, ProcessImageInformation, &image, sizeof(image), &len ); + ok( !status, "ProcessImageInformation failed %lx\n", status ); + ok( image.Machine == expect_image, "wrong image info %x / %x\n", image.Machine, expect_image ); + + status = NtQueryInformationThread( thread, ThreadQuerySetWin32StartAddress, + &win32_entry, sizeof(win32_entry), &len ); + ok( !status, "ThreadQuerySetWin32StartAddress failed %lx\n", status ); + + if (!entry_point) return; + + if (image.Machine == expect_machine) + { + ok( image.TransferAddress == entry_point, "wrong entry %p / %p\n", + image.TransferAddress, entry_point ); + ok( win32_entry == entry_point, "wrong win32 entry %p / %p\n", + win32_entry, entry_point ); + } + else + { + /* image.TransferAddress is the ARM64 entry, entry_point is the x86-64 one, + win32_entry is the redirected x86-64 -> ARM64EC one */ + ok( image.TransferAddress != entry_point, "wrong entry %p\n", image.TransferAddress ); + ok( image.TransferAddress != win32_entry, "wrong entry %p\n", image.TransferAddress ); + ok( win32_entry != entry_point, "wrong win32 entry %p\n", win32_entry ); + } +} + +static void test_query_architectures(void) +{ + static char cmd_sysnative[] = "C:\\windows\\sysnative\\cmd.exe /c exit"; + static char cmd_system32[] = "C:\\windows\\system32\\cmd.exe /c exit"; + static char cmd_syswow64[] = "C:\\windows\\syswow64\\cmd.exe /c exit"; + SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION machines[8]; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { sizeof(si) }; + NTSTATUS status; + HANDLE process; + ULONG i, len; +#ifdef __arm64ec__ + BOOL is_arm64ec = TRUE; +#else + BOOL is_arm64ec = FALSE; +#endif + + if (!pNtQuerySystemInformationEx) return; + + process = GetCurrentProcess(); + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, sizeof(process), + machines, sizeof(machines), &len ); + if (status == STATUS_INVALID_INFO_CLASS) + { + win_skip( "SystemSupportedProcessorArchitectures not supported\n" ); + return; + } + ok( !status, "failed %lx\n", status ); + + process = (HANDLE)0xdeadbeef; + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, sizeof(process), + machines, sizeof(machines), &len ); + ok( status == STATUS_INVALID_HANDLE, "failed %lx\n", status ); + process = (HANDLE)0xdeadbeef; + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, 3, + machines, sizeof(machines), &len ); + ok( status == STATUS_INVALID_PARAMETER || broken(status == STATUS_INVALID_HANDLE), + "failed %lx\n", status ); + process = GetCurrentProcess(); + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, &process, 3, + machines, sizeof(machines), &len ); + ok( status == STATUS_INVALID_PARAMETER || broken( status == STATUS_SUCCESS), + "failed %lx\n", status ); + status = pNtQuerySystemInformationEx( SystemSupportedProcessorArchitectures, NULL, 0, + machines, sizeof(machines), &len ); + ok( status == STATUS_INVALID_PARAMETER, "failed %lx\n", status ); + + winetest_push_context( "current" ); + test_process_architecture( GetCurrentProcess(), is_win64 ? native_machine : current_machine, + native_machine ); + test_process_machine( GetCurrentProcess(), GetCurrentThread(), current_machine, + is_arm64ec ? native_machine : current_machine ); + winetest_pop_context(); + + winetest_push_context( "zero" ); + test_process_architecture( 0, 0, native_machine ); + winetest_pop_context(); + + if (CreateProcessA( NULL, is_win64 ? cmd_system32 : cmd_sysnative, NULL, NULL, + FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi )) + { + winetest_push_context( "system32" ); + test_process_architecture( pi.hProcess, native_machine, native_machine ); + test_process_machine( pi.hProcess, pi.hThread, + is_win64 ? current_machine : native_machine, native_machine ); + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + winetest_pop_context(); + } + if (CreateProcessA( NULL, is_win64 ? cmd_syswow64 : cmd_system32, NULL, NULL, + FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi )) + { + winetest_push_context( "syswow64" ); + test_process_architecture( pi.hProcess, IMAGE_FILE_MACHINE_I386, native_machine ); + test_process_machine( pi.hProcess, pi.hThread, IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_I386 ); + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + winetest_pop_context(); + } + if (is_win64 && native_machine == IMAGE_FILE_MACHINE_ARM64) + { + USHORT machine = IMAGE_FILE_MACHINE_ARM64 + IMAGE_FILE_MACHINE_AMD64 - current_machine; + + if (create_process_machine( cmd_system32, CREATE_SUSPENDED, machine, &pi )) + { + winetest_push_context( "%04x", machine ); + test_process_architecture( pi.hProcess, native_machine, native_machine ); + test_process_machine( pi.hProcess, pi.hThread, machine, native_machine ); + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + winetest_pop_context(); + } + } + + if (pRtlWow64GetCurrentMachine) + { + USHORT machine = pRtlWow64GetCurrentMachine(); + ok( machine == current_machine, "wrong machine %x / %x\n", machine, current_machine ); + } + if (pRtlWow64IsWowGuestMachineSupported) + { + static const WORD machines[] = { IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_ARMNT, + IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_ARM64, 0xdead }; + + for (i = 0; i < ARRAY_SIZE(machines); i++) + { + BOOLEAN ret = 0xcc; + status = pRtlWow64IsWowGuestMachineSupported( machines[i], &ret ); + ok( !status, "failed %lx\n", status ); + if (is_machine_32bit( machines[i] ) && !is_machine_32bit( native_machine )) + ok( ret || machines[i] == IMAGE_FILE_MACHINE_ARMNT || + broken(current_machine == IMAGE_FILE_MACHINE_I386), /* win10-1607 wow64 */ + "%04x: got %u\n", machines[i], ret ); + else + ok( !ret, "%04x: got %u\n", machines[i], ret ); + } + } +} +#endif // !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) + +static void push_onto_free_list( CROSS_PROCESS_WORK_HDR *list, CROSS_PROCESS_WORK_ENTRY *entry ) +{ +#ifdef _WIN64 + pRtlWow64PushCrossProcessWorkOntoFreeList( list, entry ); +#else + entry->next = list->first; + list->first = (char *)entry - (char *)list; +#endif +} + +static void push_onto_work_list( CROSS_PROCESS_WORK_HDR *list, CROSS_PROCESS_WORK_ENTRY *entry ) +{ +#ifdef _WIN64 + void *ret; + pRtlWow64PushCrossProcessWorkOntoWorkList( list, entry, &ret ); +#else + entry->next = list->first; + list->first = (char *)entry - (char *)list; +#endif +} + +static CROSS_PROCESS_WORK_ENTRY *pop_from_free_list( CROSS_PROCESS_WORK_HDR *list ) +{ +#ifdef _WIN64 + return pRtlWow64PopCrossProcessWorkFromFreeList( list ); +#else + CROSS_PROCESS_WORK_ENTRY *ret; + + if (!list->first) return NULL; + ret = (CROSS_PROCESS_WORK_ENTRY *)((char *)list + list->first); + list->first = ret->next; + ret->next = 0; + return ret; +#endif +} + +static CROSS_PROCESS_WORK_ENTRY *pop_from_work_list( CROSS_PROCESS_WORK_HDR *list ) +{ +#ifdef _WIN64 + BOOLEAN flush; + + return pRtlWow64PopAllCrossProcessWorkFromWorkList( list, &flush ); +#else + UINT pos = list->first, prev_pos = 0; + + list->first = 0; + if (!pos) return NULL; + + for (;;) /* reverse the list */ + { + CROSS_PROCESS_WORK_ENTRY *entry = CROSS_PROCESS_LIST_ENTRY( list, pos ); + UINT next = entry->next; + entry->next = prev_pos; + if (!next) return entry; + prev_pos = pos; + pos = next; + } +#endif +} + +static void request_cross_process_flush( CROSS_PROCESS_WORK_HDR *list ) +{ +#ifdef _WIN64 + pRtlWow64RequestCrossProcessHeavyFlush( list ); +#else + list->first |= CROSS_PROCESS_LIST_FLUSH; +#endif +} + +#define expect_cross_work_entry(list,entry,id,addr,size,arg0,arg1,arg2,arg3) \ + expect_cross_work_entry_(list,entry,id,addr,size,arg0,arg1,arg2,arg3,__LINE__) +static CROSS_PROCESS_WORK_ENTRY *expect_cross_work_entry_( CROSS_PROCESS_WORK_LIST *list, + CROSS_PROCESS_WORK_ENTRY *entry, + UINT id, void *addr, SIZE_T size, + UINT arg0, UINT arg1, UINT arg2, UINT arg3, + int line ) +{ + CROSS_PROCESS_WORK_ENTRY *next; + + ok_(__FILE__,line)( entry != NULL, "no more entries in list\n" ); + if (!entry) return NULL; + ok_(__FILE__,line)( entry->id == id, "wrong type %u / %u\n", entry->id, id ); + ok_(__FILE__,line)( entry->addr == (ULONG_PTR)addr, "wrong address %s / %p\n", + wine_dbgstr_longlong(entry->addr), addr ); + ok_(__FILE__,line)( entry->size == size, "wrong size %s / %Ix\n", + wine_dbgstr_longlong(entry->size), size ); + ok_(__FILE__,line)( entry->args[0] == arg0, "wrong args[0] %x / %x\n", entry->args[0], arg0 ); + ok_(__FILE__,line)( entry->args[1] == arg1, "wrong args[1] %x / %x\n", entry->args[1], arg1 ); + ok_(__FILE__,line)( entry->args[2] == arg2, "wrong args[2] %x / %x\n", entry->args[2], arg2 ); + ok_(__FILE__,line)( entry->args[3] == arg3, "wrong args[3] %x / %x\n", entry->args[3], arg3 ); + next = entry->next ? CROSS_PROCESS_LIST_ENTRY( &list->work_list, entry->next ) : NULL; + memset( entry, 0xcc, sizeof(*entry) ); + push_onto_free_list( &list->free_list, entry ); + return next; +} + +static void test_cross_process_notifications( HANDLE process, ULONG_PTR section, ULONG_PTR ptr ) +{ + CROSS_PROCESS_WORK_ENTRY *entry; + CROSS_PROCESS_WORK_LIST *list; + UINT pos; + void *addr = NULL, *addr2; + SIZE_T size = 0; + DWORD old_prot; + LARGE_INTEGER offset; + HANDLE file, mapping; + NTSTATUS status; + BOOL ret; + BYTE data[] = { 0xcc, 0xcc, 0xcc }; + + ret = DuplicateHandle( process, (HANDLE)section, GetCurrentProcess(), &mapping, + 0, FALSE, DUPLICATE_SAME_ACCESS ); + ok( ret, "DuplicateHandle failed %lu\n", GetLastError() ); + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr, 0, 0, NULL, + &size, ViewShare, 0, PAGE_READWRITE ); + ok( !status, "NtMapViewOfSection failed %lx\n", status ); + ok( size == 0x4000, "unexpected size %Ix\n", size ); + list = addr; + addr2 = malloc( size ); + ret = ReadProcessMemory( process, (void *)ptr, addr2, size, &size ); + ok( ret, "ReadProcessMemory failed %lu\n", GetLastError() ); + ok( !memcmp( addr2, addr, size ), "wrong data\n" ); + free( addr2 ); + CloseHandle( mapping ); + + if (pRtlOpenCrossProcessEmulatorWorkConnection) + { + pRtlOpenCrossProcessEmulatorWorkConnection( process, &mapping, &addr2 ); + ok( mapping != 0, "got 0 handle\n" ); + ok( addr2 != NULL, "got NULL data\n" ); + ok( !memcmp( addr2, addr, size ), "wrong data\n" ); + UnmapViewOfFile( addr2 ); + addr2 = NULL; + size = 0; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr2, 0, 0, NULL, + &size, ViewShare, 0, PAGE_READWRITE ); + ok( !status, "NtMapViewOfSection failed %lx\n", status ); + ok( !memcmp( addr2, addr, size ), "wrong data\n" ); + ok( CloseHandle( mapping ), "invalid handle\n" ); + UnmapViewOfFile( addr2 ); + + mapping = (HANDLE)0xdead; + addr2 = (void *)0xdeadbeef; + pRtlOpenCrossProcessEmulatorWorkConnection( GetCurrentProcess(), &mapping, &addr2 ); + ok( !mapping, "got handle %p\n", mapping ); + ok( !addr2, "got data %p\n", addr2 ); + } + else skip( "RtlOpenCrossProcessEmulatorWorkConnection not supported\n" ); + + NtSuspendProcess( process ); + + /* set argument values in free list to detect changes */ + for (pos = list->free_list.first; pos; pos = entry->next ) + { + entry = CROSS_PROCESS_LIST_ENTRY( &list->free_list, pos ); + memset( entry->args, 0xcc, sizeof(entry->args) ); + } + + addr = VirtualAllocEx( process, NULL, 0x1234, MEM_COMMIT, PAGE_READWRITE ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualAlloc, NULL, 0x1234, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualAlloc, addr, 0x2000, + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, 0, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + VirtualProtectEx( process, (char *)addr + 0x333, 17, PAGE_READONLY, &old_prot ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualProtect, + (char *)addr + 0x333, 17, + PAGE_READONLY, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualProtect, addr, 0x1000, + PAGE_READONLY, 0, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + VirtualFreeEx( process, addr, 0, MEM_RELEASE ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualFree, addr, 0, + MEM_RELEASE, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualFree, addr, 0x2000, + MEM_RELEASE, 0, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr = (void *)0x123; + size = 0x321; + status = NtAllocateVirtualMemory( process, &addr, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READ ); + ok( status == STATUS_CONFLICTING_ADDRESSES || status == STATUS_INVALID_PARAMETER, + "NtAllocateVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualAlloc, addr, 0x321, + MEM_COMMIT, PAGE_EXECUTE_READ, 0, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualAlloc, addr, 0x321, + MEM_COMMIT, PAGE_EXECUTE_READ, status, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr = NULL; + size = 0x321; + status = NtAllocateVirtualMemory( process, &addr, 0, &size, 0, PAGE_EXECUTE_READ ); + ok( status == STATUS_INVALID_PARAMETER, "NtAllocateVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualAlloc, addr, 0x321, + 0, PAGE_EXECUTE_READ, 0, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualAlloc, addr, 0x321, + 0, PAGE_EXECUTE_READ, status, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr = NULL; + size = 0x4321; + status = NtAllocateVirtualMemory( process, &addr, 0, &size, MEM_RESERVE, PAGE_EXECUTE_READWRITE ); + ok( !status, "NtAllocateVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualAlloc, NULL, 0x4321, + MEM_RESERVE, PAGE_EXECUTE_READWRITE, 0, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualAlloc, addr, 0x5000, + MEM_RESERVE, PAGE_EXECUTE_READWRITE, 0, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + size = 0x4321; + status = NtAllocateVirtualMemory( process, &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE ); + ok( !status, "NtAllocateVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualAlloc, addr, 0x4321, + MEM_COMMIT, PAGE_READWRITE, 0, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualAlloc, addr, 0x5000, + MEM_COMMIT, PAGE_READWRITE, 0, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr2 = (char *)addr + 0x111; + size = 23; + status = NtProtectVirtualMemory( process, &addr2, &size, PAGE_EXECUTE_READWRITE, &old_prot ); + ok( !status, "NtProtectVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualProtect, (char *)addr + 0x111, 23, + PAGE_EXECUTE_READWRITE, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualProtect, addr, 0x1000, + PAGE_EXECUTE_READWRITE, 0, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr2 = (char *)addr + 0x222; + size = 34; + status = NtProtectVirtualMemory( process, &addr2, &size, PAGE_EXECUTE_WRITECOPY, &old_prot ); + ok( status == STATUS_INVALID_PARAMETER_4 || status == STATUS_INVALID_PAGE_PROTECTION, + "NtProtectVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualProtect, + (char *)addr + 0x222, 34, + PAGE_EXECUTE_WRITECOPY, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualProtect, + (char *)addr + 0x222, 34, + PAGE_EXECUTE_WRITECOPY, status, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + status = NtWriteVirtualMemory( process, (char *)addr + 0x1111, data, sizeof(data), &size ); + ok( !status, "NtWriteVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + ok( !entry, "not at end of list\n" ); + + addr2 = (char *)addr + 0x1234; + size = 45; + status = NtFreeVirtualMemory( process, &addr2, &size, MEM_DECOMMIT ); + ok( !status, "NtFreeVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualFree, (char *)addr + 0x1234, 45, + MEM_DECOMMIT, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualFree, addr2, 0x1000, + MEM_DECOMMIT, 0, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + size = 0; + status = NtFreeVirtualMemory( process, &addr, &size, MEM_RELEASE ); + ok( !status, "NtFreeVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualFree, addr, 0, + MEM_RELEASE, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualFree, addr, 0x5000, + MEM_RELEASE, 0, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + addr = (void *)0x123; + size = 0; + status = NtFreeVirtualMemory( process, &addr, &size, MEM_RELEASE ); + ok( status == STATUS_MEMORY_NOT_ALLOCATED || status == STATUS_INVALID_PARAMETER, + "NtFreeVirtualMemory failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualFree, addr, 0, + MEM_RELEASE, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualFree, addr, 0, + MEM_RELEASE, status, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + file = CreateFileA( "c:\\windows\\syswow64\\version.dll", GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + addr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, process, &addr, 0, 0, &offset, &size, ViewShare, 0, PAGE_READONLY ); + ok( NT_SUCCESS(status), "NtMapViewOfSection failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + ok( !entry, "list not empty\n" ); + + FlushInstructionCache( process, addr, 0x1234 ); + entry = pop_from_work_list( &list->work_list ); + entry = expect_cross_work_entry( list, entry, CrossProcessFlushCache, addr, 0x1234, + 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc ); + ok( !entry, "not at end of list\n" ); + + NtFlushInstructionCache( process, addr, 0x1234 ); + entry = pop_from_work_list( &list->work_list ); + if (current_machine != IMAGE_FILE_MACHINE_ARM64) + { + entry = expect_cross_work_entry( list, entry, CrossProcessFlushCache, addr, 0x1234, + 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc ); + } + ok( !entry, "not at end of list\n" ); + + WriteProcessMemory( process, (char *)addr + 0x1ffe, data, sizeof(data), &size ); + entry = pop_from_work_list( &list->work_list ); + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualProtect, + (char *)addr + 0x1000, 0x2000, 0x60000000 | PAGE_EXECUTE_WRITECOPY, + (current_machine != IMAGE_FILE_MACHINE_ARM64) ? 0 : 0xcccccccc, + 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualProtect, + (char *)addr + 0x1000, 0x2000, + 0x60000000 | PAGE_EXECUTE_WRITECOPY, 0, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessFlushCache, + (char *)addr + 0x1ffe, sizeof(data), + 0xcccccccc, 0xcccccccc, 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPreVirtualProtect, + (char *)addr + 0x1000, 0x2000, 0x60000000 | PAGE_EXECUTE_READ, + (current_machine != IMAGE_FILE_MACHINE_ARM64) ? 0 : 0xcccccccc, + 0xcccccccc, 0xcccccccc ); + entry = expect_cross_work_entry( list, entry, CrossProcessPostVirtualProtect, + (char *)addr + 0x1000, 0x2000, + 0x60000000 | PAGE_EXECUTE_READ, 0, 0xcccccccc, 0xcccccccc ); + ok( !entry, "not at end of list\n" ); + + status = NtUnmapViewOfSection( process, addr ); + ok( !status, "NtUnmapViewOfSection failed %lx\n", status ); + entry = pop_from_work_list( &list->work_list ); + ok( !entry, "list not empty\n" ); + + CloseHandle( mapping ); + CloseHandle( file ); + UnmapViewOfFile( list ); +} + +static void test_wow64_shared_info( HANDLE process ) +{ + ULONG i, peb_data[0x200], buffer[16]; + WOW64INFO *info = (WOW64INFO *)buffer; + ULONG_PTR peb_ptr; + NTSTATUS status; + SIZE_T res; + BOOLEAN wow64 = 0xcc; + + NtQueryInformationProcess( process, ProcessWow64Information, &peb_ptr, sizeof(peb_ptr), NULL ); + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlWow64GetSharedInfoProcess( process, &wow64, info ); + ok( !status, "RtlWow64GetSharedInfoProcess failed %lx\n", status ); + ok( wow64 == TRUE, "wrong wow64 %u\n", wow64 ); + todo_wine_if (!info->NativeSystemPageSize) /* not set in old wow64 */ + { + ok( info->NativeSystemPageSize == 0x1000, "wrong page size %lx\n", + info->NativeSystemPageSize ); + ok( info->CpuFlags == (native_machine == IMAGE_FILE_MACHINE_AMD64 ? WOW64_CPUFLAGS_MSFT64 : WOW64_CPUFLAGS_SOFTWARE), + "wrong flags %lx\n", info->CpuFlags ); + ok( info->NativeMachineType == native_machine, "wrong machine %x / %x\n", + info->NativeMachineType, native_machine ); + ok( info->EmulatedMachineType == IMAGE_FILE_MACHINE_I386, "wrong machine %x\n", + info->EmulatedMachineType ); + } + ok( buffer[sizeof(*info) / sizeof(ULONG)] == 0xcccccccc, "buffer set %lx\n", + buffer[sizeof(*info) / sizeof(ULONG)] ); + if (ReadProcessMemory( process, (void *)peb_ptr, peb_data, sizeof(peb_data), &res )) + { + ULONG limit = (sizeof(peb_data) - sizeof(info)) / sizeof(ULONG); + for (i = 0; i < limit; i++) + { + if (!memcmp( peb_data + i, info, sizeof(*info) )) + { + trace( "wow64info found at %lx\n", i * 4 ); + break; + } + } + ok( i < limit, "wow64info not found in PEB\n" ); + } + if (info->SectionHandle && info->CrossProcessWorkList) + test_cross_process_notifications( process, info->SectionHandle, info->CrossProcessWorkList ); + else + trace( "no WOW64INFO section handle\n" ); +} + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) +static void test_amd64_shared_info( HANDLE process ) +{ + ULONG i, peb_data[0x200], buffer[16]; + PROCESS_BASIC_INFORMATION proc_info; + NTSTATUS status; + SIZE_T res; + BOOLEAN wow64 = 0xcc; + struct arm64ec_shared_info *info = NULL; + + NtQueryInformationProcess( process, ProcessBasicInformation, &proc_info, sizeof(proc_info), NULL ); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlWow64GetSharedInfoProcess( process, &wow64, (WOW64INFO *)buffer ); + ok( !status, "RtlWow64GetSharedInfoProcess failed %lx\n", status ); + ok( !wow64, "wrong wow64 %u\n", wow64 ); + ok( buffer[0] == 0xcccccccc, "buffer initialized %lx\n", buffer[0] ); + + if (ReadProcessMemory( process, (void *)proc_info.PebBaseAddress, peb_data, sizeof(peb_data), &res )) + { + ULONG limit = (sizeof(peb_data) - sizeof(*info)) / sizeof(ULONG); + for (i = 0; i < limit; i++) + { + info = (struct arm64ec_shared_info *)(peb_data + i); + if (info->NativeMachineType == IMAGE_FILE_MACHINE_ARM64 && + info->EmulatedMachineType == IMAGE_FILE_MACHINE_AMD64) + { + trace( "shared info found at %lx\n", i * 4 ); + break; + } + } + ok( i < limit, "shared info not found in PEB\n" ); + } + if (info && info->SectionHandle && info->CrossProcessWorkList) + test_cross_process_notifications( process, info->SectionHandle, info->CrossProcessWorkList ); + else + trace( "no shared info section handle\n" ); +} +#endif // !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) + +static void test_peb_teb(void) +{ + PROCESS_BASIC_INFORMATION proc_info; + THREAD_BASIC_INFORMATION info; + PROCESS_INFORMATION pi; + STARTUPINFOA si = {0}; + NTSTATUS status; + void *redir; + SIZE_T res; + BOOL ret; + TEB teb; + PEB peb; + TEB32 teb32; + PEB32 peb32; + RTL_USER_PROCESS_PARAMETERS params; + RTL_USER_PROCESS_PARAMETERS32 params32; + ULONG_PTR peb_ptr; + ULONG buffer[16]; + WOW64INFO *wow64info = (WOW64INFO *)buffer; + BOOLEAN wow64; + + Wow64DisableWow64FsRedirection( &redir ); + + if (CreateProcessA( "C:\\windows\\syswow64\\msinfo32.exe", NULL, NULL, NULL, + FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi )) + { + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationThread( pi.hThread, ThreadBasicInformation, &info, sizeof(info), NULL ); + ok( !status, "ThreadBasicInformation failed %lx\n", status ); + if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0; + ok( res == sizeof(teb), "wrong len %Ix\n", res ); + ok( teb.Tib.Self == info.TebBaseAddress, "wrong teb %p / %p\n", teb.Tib.Self, info.TebBaseAddress ); + if (is_wow64) + { + ok( !!teb.GdiBatchCount, "GdiBatchCount not set\n" ); + ok( (char *)info.TebBaseAddress + teb.WowTebOffset == ULongToPtr(teb.GdiBatchCount) || + broken(!NtCurrentTeb()->WowTebOffset), /* pre-win10 */ + "wrong teb offset %ld\n", teb.WowTebOffset ); + } + else + { + ok( !teb.GdiBatchCount, "GdiBatchCount set\n" ); + ok( teb.WowTebOffset == 0x2000 || + broken( !teb.WowTebOffset || teb.WowTebOffset == 1 ), /* pre-win10 */ + "wrong teb offset %ld\n", teb.WowTebOffset ); + ok( (char *)teb.Tib.ExceptionList == (char *)info.TebBaseAddress + 0x2000, + "wrong Tib.ExceptionList %p / %p\n", + (char *)teb.Tib.ExceptionList, (char *)info.TebBaseAddress + 0x2000 ); + if (!ReadProcessMemory( pi.hProcess, teb.Tib.ExceptionList, &teb32, sizeof(teb32), &res )) res = 0; + ok( res == sizeof(teb32), "wrong len %Ix\n", res ); + ok( (char *)ULongToPtr(teb32.Peb) == (char *)teb.Peb + 0x1000 || + broken( ULongToPtr(teb32.Peb) != teb.Peb ), /* vista */ + "wrong peb %p / %p\n", ULongToPtr(teb32.Peb), teb.Peb ); + } + + status = NtQueryInformationProcess( pi.hProcess, ProcessBasicInformation, + &proc_info, sizeof(proc_info), NULL ); + ok( !status, "ProcessBasicInformation failed %lx\n", status ); + ok( proc_info.PebBaseAddress == teb.Peb, "wrong peb %p / %p\n", proc_info.PebBaseAddress, teb.Peb ); + + status = NtQueryInformationProcess( pi.hProcess, ProcessWow64Information, + &peb_ptr, sizeof(peb_ptr), NULL ); + ok( !status, "ProcessWow64Information failed %lx\n", status ); + ok( (void *)peb_ptr == (is_wow64 ? teb.Peb : ULongToPtr(teb32.Peb)), + "wrong peb %p\n", (void *)peb_ptr ); + + if (!ReadProcessMemory( pi.hProcess, proc_info.PebBaseAddress, &peb, sizeof(peb), &res )) res = 0; + ok( res == sizeof(peb), "wrong len %Ix\n", res ); + ok( !peb.BeingDebugged, "BeingDebugged is %u\n", peb.BeingDebugged ); + if (!is_wow64) + { + if (!ReadProcessMemory( pi.hProcess, ULongToPtr(teb32.Peb), &peb32, sizeof(peb32), &res )) res = 0; + ok( res == sizeof(peb32), "wrong len %Ix\n", res ); + ok( !peb32.BeingDebugged, "BeingDebugged is %u\n", peb32.BeingDebugged ); + } + + if (!ReadProcessMemory( pi.hProcess, peb.ProcessParameters, ¶ms, sizeof(params), &res )) res = 0; + ok( res == sizeof(params), "wrong len %Ix\n", res ); +#define CHECK_STR(name) \ + ok( (char *)params.name.Buffer >= (char *)peb.ProcessParameters && \ + (char *)params.name.Buffer < (char *)peb.ProcessParameters + params.Size, \ + "wrong " #name " ptr %p / %p-%p\n", params.name.Buffer, peb.ProcessParameters, \ + (char *)peb.ProcessParameters + params.Size ) + CHECK_STR( ImagePathName ); + CHECK_STR( CommandLine ); + CHECK_STR( WindowTitle ); + CHECK_STR( Desktop ); + CHECK_STR( ShellInfo ); +#undef CHECK_STR + if (!is_wow64) + { + ok( peb32.ProcessParameters && ULongToPtr(peb32.ProcessParameters) != peb.ProcessParameters, + "wrong ptr32 %p / %p\n", ULongToPtr(peb32.ProcessParameters), peb.ProcessParameters ); + if (!ReadProcessMemory( pi.hProcess, ULongToPtr(peb32.ProcessParameters), ¶ms32, sizeof(params32), &res )) res = 0; + ok( res == sizeof(params32), "wrong len %Ix\n", res ); +#define CHECK_STR(name) \ + ok( ULongToPtr(params32.name.Buffer) >= ULongToPtr(peb32.ProcessParameters) && \ + ULongToPtr(params32.name.Buffer) < ULongToPtr(peb32.ProcessParameters + params32.Size), \ + "wrong " #name " ptr %lx / %lx-%lx\n", params32.name.Buffer, peb32.ProcessParameters, \ + peb32.ProcessParameters + params.Size ); \ + ok( params32.name.Length == params.name.Length, "wrong " #name "len %u / %u\n", \ + params32.name.Length, params.name.Length ) + CHECK_STR( ImagePathName ); + CHECK_STR( CommandLine ); + CHECK_STR( WindowTitle ); + CHECK_STR( Desktop ); + CHECK_STR( ShellInfo ); +#undef CHECK_STR + ok( params32.EnvironmentSize == params.EnvironmentSize, "wrong size %lu / %Iu\n", + params32.EnvironmentSize, params.EnvironmentSize ); + } + + ResumeThread( pi.hThread ); + WaitForInputIdle( pi.hProcess, 1000 ); + + if (pRtlWow64GetSharedInfoProcess) test_wow64_shared_info( pi.hProcess ); + else win_skip( "RtlWow64GetSharedInfoProcess not supported\n" ); + + ret = DebugActiveProcess( pi.dwProcessId ); + ok( ret, "debugging failed\n" ); + if (!ReadProcessMemory( pi.hProcess, proc_info.PebBaseAddress, &peb, sizeof(peb), &res )) res = 0; + ok( res == sizeof(peb), "wrong len %Ix\n", res ); + ok( peb.BeingDebugged == !!ret, "BeingDebugged is %u\n", peb.BeingDebugged ); + if (!is_wow64) + { + if (!ReadProcessMemory( pi.hProcess, ULongToPtr(teb32.Peb), &peb32, sizeof(peb32), &res )) res = 0; + ok( res == sizeof(peb32), "wrong len %Ix\n", res ); + ok( peb32.BeingDebugged == !!ret, "BeingDebugged is %u\n", peb32.BeingDebugged ); + } + + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) + if (is_win64 && native_machine == IMAGE_FILE_MACHINE_ARM64 && + create_process_machine( (char *)"C:\\windows\\system32\\regsvr32.exe /?", CREATE_SUSPENDED, + IMAGE_FILE_MACHINE_AMD64, &pi )) + { + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationThread( pi.hThread, ThreadBasicInformation, &info, sizeof(info), NULL ); + ok( !status, "ThreadBasicInformation failed %lx\n", status ); + if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0; + ok( res == sizeof(teb), "wrong len %Ix\n", res ); + ok( teb.Tib.Self == info.TebBaseAddress, "wrong teb %p / %p\n", teb.Tib.Self, info.TebBaseAddress ); + ok( !teb.GdiBatchCount, "GdiBatchCount set\n" ); + ok( !teb.WowTebOffset, "wrong teb offset %ld\n", teb.WowTebOffset ); + ok( !teb.Tib.ExceptionList, "wrong Tib.ExceptionList %p\n", (char *)teb.Tib.ExceptionList ); + + status = NtQueryInformationProcess( pi.hProcess, ProcessBasicInformation, + &proc_info, sizeof(proc_info), NULL ); + ok( !status, "ProcessBasicInformation failed %lx\n", status ); + ok( proc_info.PebBaseAddress == teb.Peb, "wrong peb %p / %p\n", proc_info.PebBaseAddress, teb.Peb ); + + status = NtQueryInformationProcess( pi.hProcess, ProcessWow64Information, + &peb_ptr, sizeof(peb_ptr), NULL ); + ok( !status, "ProcessWow64Information failed %lx\n", status ); + ok( !peb_ptr, "wrong peb %p\n", (void *)peb_ptr ); + + if (!ReadProcessMemory( pi.hProcess, proc_info.PebBaseAddress, &peb, sizeof(peb), &res )) res = 0; + ok( res == sizeof(peb), "wrong len %Ix\n", res ); + ok( !peb.BeingDebugged, "BeingDebugged is %u\n", peb.BeingDebugged ); + + ResumeThread( pi.hThread ); + WaitForInputIdle( pi.hProcess, 1000 ); + + test_amd64_shared_info( pi.hProcess ); + + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } +#endif + + if (CreateProcessA( "C:\\windows\\system32\\msinfo32.exe", NULL, NULL, NULL, + FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi )) + { + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationThread( pi.hThread, ThreadBasicInformation, &info, sizeof(info), NULL ); + ok( !status, "ThreadBasicInformation failed %lx\n", status ); + if (!is_wow64) + { + if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0; + ok( res == sizeof(teb), "wrong len %Ix\n", res ); + ok( teb.Tib.Self == info.TebBaseAddress, "wrong teb %p / %p\n", + teb.Tib.Self, info.TebBaseAddress ); + ok( !teb.GdiBatchCount, "GdiBatchCount set\n" ); + ok( !teb.WowTebOffset || broken( teb.WowTebOffset == 1 ), /* vista */ + "wrong teb offset %ld\n", teb.WowTebOffset ); + } + else ok( !info.TebBaseAddress, "got teb %p\n", info.TebBaseAddress ); + + status = NtQueryInformationProcess( pi.hProcess, ProcessBasicInformation, + &proc_info, sizeof(proc_info), NULL ); + ok( !status, "ProcessBasicInformation failed %lx\n", status ); + if (is_wow64) + ok( !proc_info.PebBaseAddress || + broken( (char *)proc_info.PebBaseAddress >= (char *)0x7f000000 ), /* vista */ + "wrong peb %p\n", proc_info.PebBaseAddress ); + else + ok( proc_info.PebBaseAddress == teb.Peb, "wrong peb %p / %p\n", + proc_info.PebBaseAddress, teb.Peb ); + + ResumeThread( pi.hThread ); + WaitForInputIdle( pi.hProcess, 1000 ); + + if (pRtlWow64GetSharedInfoProcess) + { + wow64 = 0xcc; + memset( buffer, 0xcc, sizeof(buffer) ); + status = pRtlWow64GetSharedInfoProcess( pi.hProcess, &wow64, wow64info ); + ok( !status, "RtlWow64GetSharedInfoProcess failed %lx\n", status ); + ok( !wow64, "wrong wow64 %u\n", wow64 ); + ok( buffer[0] == 0xcccccccc, "buffer set %lx\n", buffer[0] ); + } + + TerminateProcess( pi.hProcess, 0 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + + Wow64RevertWow64FsRedirection( redir ); + +#ifndef _WIN64 + if (is_wow64) + { + PEB64 *peb64; + TEB64 *teb64 = (TEB64 *)NtCurrentTeb()->GdiBatchCount; + + ok( !!teb64, "GdiBatchCount not set\n" ); + ok( (char *)NtCurrentTeb() + NtCurrentTeb()->WowTebOffset == (char *)teb64 || + broken(!NtCurrentTeb()->WowTebOffset), /* pre-win10 */ + "wrong WowTebOffset %lx (%p/%p)\n", NtCurrentTeb()->WowTebOffset, teb64, NtCurrentTeb() ); + ok( (char *)teb64 + 0x2000 == (char *)NtCurrentTeb(), "unexpected diff %p / %p\n", + teb64, NtCurrentTeb() ); + ok( (char *)teb64 + teb64->WowTebOffset == (char *)NtCurrentTeb() || + broken( !teb64->WowTebOffset || teb64->WowTebOffset == 1 ), /* pre-win10 */ + "wrong WowTebOffset %lx (%p/%p)\n", teb64->WowTebOffset, teb64, NtCurrentTeb() ); + ok( !teb64->GdiBatchCount, "GdiBatchCount set %lx\n", teb64->GdiBatchCount ); + ok( teb64->Tib.ExceptionList == PtrToUlong( NtCurrentTeb() ), "wrong Tib.ExceptionList %s / %p\n", + wine_dbgstr_longlong(teb64->Tib.ExceptionList), NtCurrentTeb() ); + ok( teb64->Tib.Self == PtrToUlong( teb64 ), "wrong Tib.Self %s / %p\n", + wine_dbgstr_longlong(teb64->Tib.Self), teb64 ); + ok( teb64->StaticUnicodeString.Buffer == PtrToUlong( teb64->StaticUnicodeBuffer ), + "wrong StaticUnicodeString %s / %p\n", + wine_dbgstr_longlong(teb64->StaticUnicodeString.Buffer), teb64->StaticUnicodeBuffer ); + ok( teb64->ClientId.UniqueProcess == GetCurrentProcessId(), "wrong pid %s / %lx\n", + wine_dbgstr_longlong(teb64->ClientId.UniqueProcess), GetCurrentProcessId() ); + ok( teb64->ClientId.UniqueThread == GetCurrentThreadId(), "wrong tid %s / %lx\n", + wine_dbgstr_longlong(teb64->ClientId.UniqueThread), GetCurrentThreadId() ); + peb64 = ULongToPtr( teb64->Peb ); + ok( peb64->ImageBaseAddress == PtrToUlong( NtCurrentTeb()->Peb->ImageBaseAddress ), + "wrong ImageBaseAddress %s / %p\n", + wine_dbgstr_longlong(peb64->ImageBaseAddress), NtCurrentTeb()->Peb->ImageBaseAddress); + ok( peb64->OSBuildNumber == NtCurrentTeb()->Peb->OSBuildNumber, "wrong OSBuildNumber %lx / %lx\n", + peb64->OSBuildNumber, NtCurrentTeb()->Peb->OSBuildNumber ); + ok( peb64->OSPlatformId == NtCurrentTeb()->Peb->OSPlatformId, "wrong OSPlatformId %lx / %lx\n", + peb64->OSPlatformId, NtCurrentTeb()->Peb->OSPlatformId ); + ok( peb64->AnsiCodePageData == PtrToUlong( NtCurrentTeb()->Peb->AnsiCodePageData ), + "wrong AnsiCodePageData %I64x / %p\n", + peb64->AnsiCodePageData, NtCurrentTeb()->Peb->AnsiCodePageData ); + ok( peb64->OemCodePageData == PtrToUlong( NtCurrentTeb()->Peb->OemCodePageData ), + "wrong OemCodePageData %I64x / %p\n", + peb64->OemCodePageData, NtCurrentTeb()->Peb->OemCodePageData ); + ok( peb64->UnicodeCaseTableData == PtrToUlong( NtCurrentTeb()->Peb->UnicodeCaseTableData ), + "wrong UnicodeCaseTableData %I64x / %p\n", + peb64->UnicodeCaseTableData, NtCurrentTeb()->Peb->UnicodeCaseTableData ); + return; + } +#endif + ok( !NtCurrentTeb()->GdiBatchCount, "GdiBatchCount set to %lx\n", NtCurrentTeb()->GdiBatchCount ); + ok( !NtCurrentTeb()->WowTebOffset || broken( NtCurrentTeb()->WowTebOffset == 1 ), /* vista */ + "WowTebOffset set to %lx\n", NtCurrentTeb()->WowTebOffset ); +} + +static void test_selectors(void) +{ +#ifndef __arm__ + THREAD_DESCRIPTOR_INFORMATION info; + NTSTATUS status; + ULONG base, limit, sel, retlen; + I386_CONTEXT context = { CONTEXT_I386_CONTROL | CONTEXT_I386_SEGMENTS }; + +#ifdef _WIN64 + if (!pRtlWow64GetThreadSelectorEntry) + { + win_skip( "RtlWow64GetThreadSelectorEntry not supported\n" ); + return; + } + if (!pRtlWow64GetThreadContext || pRtlWow64GetThreadContext( GetCurrentThread(), &context )) + { + /* hardcoded values */ +#ifdef __arm64ec__ + context.SegCs = 0x23; + context.SegSs = 0x2b; + context.SegFs = 0x53; +#elif defined __x86_64__ +#ifdef _MSC_VER + context.SegFs = __readsegfs(); + context.SegSs = __readsegss(); +#else + __asm__( "movw %%fs,%0" : "=m" (context.SegFs) ); + __asm__( "movw %%ss,%0" : "=m" (context.SegSs) ); +#endif +#else + context.SegCs = 0x1b; + context.SegSs = 0x23; + context.SegFs = 0x3b; +#endif + } +#define GET_ENTRY(info,size,ret) \ + pRtlWow64GetThreadSelectorEntry( GetCurrentThread(), info, size, ret ) + +#else + GetThreadContext( GetCurrentThread(), &context ); +#define GET_ENTRY(info,size,ret) \ + NtQueryInformationThread( GetCurrentThread(), ThreadDescriptorTableEntry, info, size, ret ) +#endif + + trace( "cs %04lx ss %04lx fs %04lx\n", context.SegCs, context.SegSs, context.SegFs ); + retlen = 0xdeadbeef; + info.Selector = 0; + status = GET_ENTRY( &info, sizeof(info) - 1, &retlen ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "wrong status %lx\n", status ); + ok( retlen == 0xdeadbeef, "len set %lu\n", retlen ); + + retlen = 0xdeadbeef; + status = GET_ENTRY( &info, sizeof(info) + 1, &retlen ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "wrong status %lx\n", status ); + ok( retlen == 0xdeadbeef, "len set %lu\n", retlen ); + + retlen = 0xdeadbeef; + status = GET_ENTRY( NULL, 0, &retlen ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "wrong status %lx\n", status ); + ok( retlen == 0xdeadbeef, "len set %lu\n", retlen ); + + status = GET_ENTRY( &info, sizeof(info), NULL ); + ok( !status, "wrong status %lx\n", status ); + + for (info.Selector = 0; info.Selector < 0x100; info.Selector++) + { + retlen = 0xdeadbeef; + status = GET_ENTRY( &info, sizeof(info), &retlen ); + base = (info.Entry.BaseLow | + (info.Entry.HighWord.Bytes.BaseMid << 16) | + (info.Entry.HighWord.Bytes.BaseHi << 24)); + limit = (info.Entry.LimitLow | info.Entry.HighWord.Bits.LimitHi << 16); + sel = info.Selector | 3; + + if (sel == 0x03) /* null selector */ + { + ok( !status, "wrong status %lx\n", status ); + ok( retlen == sizeof(info.Entry), "len set %lu\n", retlen ); + ok( !base, "wrong base %lx\n", base ); + ok( !limit, "wrong limit %lx\n", limit ); + ok( !info.Entry.HighWord.Bytes.Flags1, "wrong flags1 %x\n", info.Entry.HighWord.Bytes.Flags1 ); + ok( !info.Entry.HighWord.Bytes.Flags2, "wrong flags2 %x\n", info.Entry.HighWord.Bytes.Flags2 ); + } + else if (sel == context.SegCs) /* 32-bit code selector */ + { + ok( !status, "wrong status %lx\n", status ); + ok( retlen == sizeof(info.Entry), "len set %lu\n", retlen ); + ok( !base, "wrong base %lx\n", base ); + ok( limit == 0xfffff, "wrong limit %lx\n", limit ); + ok( info.Entry.HighWord.Bits.Type == 0x1b, "wrong type %x\n", info.Entry.HighWord.Bits.Type ); + ok( info.Entry.HighWord.Bits.Dpl == 3, "wrong dpl %x\n", info.Entry.HighWord.Bits.Dpl ); + ok( info.Entry.HighWord.Bits.Pres, "wrong pres\n" ); + ok( !info.Entry.HighWord.Bits.Sys, "wrong sys\n" ); + ok( info.Entry.HighWord.Bits.Default_Big, "wrong big\n" ); + ok( info.Entry.HighWord.Bits.Granularity, "wrong granularity\n" ); + } + else if (sel == context.SegSs) /* 32-bit data selector */ + { + ok( !status, "wrong status %lx\n", status ); + ok( retlen == sizeof(info.Entry), "len set %lu\n", retlen ); + ok( !base, "wrong base %lx\n", base ); + ok( limit == 0xfffff, "wrong limit %lx\n", limit ); + ok( info.Entry.HighWord.Bits.Type == 0x13, "wrong type %x\n", info.Entry.HighWord.Bits.Type ); + ok( info.Entry.HighWord.Bits.Dpl == 3, "wrong dpl %x\n", info.Entry.HighWord.Bits.Dpl ); + ok( info.Entry.HighWord.Bits.Pres, "wrong pres\n" ); + ok( !info.Entry.HighWord.Bits.Sys, "wrong sys\n" ); + ok( info.Entry.HighWord.Bits.Default_Big, "wrong big\n" ); + ok( info.Entry.HighWord.Bits.Granularity, "wrong granularity\n" ); + } + else if (sel == context.SegFs) /* TEB selector */ + { + ok( !status, "wrong status %lx\n", status ); + ok( retlen == sizeof(info.Entry), "len set %lu\n", retlen ); +#ifdef _WIN64 + if (NtCurrentTeb()->WowTebOffset == 0x2000) + ok( base == (ULONG_PTR)NtCurrentTeb() + 0x2000, "wrong base %lx / %p\n", + base, NtCurrentTeb() ); +#else + ok( base == (ULONG_PTR)NtCurrentTeb(), "wrong base %lx / %p\n", base, NtCurrentTeb() ); +#endif + ok( limit == 0xfff || broken(limit == 0x4000), /* <= win8 */ + "wrong limit %lx\n", limit ); + ok( info.Entry.HighWord.Bits.Type == 0x13, "wrong type %x\n", info.Entry.HighWord.Bits.Type ); + ok( info.Entry.HighWord.Bits.Dpl == 3, "wrong dpl %x\n", info.Entry.HighWord.Bits.Dpl ); + ok( info.Entry.HighWord.Bits.Pres, "wrong pres\n" ); + ok( !info.Entry.HighWord.Bits.Sys, "wrong sys\n" ); + ok( info.Entry.HighWord.Bits.Default_Big, "wrong big\n" ); + ok( !info.Entry.HighWord.Bits.Granularity, "wrong granularity\n" ); + } + else if (!status) + { + ok( retlen == sizeof(info.Entry), "len set %lu\n", retlen ); + trace( "succeeded for %lx base %lx limit %lx type %x\n", + sel, base, limit, info.Entry.HighWord.Bits.Type ); + } + else + { + ok( status == STATUS_UNSUCCESSFUL || + ((sel & 4) && (status == STATUS_NO_LDT)) || + broken( status == STATUS_ACCESS_VIOLATION), /* <= win8 */ + "%lx: wrong status %lx\n", info.Selector, status ); + ok( retlen == 0xdeadbeef, "len set %lu\n", retlen ); + } + } +#undef GET_ENTRY +#endif /* __arm__ */ +} + +static void test_image_mappings(void) +{ + MEM_EXTENDED_PARAMETER ext = { .Type = MemExtendedParameterImageMachine }; + HANDLE file, mapping, process = GetCurrentProcess(); + NTSTATUS status; + SIZE_T size; + LARGE_INTEGER offset; + void *ptr; + + if (!pNtMapViewOfSectionEx) + { + win_skip( "NtMapViewOfSectionEx() not supported\n" ); + return; + } + + offset.QuadPart = 0; + file = CreateFileA( "c:\\windows\\system32\\version.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + CloseHandle( file ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_AMD64; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + if (status == STATUS_INVALID_PARAMETER) + { + win_skip( "MemExtendedParameterImageMachine not supported\n" ); + NtClose( mapping ); + return; + } + if (current_machine == IMAGE_FILE_MACHINE_AMD64) + { + ok( status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, + "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + } + else if (current_machine == IMAGE_FILE_MACHINE_ARM64) + { + todo_wine + ok( status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH, "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + } + else ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_I386; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + if (current_machine == IMAGE_FILE_MACHINE_I386) + { + ok( status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, + "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + } + else ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_ARM64; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + if (native_machine == IMAGE_FILE_MACHINE_ARM64) + { + switch (current_machine) + { + case IMAGE_FILE_MACHINE_ARM64: + ok( status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, + "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + break; + case IMAGE_FILE_MACHINE_AMD64: + ok( status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH, "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + break; + default: + ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + break; + } + } + else ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_R3000; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + + ptr = NULL; + size = 0; + ext.ULong = 0; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, + "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + + NtClose( mapping ); + + if (is_wow64) + { + file = CreateFileA( "c:\\windows\\sysnative\\version.dll", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + CloseHandle( file ); + + ptr = NULL; + size = 0; + ext.ULong = native_machine; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_SUCCESS || status == STATUS_IMAGE_NOT_AT_BASE, + "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_I386; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + NtClose( mapping ); + } + else if (native_machine == IMAGE_FILE_MACHINE_AMD64 || native_machine == IMAGE_FILE_MACHINE_ARM64) + { + file = CreateFileA( "c:\\windows\\syswow64\\version.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + CloseHandle( file ); + + ptr = NULL; + size = 0; + ext.ULong = native_machine; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_NOT_SUPPORTED, "NtMapViewOfSection returned %08lx\n", status ); + + ptr = NULL; + size = 0; + ext.ULong = IMAGE_FILE_MACHINE_I386; + status = pNtMapViewOfSectionEx( mapping, process, &ptr, &offset, &size, 0, PAGE_READONLY, &ext, 1 ); + ok( status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH, "NtMapViewOfSection returned %08lx\n", status ); + NtUnmapViewOfSection( process, ptr ); + NtClose( mapping ); + } +} + +static DWORD hook_code[] = +{ + 0x58000048, /* ldr x8, 1f */ + 0xd61f0100, /* br x8 */ + 0, 0 /* 1: .quad ptr */ +}; + +static const DWORD log_params_code[] = +{ + 0x10008009, /* adr x9, .+0x1000 */ + 0xf940012a, /* ldr x10, [x9] */ + 0xa8810540, /* stp x0, x1, [x10], #0x10 */ + 0xa8810d42, /* stp x2, x3, [x10], #0x10 */ + 0xa8811544, /* stp x4, x5, [x10], #0x10 */ + 0xa8811d46, /* stp x6, x7, [x10], #0x10 */ + 0xf900012a, /* str x10, [x9] */ + 0xf9400520, /* ldr x0, [x9, #0x8] */ + 0xd65f03c0, /* ret */ +}; + +static void CALLBACK dummy_apc( ULONG_PTR arg ) +{ +} + +struct expected_notification +{ + UINT nb_args; + ULONG64 args[6]; +}; + +static void reset_results( ULONG64 *results ) +{ + memset( results + 1, 0xcc, 0x1000 - sizeof(*results) ); + results[0] = (ULONG_PTR)(results + 2); +} + +#define expect_notifications(results, count, expect, syscall) \ + expect_notifications_(results, count, expect, syscall, __LINE__) +static void expect_notifications_( ULONG64 *results, UINT count, const struct expected_notification *expect, + BOOL syscall, int line ) +{ + ULONG64 *regs = results + 2; + UINT i, j, len = (results[0] - (ULONG_PTR)regs) / 8 / sizeof(*regs); + +#ifdef _WIN64 + if (syscall) + { + CHPE_V2_CPU_AREA_INFO *cpu_area = NtCurrentTeb()->ChpeV2CpuAreaInfo; + if (cpu_area && cpu_area->InSyscallCallback) count = 0; + } +#endif + + ok_(__FILE__,line)( count == len, "wrong notification count %u / %u\n", len, count ); + for (i = 0; i < min( count, len ); i++, expect++, regs += 8) + for (j = 0; j < expect->nb_args; j++) + ok_(__FILE__,line)( regs[j] == expect->args[j], "%u: wrong args[%u] %I64x / %I64x\n", + i, j, regs[j], expect->args[j] ); + reset_results( results ); +} + +static void add_work_item( CROSS_PROCESS_WORK_LIST *list, UINT id, ULONG64 addr, ULONG64 size, + UINT arg0, UINT arg1, UINT arg2, UINT arg3 ) +{ + CROSS_PROCESS_WORK_ENTRY *entry = pop_from_free_list( &list->free_list ); + + entry->id = id; + entry->addr = addr; + entry->size = size; + entry->args[0] = arg0; + entry->args[1] = arg1; + entry->args[2] = arg2; + entry->args[3] = arg3; + push_onto_work_list( &list->work_list, entry ); +} + +static void process_work_items(void) +{ +#ifdef _WIN64 + if (pProcessPendingCrossProcessEmulatorWork) + { + pProcessPendingCrossProcessEmulatorWork(); + return; + } +#endif + QueueUserAPC( dummy_apc, GetCurrentThread(), 0 ); + SleepEx( 1, TRUE ); +} + +static BYTE old_code[sizeof(hook_code)]; + +static void *hook_notification_function( HMODULE module, const char *win32_name, const char *win64_name ) +{ + BYTE *ptr; + BOOL ret; + + if (current_machine == IMAGE_FILE_MACHINE_AMD64) + { + static const BYTE fast_forward[] = { 0x48, 0x8b, 0xc4, 0x48, 0x89, 0x58, 0x20, 0x55, 0x5d, 0xe9 }; + + if (!(ptr = pRtlFindExportedRoutineByName( module, win64_name ))) + { + skip( "%s not exported\n", win64_name ); + return NULL; + } + if (memcmp( ptr, fast_forward, sizeof(fast_forward) )) + { + skip( "unrecognized x64 thunk for %s\n", win64_name ); + return NULL; + } + ptr += sizeof(fast_forward); + ptr += sizeof(LONG) + *(LONG *)ptr; + } + else if (!(ptr = pRtlFindExportedRoutineByName( module, win32_name ))) + { + skip( "%s not exported\n", win32_name ); + return NULL; + } + + memcpy( old_code, ptr, sizeof(old_code) ); + ret = WriteProcessMemory( GetCurrentProcess(), ptr, hook_code, sizeof(hook_code), NULL ); + ok( ret, "hooking failed %p %lu\n", ptr, GetLastError() ); + return ptr; +} + +static void test_notifications( HMODULE module, CROSS_PROCESS_WORK_LIST *list ) +{ + void *code, *ptr, *addr = NULL; + DWORD old_prot; + SIZE_T size; + ULONG64 *results; + NTSTATUS status; + HANDLE file, mapping; + + code = VirtualAlloc( NULL, 0x2000, MEM_COMMIT, PAGE_READWRITE ); + memcpy( code, log_params_code, sizeof(log_params_code) ); + VirtualProtect( code, 0x1000, PAGE_EXECUTE_READ, &old_prot ); + *(void **)&hook_code[2] = code; + + results = (ULONG64 *)((char *)code + 0x1000); + reset_results( results ); + + file = CreateFileA( "c:\\windows\\system32\\version.dll", GENERIC_READ | GENERIC_EXECUTE, + FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + ok( file != INVALID_HANDLE_VALUE, "Failed to open version.dll\n" ); + mapping = CreateFileMappingA( file, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL ); + ok( mapping != 0, "CreateFileMapping failed\n" ); + + if ((ptr = hook_notification_function( module, "BTCpuNotifyMemoryAlloc", "NotifyMemoryAlloc" ))) + { + struct expected_notification expect_cross[2] = + { + { 6, { 0x1234567890, 0x6543210000, MEM_COMMIT, PAGE_EXECUTE_READ, 0, 0 } }, + { 6, { 0x1234567890, 0x6543210000, MEM_COMMIT, PAGE_EXECUTE_READ, 1, 0xdeadbeef } } + }; + struct expected_notification expect_alloc[2] = + { + { 6, { 0, 0x123456, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, 0, 0 } }, + { 6, { 0, 0x124000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, 1, 0 } } + }; + + add_work_item( list, CrossProcessPreVirtualAlloc, expect_cross[0].args[0], expect_cross[0].args[1], + expect_cross[0].args[2], expect_cross[0].args[3], 0, 0 ); + add_work_item( list, CrossProcessPostVirtualAlloc, expect_cross[1].args[0], expect_cross[1].args[1], + expect_cross[1].args[2], expect_cross[1].args[3], 0xdeadbeef, 0 ); + process_work_items(); + expect_notifications( results, 2, expect_cross, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + size = expect_alloc[0].args[1]; + status = NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE ); + ok( !status, "NtAllocateVirtualMemory failed %lx\n", status ); + expect_alloc[1].args[0] = (ULONG_PTR)addr; + expect_notifications( results, 2, expect_alloc, TRUE ); + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyMemoryProtect", "NotifyMemoryProtect" ))) + { + struct expected_notification expect_cross[2] = + { + { 5, { 0x1234567890, 0x6543210000, PAGE_READWRITE, 0, 0 } }, + { 5, { 0x1234567890, 0x6543210000, PAGE_READWRITE, 1, 0xdeadbeef } } + }; + struct expected_notification expect_protect[2] = + { + { 5, { 0, 0x123456, PAGE_EXECUTE_READ, 0, 0 } }, + { 5, { 0, 0x124000, PAGE_EXECUTE_READ, 1, 0 } } + }; + + reset_results( results ); + add_work_item( list, CrossProcessPreVirtualProtect, expect_cross[0].args[0], + expect_cross[0].args[1], expect_cross[0].args[2], 0, 0, 0 ); + add_work_item( list, CrossProcessPostVirtualProtect, expect_cross[1].args[0], + expect_cross[1].args[1], expect_cross[1].args[2], 0xdeadbeef, 0, 0 ); + process_work_items(); + expect_notifications( results, 2, expect_cross, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + expect_protect[1].args[0] = (ULONG_PTR)addr; + addr = (char *)addr + 0x123; + expect_protect[0].args[0] = (ULONG_PTR)addr; + size = expect_protect[0].args[1]; + status = NtProtectVirtualMemory( GetCurrentProcess(), &addr, &size, PAGE_EXECUTE_READ, &old_prot ); + ok( !status, "NtProtectVirtualMemory failed %lx\n", status ); + expect_notifications( results, 2, expect_protect, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + reset_results( results ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyMemoryFree", "NotifyMemoryFree" ))) + { + struct expected_notification expect_cross[2] = + { + { 5, { 0x1234567890, 0x6543210000, MEM_RELEASE, 0, 0 } }, + { 5, { 0x1234567890, 0x6543210000, MEM_RELEASE, 1, 0xdeadbeef } } + }; + struct expected_notification expect_free[2] = + { + { 5, { 0, 0x123456, MEM_RELEASE, 0, 0 } }, + { 5, { 0, 0x124000, MEM_RELEASE, 1, 0 } } + }; + + add_work_item( list, CrossProcessPreVirtualFree, expect_cross[0].args[0], + expect_cross[0].args[1], expect_cross[0].args[2], 0, 0, 0 ); + add_work_item( list, CrossProcessPostVirtualFree, expect_cross[1].args[0], + expect_cross[1].args[1], expect_cross[1].args[2], 0xdeadbeef, 0, 0 ); + process_work_items(); + expect_notifications( results, 2, expect_cross, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + expect_free[0].args[0] = (ULONG_PTR)addr; + expect_free[1].args[0] = (ULONG_PTR)addr; + size = expect_free[0].args[1]; + status = NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE ); + ok( !status, "NtFreeVirtualMemory failed %lx\n", status ); + expect_notifications( results, 2, expect_free, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyMemoryDirty", "BTCpu64NotifyMemoryDirty" ))) + { + struct expected_notification expect = { 2, { 0x1234567890, 0x6543210000 } }; + + add_work_item( list, CrossProcessMemoryWrite, expect.args[0], expect.args[1], 0, 0, 0, 0 ); + process_work_items(); + expect_notifications( results, 1, &expect, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuFlushInstructionCache2", "BTCpu64FlushInstructionCache" ))) + { + struct expected_notification expect_cross = { 2, { 0x1234567890, 0x6543210000 } }; + struct expected_notification expect_flush = { 2, { 0, 0x1234 } }; + + reset_results( results ); + add_work_item(list, CrossProcessFlushCache, expect_cross.args[0], + expect_cross.args[1], 0, 0, 0, 0 ); + process_work_items(); + expect_notifications( results, 1, &expect_cross, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + expect_flush.args[0] = (ULONG_PTR)ptr; + NtFlushInstructionCache( GetCurrentProcess(), ptr, expect_flush.args[1] ); + expect_notifications( results, 1, &expect_flush, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuFlushInstructionCacheHeavy", "FlushInstructionCacheHeavy" ))) + { + struct expected_notification expect = { 2, { 0x1234567890, 0x6543210000 } }; + struct expected_notification expect2 = { 2 }; + + reset_results( results ); + add_work_item( list, CrossProcessFlushCacheHeavy, expect.args[0], expect.args[1], 0, 0, 0, 0 ); + process_work_items(); + expect_notifications( results, 1, &expect, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + request_cross_process_flush( &list->work_list ); + process_work_items(); + expect_notifications( results, 1, &expect2, FALSE ); + ok( !list->work_list.first, "list not empty\n" ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyMapViewOfSection", "NotifyMapViewOfSection" ))) + { + struct expected_notification expect = { 6 }; + LARGE_INTEGER offset; + + addr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr, 0, 0, &offset, &size, + ViewShare, 0, PAGE_READONLY ); + ok( NT_SUCCESS(status), "NtMapViewOfSection failed %lx\n", status ); + expect_notifications( results, 0, NULL, TRUE ); + NtUnmapViewOfSection( GetCurrentProcess(), addr ); + + /* only NtMapViewOfSection calls coming from the loader trigger a notification */ + NtCurrentTeb()->Tib.ArbitraryUserPointer = (WCHAR *)L"c:\\windows\\system32\\version.dll"; + addr = NULL; + size = 0; + results[1] = STATUS_SUCCESS; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr, 0, 0, &offset, &size, + ViewShare, 0, PAGE_READONLY ); + ok( NT_SUCCESS(status), "NtMapViewOfSection failed %lx\n", status ); + expect.args[0] = results[2]; /* FIXME: first parameter unknown */ + expect.args[1] = (ULONG_PTR)addr; + expect.args[3] = size; + expect.args[5] = PAGE_READONLY; + expect_notifications( results, 1, &expect, TRUE ); + NtUnmapViewOfSection( GetCurrentProcess(), addr ); + + results[1] = 0xdeadbeef; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr, 0, 0, &offset, &size, + ViewShare, 0, PAGE_READONLY ); +#ifdef _WIN64 + if (NtCurrentTeb()->ChpeV2CpuAreaInfo->InSyscallCallback) + { + ok( status == STATUS_SUCCESS, "NtMapViewOfSection failed %lx\n", status ); + expect_notifications( results, 0, NULL, TRUE ); + NtUnmapViewOfSection( GetCurrentProcess(), addr ); + } + else +#endif + { + ok( status == 0xdeadbeef, "NtMapViewOfSection failed %lx\n", status ); + expect.args[0] = results[2]; /* FIXME: first parameter unknown */ + expect.args[1] = (ULONG_PTR)addr; + expect.args[3] = size; + expect.args[5] = PAGE_READONLY; + expect_notifications( results, 1, &expect, TRUE ); + } + NtCurrentTeb()->Tib.ArbitraryUserPointer = NULL; + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyUnmapViewOfSection", "NotifyUnmapViewOfSection" ))) + { + struct expected_notification expect[2] = { { 3 }, { 3 } }; + LARGE_INTEGER offset; + + addr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection( mapping, GetCurrentProcess(), &addr, 0, 0, &offset, &size, + ViewShare, 0, PAGE_READONLY ); + ok( NT_SUCCESS(status), "NtMapViewOfSection failed %lx\n", status ); + NtUnmapViewOfSection( GetCurrentProcess(), (char *)addr + 0x123 ); + expect[0].args[0] = expect[1].args[0] = (ULONG_PTR)addr + 0x123; + expect[1].args[1] = 1; + expect_notifications( results, 2, expect, TRUE ); + + NtUnmapViewOfSection( GetCurrentProcess(), (char *)0x1234 ); + expect[0].args[0] = expect[1].args[0] = 0x1234; + expect[1].args[1] = 1; + expect[1].args[2] = (ULONG)STATUS_NOT_MAPPED_VIEW; + expect_notifications( results, 2, expect, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuNotifyReadFile", "BTCpu64NotifyReadFile" ))) + { + char buffer[0x123]; + IO_STATUS_BLOCK io; + struct expected_notification expect[2] = + { + { 5, { (ULONG_PTR)file, (ULONG_PTR)buffer, sizeof(buffer), 0, 0 } }, + { 5, { (ULONG_PTR)file, (ULONG_PTR)buffer, sizeof(buffer), 1, 0 } } + }; + + reset_results( results ); + status = NtReadFile( file, 0, NULL, NULL, &io, buffer, sizeof(buffer), NULL, NULL ); + ok( !status, "NtReadFile failed %lx\n", status ); + expect_notifications( results, 2, expect, TRUE ); + + status = NtReadFile( (HANDLE)0xdead, 0, NULL, NULL, &io, buffer, sizeof(buffer), NULL, NULL ); + ok( status == STATUS_INVALID_HANDLE, "NtReadFile failed %lx\n", status ); + expect[0].args[0] = expect[1].args[0] = 0xdead; + expect[1].args[4] = (ULONG)STATUS_INVALID_HANDLE; + expect_notifications( results, 2, expect, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuThreadTerm", "ThreadTerm" ))) + { + struct expected_notification expect = { 2, { 0xdead, 0xbeef } }; + + reset_results( results ); + status = NtTerminateThread( (HANDLE)0xdead, 0xbeef ); + ok( status == STATUS_INVALID_HANDLE, "NtTerminateThread failed %lx\n", status ); + expect_notifications( results, 1, &expect, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + if ((ptr = hook_notification_function( module, "BTCpuProcessTerm", "ProcessTerm" ))) + { + struct expected_notification expect[2] = + { + { 3, { 0, 0, 0 } }, + { 3, { 0, 1, 0 } } + }; + + reset_results( results ); + status = NtTerminateProcess( (HANDLE)0xdead, 0xbeef ); + ok( status == STATUS_INVALID_HANDLE, "NtTerminateProcess failed %lx\n", status ); + expect_notifications( results, 0, NULL, TRUE ); + + status = NtTerminateProcess( 0, 0xbeef ); + ok( !status, "NtTerminateProcess failed %lx\n", status ); + expect_notifications( results, 2, expect, TRUE ); + + WriteProcessMemory( GetCurrentProcess(), ptr, old_code, sizeof(old_code), NULL ); + } + + NtClose( mapping ); + NtClose( file ); + VirtualFree( code, 0, MEM_RELEASE ); +} + + +#ifdef _WIN64 + +static void test_cross_process_work_list(void) +{ + UINT i, next, count = 10, size = offsetof( CROSS_PROCESS_WORK_LIST, entries[count] ); + BOOLEAN res, flush; + CROSS_PROCESS_WORK_ENTRY *ptr, *ret; + CROSS_PROCESS_WORK_LIST *list = calloc( size, 1 ); + + if (!pRtlWow64PopAllCrossProcessWorkFromWorkList) + { + win_skip( "cross process list not supported\n" ); + return; + } + + list = calloc( size, 1 ); + for (i = 0; i < count; i++) + { + res = pRtlWow64PushCrossProcessWorkOntoFreeList( &list->free_list, &list->entries[i] ); + ok( res == TRUE, "%u: RtlWow64PushCrossProcessWorkOntoFreeList failed\n", i ); + } + + ok( list->free_list.counter == count, "wrong counter %u\n", list->free_list.counter ); + ok( CROSS_PROCESS_LIST_ENTRY( &list->free_list, list->free_list.first ) == &list->entries[count - 1], + "wrong offset %u\n", list->free_list.first ); + for (i = count; i > 1; i--) + ok( CROSS_PROCESS_LIST_ENTRY( &list->free_list, list->entries[i - 1].next ) == &list->entries[i - 2], + "%u: wrong offset %x / %x\n", i, list->entries[i - 1].next, + (UINT)((char *)&list->entries[i - 2] - (char *)&list->free_list) ); + ok( !list->entries[0].next, "wrong last offset %x\n", list->entries[0].next ); + + next = list->entries[count - 1].next; + ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list ); + ok( ptr == (void *)&list->entries[count - 1], "wrong ptr %p (%p)\n", ptr, list ); + ok( !ptr->next, "next not reset %x\n", ptr->next ); + ok( list->free_list.first == next, "wrong offset %x / %x\n", list->free_list.first, next ); + ok( list->free_list.counter == count + 1, "wrong counter %u\n", list->free_list.counter ); + + ptr->next = 0xdead; + ptr->id = 3; + ptr->addr = 0xdeadbeef; + ptr->size = 0x1000; + ptr->args[0] = 7; + ret = (void *)0xdeadbeef; + res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret ); + ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" ); + ok( !ret, "got ret ptr %p\n", ret ); + ok( list->work_list.counter == 1, "wrong counter %u\n", list->work_list.counter ); + ok( ptr == CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first), "wrong ptr %p / %p\n", + ptr, CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first )); + ok( !ptr->next, "got next %x\n", ptr->next ); + + next = list->work_list.first; + ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list ); + ok( list->free_list.counter == count + 2, "wrong counter %u\n", list->free_list.counter ); + ptr->id = 20; + ptr->addr = 0x123456; + ptr->size = 0x2345; + res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret ); + ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" ); + ok( !ret, "got ret ptr %p\n", ret ); + ok( list->work_list.counter == 2, "wrong counter %u\n", list->work_list.counter ); + ok( list->work_list.first == (char *)ptr - (char *)&list->work_list, "wrong ptr %p / %p\n", + ptr, (char *)list + list->work_list.first ); + ok( ptr->next == next, "got wrong next %x / %x\n", ptr->next, next ); + + flush = 0xcc; + ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush ); + ok( !flush, "RtlWow64PopAllCrossProcessWorkFromWorkList flush is TRUE\n" ); + ok( list->work_list.counter == 3, "wrong counter %u\n", list->work_list.counter ); + ok( !list->work_list.first, "list not empty %x\n", list->work_list.first ); + ok( ptr->addr == 0xdeadbeef, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) ); + ok( ptr->size == 0x1000, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) ); + ok( ptr->next, "next not set\n" ); + + ptr = CROSS_PROCESS_LIST_ENTRY( &list->work_list, ptr->next ); + ok( ptr->addr == 0x123456, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) ); + ok( ptr->size == 0x2345, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) ); + ok( !ptr->next, "list not terminated\n" ); + + res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret ); + ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" ); + ok( !ret, "got ret ptr %p\n", ret ); + ok( list->work_list.counter == 4, "wrong counter %u\n", list->work_list.counter ); + + res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list ); + ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" ); + ok( list->work_list.counter == 5, "wrong counter %u\n", list->work_list.counter ); + ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first ); + ok( ptr == CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first), "wrong ptr %p / %p\n", + ptr, CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first )); + + flush = 0xcc; + ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush ); + ok( flush == TRUE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush not set\n" ); + ok( list->work_list.counter == 6, "wrong counter %u\n", list->work_list.counter ); + ok( !list->work_list.first, "list not empty %x\n", list->work_list.first ); + ok( ptr->addr == 0x123456, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) ); + ok( ptr->size == 0x2345, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) ); + ok( !ptr->next, "next not set\n" ); + + flush = 0xcc; + ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush ); + ok( flush == FALSE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush set\n" ); + ok( list->work_list.counter == 6, "wrong counter %u\n", list->work_list.counter ); + ok( !list->work_list.first, "list not empty %x\n", list->work_list.first ); + ok( !ptr, "got ptr %p\n", ptr ); + + res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list ); + ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" ); + ok( list->work_list.counter == 7, "wrong counter %u\n", list->work_list.counter ); + ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first ); + + res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list ); + ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" ); + ok( list->work_list.counter == 8, "wrong counter %u\n", list->work_list.counter ); + ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first ); + + flush = 0xcc; + ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush ); + ok( flush == TRUE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush set\n" ); + ok( list->work_list.counter == 9, "wrong counter %u\n", list->work_list.counter ); + ok( !list->work_list.first, "list not empty %x\n", list->work_list.first ); + ok( !ptr, "got ptr %p\n", ptr ); + + for (i = 0; i < count; i++) + { + ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list ); + if (!ptr) break; + ok( list->free_list.counter == count + 3 + i, "wrong counter %u\n", list->free_list.counter ); + } + ok( list->free_list.counter == count + 2 + i, "wrong counter %u\n", list->free_list.counter ); + ok( !list->free_list.first, "first still set %x\n", list->free_list.first ); + + free( list ); +} + + +static void test_cpu_area(void) +{ + if (pRtlWow64GetCpuAreaInfo) + { + static const struct + { + USHORT machine; + NTSTATUS expect; + ULONG_PTR align, size, offset, flag; + } tests[] = + { + { IMAGE_FILE_MACHINE_I386, 0, 4, 0x2cc, 0x00, 0x00010000 }, + { IMAGE_FILE_MACHINE_AMD64, 0, 16, 0x4d0, 0x30, 0x00100000 }, + { IMAGE_FILE_MACHINE_ARMNT, 0, 8, 0x1a0, 0x00, 0x00200000 }, + { IMAGE_FILE_MACHINE_ARM64, 0, 16, 0x390, 0x00, 0x00400000 }, + { IMAGE_FILE_MACHINE_ARM, STATUS_INVALID_PARAMETER }, + { IMAGE_FILE_MACHINE_THUMB, STATUS_INVALID_PARAMETER }, + }; + USHORT buffer[2048]; + WOW64_CPURESERVED *cpu; + WOW64_CPU_AREA_INFO info; + ULONG i, j; + NTSTATUS status; +#define ALIGN(ptr,align) ((void *)(((ULONG_PTR)(ptr) + (align) - 1) & ~((align) - 1))) + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + for (j = 0; j < 8; j++) + { + cpu = (WOW64_CPURESERVED *)(buffer + j); + cpu->Flags = 0; + cpu->Machine = tests[i].machine; + status = pRtlWow64GetCpuAreaInfo( cpu, 0, &info ); + ok( status == tests[i].expect, "%lu:%lu: failed %lx\n", i, j, status ); + if (status) continue; + ok( info.Context == ALIGN( cpu + 1, tests[i].align ) || + broken( (ULONG_PTR)info.Context == (ULONG)(ULONG_PTR)ALIGN( cpu + 1, tests[i].align ) ), /* win10 <= 1709 */ + "%lu:%lu: wrong offset %Iu cpu %p context %p\n", + i, j, (ULONG_PTR)((char *)info.Context - (char *)cpu), cpu, info.Context ); + ok( info.ContextEx == ALIGN( (char *)info.Context + tests[i].size, sizeof(void*) ), + "%lu:%lu: wrong ex offset %lu\n", i, j, (ULONG)((char *)info.ContextEx - (char *)cpu) ); + ok( info.ContextFlagsLocation == (char *)info.Context + tests[i].offset, + "%lu:%lu: wrong flags offset %lu\n", + i, j, (ULONG)((char *)info.ContextFlagsLocation - (char *)info.Context) ); + ok( info.CpuReserved == cpu, "%lu:%lu: wrong cpu %p / %p\n", i, j, info.CpuReserved, cpu ); + ok( info.ContextFlag == tests[i].flag, "%lu:%lu: wrong flag %08lx\n", i, j, info.ContextFlag ); + ok( info.Machine == tests[i].machine, "%lu:%lu: wrong machine %x\n", i, j, info.Machine ); + } + } +#undef ALIGN + } + else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" ); +} + +static void test_exception_dispatcher(void) +{ +#ifdef __x86_64__ + BYTE *code = (BYTE *)pKiUserExceptionDispatcher; + void **hook; + + /* cld; mov xxx(%rip),%rax */ + ok( code[0] == 0xfc && code[1] == 0x48 && code[2] == 0x8b && code[3] == 0x05, + "wrong opcodes %02x %02x %02x %02x\n", code[0], code[1], code[2], code[3] ); + hook = (void **)(code + 8 + *(int *)(code + 4)); + ok( !*hook, "hook %p set to %p\n", hook, *hook ); +#endif +} + +#ifdef __arm64ec__ +static DWORD CALLBACK simulation_thread( void *arg ) +{ + BYTE code[] = + { + 0x48, 0xc7, 0xc1, 0x34, 0x12, 0x00, 0x00, /* mov $0x1234,%rcx */ + 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, /* movabs $RtlExitUserThread,%rax */ + 0xff, 0xd0, /* call *%rax */ + 0xc3, /* ret */ + }; + DWORD old_prot; + CONTEXT *context; + void (WINAPI *pBeginSimulation)(void) = arg; + void *addr = VirtualAlloc( NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE ); + + *(void **)(code + 9) = GetProcAddress( GetModuleHandleA("ntdll.dll"), "RtlExitUserThread" ); + memcpy( addr, code, sizeof(code) ); + VirtualProtect( addr, 0x1000, PAGE_EXECUTE_READ, &old_prot ); + + context = &NtCurrentTeb()->ChpeV2CpuAreaInfo->ContextAmd64->AMD64_Context; + context->Rsp = (ULONG_PTR)&context - 0x800; + context->Rip = (ULONG_PTR)addr; + + NtCurrentTeb()->ChpeV2CpuAreaInfo->InSimulation = 1; /* otherwise it crashes on recent Windows */ + pBeginSimulation(); + return 0x5678; +} +#endif + +static void test_xtajit64(void) +{ +#ifdef __arm64ec__ + HMODULE module = GetModuleHandleA( "xtajit64.dll" ); + BOOLEAN (WINAPI *pBTCpu64IsProcessorFeaturePresent)( UINT feature ); + void (WINAPI *pUpdateProcessorInformation)( SYSTEM_CPU_INFORMATION *info ); + void (WINAPI *pBeginSimulation)(void); + UINT i; + + if (!module) + { + win_skip( "xtaji64.dll not loaded\n" ); + return; + } +#define GET_PROC(func) p##func = pRtlFindExportedRoutineByName( module, #func ) + GET_PROC( BTCpu64IsProcessorFeaturePresent ); + GET_PROC( BeginSimulation ); + GET_PROC( UpdateProcessorInformation ); +#undef GET_PROC + + if (pBTCpu64IsProcessorFeaturePresent) + { + static const ULONGLONG expect_features = + (1ull << PF_COMPARE_EXCHANGE_DOUBLE) | + (1ull << PF_MMX_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_XMMI_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_RDTSC_INSTRUCTION_AVAILABLE) | + (1ull << PF_XMMI64_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_NX_ENABLED) | + (1ull << PF_SSE3_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_COMPARE_EXCHANGE128) | + (1ull << PF_FASTFAIL_AVAILABLE) | + (1ull << PF_RDTSCP_INSTRUCTION_AVAILABLE) | + (1ull << PF_SSSE3_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_SSE4_1_INSTRUCTIONS_AVAILABLE) | + (1ull << PF_SSE4_2_INSTRUCTIONS_AVAILABLE); + + for (i = 0; i < 64; i++) + { + BOOLEAN ret = pBTCpu64IsProcessorFeaturePresent( i ); + if (expect_features & (1ull << i)) ok( ret, "missing feature %u\n", i ); + else if (ret) trace( "extra feature %u supported\n", i ); + } + } + else win_skip( "BTCpu64IsProcessorFeaturePresent missing\n" ); + + if (pUpdateProcessorInformation) + { + SYSTEM_CPU_INFORMATION info; + + memset( &info, 0xcc, sizeof(info) ); + info.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_ARM64; + pUpdateProcessorInformation( &info ); + + ok( info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64, + "wrong architecture %u\n", info.ProcessorArchitecture ); + ok( info.ProcessorLevel == 21, "wrong level %u\n", info.ProcessorLevel ); + ok( info.ProcessorRevision == 1, "wrong revision %u\n", info.ProcessorRevision ); + ok( info.MaximumProcessors == 0xcccc, "wrong max proc %u\n", info.MaximumProcessors ); + ok( info.ProcessorFeatureBits == 0xcccccccc, "wrong features %lx\n", info.ProcessorFeatureBits ); + } + else win_skip( "UpdateProcessorInformation missing\n" ); + + if (pBeginSimulation) + { + DWORD ret, exit_code; + HANDLE thread = CreateThread( NULL, 0, simulation_thread, pBeginSimulation, 0, NULL ); + + ok( thread != 0, "thread creation failed\n" ); + ret = WaitForSingleObject( thread, 10000 ); + ok( !ret, "wait failed %lx\n", ret ); + GetExitCodeThread( thread, &exit_code ); + ok( exit_code == 0x1234, "wrong exit code %lx\n", exit_code ); + CloseHandle( thread ); + } + else win_skip( "BeginSimulation missing\n" ); +#endif +} + + +static void test_memory_notifications(void) +{ + HMODULE module; + CHPEV2_PROCESS_INFO *info; + + if (current_machine == IMAGE_FILE_MACHINE_ARM64) return; + if (!(module = GetModuleHandleA( "xtajit64.dll" ))) return; + info = NtCurrentTeb()->Peb->ChpeV2ProcessInfo; + if (info->NativeMachineType == native_machine && + info->EmulatedMachineType == IMAGE_FILE_MACHINE_AMD64) + { + test_notifications( module, (CROSS_PROCESS_WORK_LIST *)info->CrossProcessWorkList ); + + NtCurrentTeb()->ChpeV2CpuAreaInfo->InSyscallCallback++; + test_notifications( module, (CROSS_PROCESS_WORK_LIST *)info->CrossProcessWorkList ); + NtCurrentTeb()->ChpeV2CpuAreaInfo->InSyscallCallback--; + } + skip( "arm64ec shared info not found\n" ); +} + + +#else /* _WIN64 */ + +static const BYTE call_func64_code[] = +{ + 0x58, /* pop %eax */ + 0x0e, /* push %cs */ + 0x50, /* push %eax */ + 0x6a, 0x33, /* push $0x33 */ + 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 1f */ + 0x83, 0x04, 0x24, 0x05, /* 1: addl $0x5,(%esp) */ + 0xcb, /* lret */ + /* in 64-bit mode: */ + 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */ + 0x55, /* push %rbp */ + 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */ + 0x56, /* push %rsi */ + 0x57, /* push %rdi */ + 0x41, 0x8b, 0x4e, 0x10, /* mov 0x10(%r14),%ecx */ + 0x41, 0x8b, 0x76, 0x14, /* mov 0x14(%r14),%esi */ + 0x67, 0x8d, 0x04, 0xcd, 0, 0, 0, 0, /* lea 0x0(,%ecx,8),%eax */ + 0x83, 0xf8, 0x20, /* cmp $0x20,%eax */ + 0x7d, 0x05, /* jge 1f */ + 0xb8, 0x20, 0x00, 0x00, 0x00, /* mov $0x20,%eax */ + 0x48, 0x29, 0xc4, /* 1: sub %rax,%rsp */ + 0x48, 0x83, 0xe4, 0xf0, /* and $~15,%rsp */ + 0x48, 0x89, 0xe7, /* mov %rsp,%rdi */ + 0xf3, 0x48, 0xa5, /* rep movsq */ + 0x48, 0x8b, 0x0c, 0x24, /* mov (%rsp),%rcx */ + 0x48, 0x8b, 0x54, 0x24, 0x08, /* mov 0x8(%rsp),%rdx */ + 0x4c, 0x8b, 0x44, 0x24, 0x10, /* mov 0x10(%rsp),%r8 */ + 0x4c, 0x8b, 0x4c, 0x24, 0x18, /* mov 0x18(%rsp),%r9 */ + 0x41, 0xff, 0x56, 0x08, /* callq *0x8(%r14) */ + 0x48, 0x8d, 0x65, 0xf0, /* lea -0x10(%rbp),%rsp */ + 0x5f, /* pop %rdi */ + 0x5e, /* pop %rsi */ + 0x5d, /* pop %rbp */ + 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */ + 0xcb, /* lret */ +}; + +static NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args ) +{ + NTSTATUS (WINAPI *func)( ULONG64 func64, int nb_args, ULONG64 *args ) = code_mem; + + memcpy( code_mem, call_func64_code, sizeof(call_func64_code) ); + return func( func64, nb_args, args ); +} + +static ULONG64 main_module, ntdll_module, wow64_module, wow64base_module, wow64con_module, + wow64cpu_module, xtajit_module, wow64win_module; + +static void enum_modules64( void (*func)(ULONG64,const WCHAR *) ) +{ + typedef struct + { + LIST_ENTRY64 InLoadOrderLinks; + LIST_ENTRY64 InMemoryOrderLinks; + LIST_ENTRY64 InInitializationOrderLinks; + ULONG64 DllBase; + ULONG64 EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING64 FullDllName; + UNICODE_STRING64 BaseDllName; + /* etc. */ + } LDR_DATA_TABLE_ENTRY64; + + TEB64 *teb64 = (TEB64 *)NtCurrentTeb()->GdiBatchCount; + PEB64 peb64; + ULONG64 ptr; + PEB_LDR_DATA64 ldr; + LDR_DATA_TABLE_ENTRY64 entry; + NTSTATUS status; + HANDLE process; + + process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + ok( process != 0, "failed to open current process %lu\n", GetLastError() ); + status = pNtWow64ReadVirtualMemory64( process, teb64->Peb, &peb64, sizeof(peb64), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + todo_wine_if( old_wow64 ) + ok( peb64.LdrData, "LdrData not initialized\n" ); + if (!peb64.LdrData) goto done; + status = pNtWow64ReadVirtualMemory64( process, peb64.LdrData, &ldr, sizeof(ldr), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + ptr = ldr.InLoadOrderModuleList.Flink; + for (;;) + { + WCHAR buffer[256]; + status = pNtWow64ReadVirtualMemory64( process, ptr, &entry, sizeof(entry), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64ReadVirtualMemory64( process, entry.BaseDllName.Buffer, buffer, sizeof(buffer), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + if (status) break; + func( entry.DllBase, buffer ); + ptr = entry.InLoadOrderLinks.Flink; + if (ptr == peb64.LdrData + offsetof( PEB_LDR_DATA64, InLoadOrderModuleList )) break; + } +done: + NtClose( process ); +} + +static ULONG64 get_proc_address64( ULONG64 module, const char *name ) +{ + IMAGE_DOS_HEADER dos; + IMAGE_NT_HEADERS64 nt; + IMAGE_EXPORT_DIRECTORY exports; + ULONG i, *names, *funcs; + USHORT *ordinals; + NTSTATUS status; + HANDLE process; + ULONG64 ret = 0; + char buffer[64]; + + if (!module) return 0; + process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + ok( process != 0, "failed to open current process %lu\n", GetLastError() ); + status = pNtWow64ReadVirtualMemory64( process, module, &dos, sizeof(dos), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64ReadVirtualMemory64( process, module + dos.e_lfanew, &nt, sizeof(nt), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64ReadVirtualMemory64( process, module + nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, + &exports, sizeof(exports), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + names = calloc( exports.NumberOfNames, sizeof(*names) ); + ordinals = calloc( exports.NumberOfNames, sizeof(*ordinals) ); + funcs = calloc( exports.NumberOfFunctions, sizeof(*funcs) ); + status = pNtWow64ReadVirtualMemory64( process, module + exports.AddressOfNames, + names, exports.NumberOfNames * sizeof(*names), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64ReadVirtualMemory64( process, module + exports.AddressOfNameOrdinals, + ordinals, exports.NumberOfNames * sizeof(*ordinals), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64ReadVirtualMemory64( process, module + exports.AddressOfFunctions, + funcs, exports.NumberOfFunctions * sizeof(*funcs), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + for (i = 0; i < exports.NumberOfNames && !ret; i++) + { + status = pNtWow64ReadVirtualMemory64( process, module + names[i], buffer, sizeof(buffer), NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + if (!strcmp( buffer, name )) ret = module + funcs[ordinals[i]]; + } + free( funcs ); + free( ordinals ); + free( names ); + NtClose( process ); + return ret; +} + +static void check_module( ULONG64 base, const WCHAR *name ) +{ + if (base == (ULONG_PTR)GetModuleHandleW(0)) + { + WCHAR *p, module[MAX_PATH]; + + GetModuleFileNameW( 0, module, MAX_PATH ); + if ((p = wcsrchr( module, '\\' ))) p++; + else p = module; + ok( !wcsicmp( name, p ), "wrong name %s / %s\n", debugstr_w(name), debugstr_w(module)); + main_module = base; + return; + } +#define CHECK_MODULE(mod) do { if (!wcsicmp( name, L"" #mod ".dll" )) { mod ## _module = base; return; } } while(0) + CHECK_MODULE(ntdll); + CHECK_MODULE(wow64); + CHECK_MODULE(wow64base); + CHECK_MODULE(wow64con); + CHECK_MODULE(wow64win); + if (native_machine == IMAGE_FILE_MACHINE_ARM64) + CHECK_MODULE(xtajit); + else + CHECK_MODULE(wow64cpu); +#undef CHECK_MODULE + todo_wine_if( !wcscmp( name, L"win32u.dll" )) + ok( 0, "unknown module %s %s found\n", wine_dbgstr_longlong(base), wine_dbgstr_w(name)); +} + +static void test_modules(void) +{ + if (!is_wow64) return; + if (!pNtWow64ReadVirtualMemory64) return; + enum_modules64( check_module ); + todo_wine_if( old_wow64 ) + { + ok( main_module, "main module not found\n" ); + ok( ntdll_module, "64-bit ntdll not found\n" ); + ok( wow64_module, "wow64.dll not found\n" ); + if (native_machine == IMAGE_FILE_MACHINE_ARM64) + ok( xtajit_module, "xtajit.dll not found\n" ); + else + ok( wow64cpu_module, "wow64cpu.dll not found\n" ); + ok( wow64win_module, "wow64win.dll not found\n" ); + } +} + +static void test_nt_wow64(void) +{ + const char str[] = "hello wow64"; + char buffer[100]; + NTSTATUS status; + ULONG64 res; + HANDLE process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + + ok( process != 0, "failed to open current process %lu\n", GetLastError() ); + if (pNtWow64ReadVirtualMemory64) + { + status = pNtWow64ReadVirtualMemory64( process, (ULONG_PTR)str, buffer, sizeof(str), &res ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + ok( res == sizeof(str), "wrong size %s\n", wine_dbgstr_longlong(res) ); + ok( !strcmp( buffer, str ), "wrong data %s\n", debugstr_a(buffer) ); + status = pNtWow64WriteVirtualMemory64( process, (ULONG_PTR)buffer, " bye ", 5, &res ); + ok( !status, "NtWow64WriteVirtualMemory64 failed %lx\n", status ); + ok( res == 5, "wrong size %s\n", wine_dbgstr_longlong(res) ); + ok( !strcmp( buffer, " bye wow64" ), "wrong data %s\n", debugstr_a(buffer) ); + /* current process pseudo-handle is broken on some Windows versions */ + status = pNtWow64ReadVirtualMemory64( GetCurrentProcess(), (ULONG_PTR)str, buffer, sizeof(str), &res ); + ok( !status || broken( status == STATUS_INVALID_HANDLE ), + "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = pNtWow64WriteVirtualMemory64( GetCurrentProcess(), (ULONG_PTR)buffer, " bye ", 5, &res ); + ok( !status || broken( status == STATUS_INVALID_HANDLE ), + "NtWow64WriteVirtualMemory64 failed %lx\n", status ); + } + else win_skip( "NtWow64ReadVirtualMemory64 not supported\n" ); + + if (pNtWow64AllocateVirtualMemory64) + { + ULONG64 ptr = 0; + ULONG64 size = 0x2345; + + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + ok( !status, "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + ok( ptr, "ptr not set\n" ); + ok( size == 0x3000, "size not set %s\n", wine_dbgstr_longlong(size) ); + ptr += 0x1000; + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( status == STATUS_CONFLICTING_ADDRESSES, "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + ptr = 0; + size = 0; + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER_4, + "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + size = 0x1000; + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 22, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER_3, + "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 33, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + ok( status == STATUS_INVALID_PARAMETER || status == STATUS_INVALID_PARAMETER_3, + "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0x3fffffff, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + todo_wine_if( !is_wow64 ) + ok( !status, "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + ok( ptr < 0x40000000, "got wrong ptr %s\n", wine_dbgstr_longlong(ptr) ); + if (!status && pNtWow64WriteVirtualMemory64) + { + status = pNtWow64WriteVirtualMemory64( process, ptr, str, sizeof(str), &res ); + ok( !status, "NtWow64WriteVirtualMemory64 failed %lx\n", status ); + ok( res == sizeof(str), "wrong size %s\n", wine_dbgstr_longlong(res) ); + ok( !strcmp( (char *)(ULONG_PTR)ptr, str ), "wrong data %s\n", + debugstr_a((char *)(ULONG_PTR)ptr) ); + ptr = 0; + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( !status, "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + status = pNtWow64WriteVirtualMemory64( process, ptr, str, sizeof(str), &res ); + todo_wine + ok( status == STATUS_PARTIAL_COPY || broken( status == STATUS_ACCESS_VIOLATION ), + "NtWow64WriteVirtualMemory64 failed %lx\n", status ); + todo_wine + ok( !res || broken(res) /* win10 1709 */, "wrong size %s\n", wine_dbgstr_longlong(res) ); + } + ptr = 0x9876543210ull; + status = pNtWow64AllocateVirtualMemory64( process, &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + todo_wine_if( !is_wow64 || old_wow64 ) + ok( !status || broken( status == STATUS_CONFLICTING_ADDRESSES ), + "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + if (!status) ok( ptr == 0x9876540000ull || broken(ptr == 0x76540000), /* win 8.1 */ + "wrong ptr %s\n", wine_dbgstr_longlong(ptr) ); + ptr = 0; + status = pNtWow64AllocateVirtualMemory64( GetCurrentProcess(), &ptr, 0, &size, + MEM_RESERVE | MEM_COMMIT, PAGE_READONLY ); + ok( !status || broken( status == STATUS_INVALID_HANDLE ), + "NtWow64AllocateVirtualMemory64 failed %lx\n", status ); + } + else win_skip( "NtWow64AllocateVirtualMemory64 not supported\n" ); + + if (pNtWow64GetNativeSystemInformation) + { + ULONG i, len; + SYSTEM_BASIC_INFORMATION sbi, sbi2, sbi3; + + memset( &sbi, 0xcc, sizeof(sbi) ); + status = pNtQuerySystemInformation( SystemBasicInformation, &sbi, sizeof(sbi), &len ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( len == sizeof(sbi), "wrong length %ld\n", len ); + + memset( &sbi2, 0xcc, sizeof(sbi2) ); + status = pRtlGetNativeSystemInformation( SystemBasicInformation, &sbi2, sizeof(sbi2), &len ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( len == sizeof(sbi2), "wrong length %ld\n", len ); + + ok( sbi.HighestUserAddress == (void *)0x7ffeffff, "wrong limit %p\n", sbi.HighestUserAddress); + todo_wine_if( old_wow64 ) + ok( sbi2.HighestUserAddress == (is_wow64 ? (void *)0xfffeffff : (void *)0x7ffeffff), + "wrong limit %p\n", sbi.HighestUserAddress); + + memset( &sbi3, 0xcc, sizeof(sbi3) ); + status = pNtWow64GetNativeSystemInformation( SystemBasicInformation, &sbi3, sizeof(sbi3), &len ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( len == sizeof(sbi3), "wrong length %ld\n", len ); + ok( !memcmp( &sbi2, &sbi3, offsetof(SYSTEM_BASIC_INFORMATION,NumberOfProcessors)+1 ), + "info is different\n" ); + + memset( &sbi3, 0xcc, sizeof(sbi3) ); + status = pNtWow64GetNativeSystemInformation( SystemEmulationBasicInformation, &sbi3, sizeof(sbi3), &len ); + ok( status == STATUS_SUCCESS, "failed %lx\n", status ); + ok( len == sizeof(sbi3), "wrong length %ld\n", len ); + ok( !memcmp( &sbi, &sbi3, offsetof(SYSTEM_BASIC_INFORMATION,NumberOfProcessors)+1 ), + "info is different\n" ); + + for (i = 0; i < 256; i++) + { + NTSTATUS expect = pNtQuerySystemInformation( i, NULL, 0, &len ); + status = pNtWow64GetNativeSystemInformation( i, NULL, 0, &len ); + switch (i) + { + case SystemNativeBasicInformation: + ok( status == STATUS_INVALID_INFO_CLASS || status == STATUS_INFO_LENGTH_MISMATCH || + broken(status == STATUS_NOT_IMPLEMENTED) /* vista */, "%lu: %lx / %lx\n", i, status, expect ); + break; + case SystemBasicInformation: + case SystemCpuInformation: + case SystemEmulationBasicInformation: + case SystemEmulationProcessorInformation: + ok( status == expect, "%lu: %lx / %lx\n", i, status, expect ); + break; + default: + if (is_wow64) /* only a few info classes are supported on Wow64 */ + ok( status == STATUS_INVALID_INFO_CLASS || + broken(status == STATUS_NOT_IMPLEMENTED), /* vista */ + "%lu: %lx\n", i, status ); + else + ok( status == expect, "%lu: %lx / %lx\n", i, status, expect ); + break; + } + } + } + else win_skip( "NtWow64GetNativeSystemInformation not supported\n" ); + + if (pNtWow64IsProcessorFeaturePresent) + { + ULONG i; + + for (i = 0; i < 64; i++) + ok( pNtWow64IsProcessorFeaturePresent( i ) == IsProcessorFeaturePresent( i ), + "mismatch %lu wow64 returned %lx\n", i, pNtWow64IsProcessorFeaturePresent( i )); + + if (native_machine == IMAGE_FILE_MACHINE_ARM64) + { + KSHARED_USER_DATA *user_shared_data = ULongToPtr( 0x7ffe0000 ); + + ok( user_shared_data->ProcessorFeatures[PF_ARM_V8_INSTRUCTIONS_AVAILABLE], "no ARM_V8\n" ); + ok( user_shared_data->ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE], "no MMX\n" ); + ok( !pNtWow64IsProcessorFeaturePresent( PF_ARM_V8_INSTRUCTIONS_AVAILABLE ), "ARM_V8 present\n" ); + ok( pNtWow64IsProcessorFeaturePresent( PF_MMX_INSTRUCTIONS_AVAILABLE ), "MMX not present\n" ); + } + } + else win_skip( "NtWow64IsProcessorFeaturePresent not supported\n" ); + + if (pNtWow64QueryInformationProcess64) + { + PROCESS_BASIC_INFORMATION pbi32; + PROCESS_BASIC_INFORMATION64 pbi64; + ULONG expected_peb; + ULONG class; + + for (class = 0; class <= MaxProcessInfoClass; class++) + { + winetest_push_context( "Process information class %lu", class ); + + switch (class) + { + case ProcessBasicInformation: + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi32, sizeof(pbi32), NULL ); + ok( !status, "NtQueryInformationProcess returned 0x%08lx\n", status ); + + status = pNtWow64QueryInformationProcess64( GetCurrentProcess(), ProcessBasicInformation, &pbi64, sizeof(pbi64), NULL ); + ok( !status, "NtWow64QueryInformationProcess64 returned 0x%08lx\n", status ); + + expected_peb = (ULONG)pbi32.PebBaseAddress; + if (is_wow64) expected_peb -= 0x1000; + + ok( pbi64.ExitStatus == pbi32.ExitStatus, + "expected %lu got %lu\n", pbi32.ExitStatus, pbi64.ExitStatus ); + ok( pbi64.PebBaseAddress == expected_peb || + /* The 64-bit PEB is usually, but not always, 4096 bytes below the 32-bit PEB */ + broken( is_wow64 && llabs( (INT64)pbi64.PebBaseAddress - (INT64)expected_peb ) < 0x10000 ), + "expected 0x%lx got 0x%I64x\n", expected_peb, pbi64.PebBaseAddress ); + ok( pbi64.AffinityMask == pbi32.AffinityMask, + "expected 0x%Ix got 0x%I64x\n", pbi32.AffinityMask, pbi64.AffinityMask ); + ok( pbi64.UniqueProcessId == pbi32.UniqueProcessId, + "expected %Ix got %I64x\n", pbi32.UniqueProcessId, pbi64.UniqueProcessId ); + ok( pbi64.InheritedFromUniqueProcessId == pbi32.InheritedFromUniqueProcessId, + "expected %Ix got %I64x\n", pbi32.UniqueProcessId, pbi64.UniqueProcessId ); + break; + default: + status = pNtWow64QueryInformationProcess64( GetCurrentProcess(), class, NULL, 0, NULL ); + ok( status == STATUS_NOT_IMPLEMENTED, "NtWow64QueryInformationProcess64 returned 0x%08lx\n", status ); + } + + winetest_pop_context(); + } + } + else win_skip( "NtWow64QueryInformationProcess64 not supported\n" ); + + NtClose( process ); +} + +static void test_init_block(void) +{ + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + ULONG i, size = 0, *init_block; + ULONG64 ptr64, *block64; + void *ptr; + + if (!is_wow64) return; + if ((ptr = GetProcAddress( ntdll, "LdrSystemDllInitBlock" ))) + { + init_block = ptr; + trace( "got init block %08lx\n", init_block[0] ); +#define CHECK_FUNC(val,func) \ + ok( (val) == (ULONG_PTR)GetProcAddress( ntdll, func ), \ + "got %p for %s %p\n", (void *)(ULONG_PTR)(val), func, GetProcAddress( ntdll, func )) + switch (init_block[0]) + { + case 0x44: /* vistau64 */ + CHECK_FUNC( init_block[1], "LdrInitializeThunk" ); + CHECK_FUNC( init_block[2], "KiUserExceptionDispatcher" ); + CHECK_FUNC( init_block[3], "KiUserApcDispatcher" ); + CHECK_FUNC( init_block[4], "KiUserCallbackDispatcher" ); + CHECK_FUNC( init_block[5], "LdrHotPatchRoutine" ); + CHECK_FUNC( init_block[6], "ExpInterlockedPopEntrySListFault" ); + CHECK_FUNC( init_block[7], "ExpInterlockedPopEntrySListResume" ); + CHECK_FUNC( init_block[8], "ExpInterlockedPopEntrySListEnd" ); + CHECK_FUNC( init_block[9], "RtlUserThreadStart" ); + CHECK_FUNC( init_block[10], "RtlpQueryProcessDebugInformationRemote" ); + CHECK_FUNC( init_block[11], "EtwpNotificationThread" ); + ok( init_block[12] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)init_block[12], ntdll ); + size = 13 * sizeof(*init_block); + break; + case 0x50: /* win7 */ + CHECK_FUNC( init_block[4], "LdrInitializeThunk" ); + CHECK_FUNC( init_block[5], "KiUserExceptionDispatcher" ); + CHECK_FUNC( init_block[6], "KiUserApcDispatcher" ); + CHECK_FUNC( init_block[7], "KiUserCallbackDispatcher" ); + CHECK_FUNC( init_block[8], "LdrHotPatchRoutine" ); + CHECK_FUNC( init_block[9], "ExpInterlockedPopEntrySListFault" ); + CHECK_FUNC( init_block[10], "ExpInterlockedPopEntrySListResume" ); + CHECK_FUNC( init_block[11], "ExpInterlockedPopEntrySListEnd" ); + CHECK_FUNC( init_block[12], "RtlUserThreadStart" ); + CHECK_FUNC( init_block[13], "RtlpQueryProcessDebugInformationRemote" ); + CHECK_FUNC( init_block[14], "EtwpNotificationThread" ); + ok( init_block[15] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)init_block[15], ntdll ); + /* CHECK_FUNC( init_block[16], "LdrSystemDllInitBlock" ); not always present */ + size = 17 * sizeof(*init_block); + break; + case 0x70: /* win8 */ + CHECK_FUNC( init_block[4], "LdrInitializeThunk" ); + CHECK_FUNC( init_block[5], "KiUserExceptionDispatcher" ); + CHECK_FUNC( init_block[6], "KiUserApcDispatcher" ); + CHECK_FUNC( init_block[7], "KiUserCallbackDispatcher" ); + CHECK_FUNC( init_block[8], "ExpInterlockedPopEntrySListFault" ); + CHECK_FUNC( init_block[9], "ExpInterlockedPopEntrySListResume" ); + CHECK_FUNC( init_block[10], "ExpInterlockedPopEntrySListEnd" ); + CHECK_FUNC( init_block[11], "RtlUserThreadStart" ); + CHECK_FUNC( init_block[12], "RtlpQueryProcessDebugInformationRemote" ); + ok( init_block[13] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)init_block[13], ntdll ); + CHECK_FUNC( init_block[14], "LdrSystemDllInitBlock" ); + size = 15 * sizeof(*init_block); + break; + case 0x80: /* win10 1507 */ + CHECK_FUNC( init_block[4], "LdrInitializeThunk" ); + CHECK_FUNC( init_block[5], "KiUserExceptionDispatcher" ); + CHECK_FUNC( init_block[6], "KiUserApcDispatcher" ); + CHECK_FUNC( init_block[7], "KiUserCallbackDispatcher" ); + if (GetProcAddress( ntdll, "ExpInterlockedPopEntrySListFault" )) + { + CHECK_FUNC( init_block[8], "ExpInterlockedPopEntrySListFault" ); + CHECK_FUNC( init_block[9], "ExpInterlockedPopEntrySListResume" ); + CHECK_FUNC( init_block[10], "ExpInterlockedPopEntrySListEnd" ); + CHECK_FUNC( init_block[11], "RtlUserThreadStart" ); + CHECK_FUNC( init_block[12], "RtlpQueryProcessDebugInformationRemote" ); + ok( init_block[13] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)init_block[13], ntdll ); + CHECK_FUNC( init_block[14], "LdrSystemDllInitBlock" ); + size = 15 * sizeof(*init_block); + } + else /* win10 1607 */ + { + CHECK_FUNC( init_block[8], "RtlUserThreadStart" ); + CHECK_FUNC( init_block[9], "RtlpQueryProcessDebugInformationRemote" ); + ok( init_block[10] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)init_block[10], ntdll ); + CHECK_FUNC( init_block[11], "LdrSystemDllInitBlock" ); + size = 12 * sizeof(*init_block); + } + break; + case 0xe0: /* win10 1809 */ + case 0xf0: /* win10 2004 */ + case 0x128: /* win11 24h2 */ + block64 = ptr; + CHECK_FUNC( block64[3], "LdrInitializeThunk" ); + CHECK_FUNC( block64[4], "KiUserExceptionDispatcher" ); + CHECK_FUNC( block64[5], "KiUserApcDispatcher" ); + CHECK_FUNC( block64[6], "KiUserCallbackDispatcher" ); + CHECK_FUNC( block64[7], "RtlUserThreadStart" ); + CHECK_FUNC( block64[8], "RtlpQueryProcessDebugInformationRemote" ); + todo_wine_if( old_wow64 ) + ok( block64[9] == (ULONG_PTR)ntdll, "got %p for ntdll %p\n", + (void *)(ULONG_PTR)block64[9], ntdll ); + CHECK_FUNC( block64[10], "LdrSystemDllInitBlock" ); + CHECK_FUNC( block64[11], "RtlpFreezeTimeBias" ); + size = 12 * sizeof(*block64); + break; + default: + ok( 0, "unknown init block %08lx\n", init_block[0] ); + for (i = 0; i < init_block[0] / sizeof(ULONG); i++) trace("%04lx: %08lx\n", i, init_block[i]); + break; + } +#undef CHECK_FUNC + + if (size && (ptr64 = get_proc_address64( ntdll_module, "LdrSystemDllInitBlock" ))) + { + DWORD buffer[64]; + HANDLE process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + NTSTATUS status = pNtWow64ReadVirtualMemory64( process, ptr64, buffer, size, NULL ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + ok( !memcmp( buffer, init_block, size ), "wrong 64-bit init block\n" ); + NtClose( process ); + } + } + else todo_wine win_skip( "LdrSystemDllInitBlock not supported\n" ); +} + + +static void test_memory_notifications(void) +{ + HMODULE module = (HMODULE)(ULONG_PTR)xtajit_module; + WOW64INFO *info; + DWORD i; + + if (!xtajit_module) + { + skip( "xtajit.dll not loaded\n" ); + return; + } + if ((ULONG_PTR)module != xtajit_module) + { + skip( "xtajit.dll loaded above 4G\n" ); + return; + } + + for (i = 0x400; i < 0x800; i += sizeof(ULONG)) + { + info = (WOW64INFO *)((char *)NtCurrentTeb()->Peb + i); + if (info->NativeMachineType == native_machine && + info->EmulatedMachineType == IMAGE_FILE_MACHINE_I386) + { + if (info->CrossProcessWorkList >> 32) + skip( "cross-process work list above 4G (%I64x)\n", info->CrossProcessWorkList ); + else + test_notifications( module, ULongToPtr( info->CrossProcessWorkList )); + return; + } + } + skip( "WOW64INFO not found\n" ); +} + + +static DWORD WINAPI iosb_delayed_write_thread(void *arg) +{ + HANDLE client = arg; + DWORD size; + BOOL ret; + + Sleep(100); + + ret = WriteFile( client, "data", sizeof("data"), &size, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + + return 0; +} + + +static void test_iosb(void) +{ + static const char pipe_name[] = "\\\\.\\pipe\\wow64iosbnamedpipe"; + HANDLE client, server, thread; + NTSTATUS status; + ULONG64 read_func, flush_func; + IO_STATUS_BLOCK iosb32; + char buffer[6]; + DWORD size; + BOOL ret; + struct + { + union + { + NTSTATUS Status; + ULONG64 Pointer; + }; + ULONG64 Information; + } iosb64; + ULONG64 args[] = { 0, 0, 0, 0, (ULONG_PTR)&iosb64, (ULONG_PTR)buffer, sizeof(buffer), 0, 0 }; + ULONG64 flush_args[] = { 0, (ULONG_PTR)&iosb64 }; + + if (!is_wow64) return; + if (!code_mem) return; + if (!ntdll_module) return; + read_func = get_proc_address64( ntdll_module, "NtReadFile" ); + flush_func = get_proc_address64( ntdll_module, "NtFlushBuffersFile" ); + + /* async calls set iosb32 but not iosb64 */ + + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); + + memset( buffer, 0xcc, sizeof(buffer) ); + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + args[0] = (LONG_PTR)server; + status = call_func64( read_func, ARRAY_SIZE(args), args ); + ok( status == STATUS_PENDING, "NtReadFile returned %lx\n", status ); + ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); + ok( iosb64.Pointer == PtrToUlong(&iosb32), "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == 0xdeadbeef, "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + + ret = WriteFile( client, "data", sizeof("data"), &size, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + + ok( iosb32.Status == 0, "Wrong iostatus %lx\n", iosb32.Status ); + ok( iosb32.Information == sizeof("data"), "Wrong information %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == PtrToUlong(&iosb32), "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == 0xdeadbeef, "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + ok( !memcmp( buffer, "data", iosb32.Information ), + "got wrong data %s\n", debugstr_an(buffer, iosb32.Information) ); + + memset( buffer, 0xcc, sizeof(buffer) ); + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + ret = WriteFile( client, "data", sizeof("data"), &size, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + + status = call_func64( read_func, ARRAY_SIZE(args), args ); + ok( status == STATUS_SUCCESS, "NtReadFile returned %lx\n", status ); + ok( iosb32.Status == STATUS_SUCCESS, "status changed to %lx\n", iosb32.Status ); + ok( iosb32.Information == sizeof("data"), "info changed to %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == PtrToUlong(&iosb32), "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == 0xdeadbeef, "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + ok( !memcmp( buffer, "data", iosb32.Information ), + "got wrong data %s\n", debugstr_an(buffer, iosb32.Information) ); + + /* syscalls which are always synchronous set iosb64 but not iosb32 */ + + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + flush_args[0] = (LONG_PTR)server; + status = call_func64( flush_func, ARRAY_SIZE(flush_args), flush_args ); + ok( status == STATUS_SUCCESS, "NtFlushBuffersFile returned %lx\n", status ); + ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); + ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == STATUS_SUCCESS, "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == 0, "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + + CloseHandle( client ); + CloseHandle( server ); + + /* synchronous calls set iosb64 but not iosb32 */ + + server = CreateNamedPipeA( pipe_name, PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 4, 1024, 1024, 1000, NULL ); + ok( server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError() ); + + client = CreateFileA( pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL ); + ok( client != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError() ); + + ret = WriteFile( client, "data", sizeof("data"), &size, NULL ); + ok( ret == TRUE, "got error %lu\n", GetLastError() ); + + memset( buffer, 0xcc, sizeof(buffer) ); + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + args[0] = (LONG_PTR)server; + status = call_func64( read_func, ARRAY_SIZE(args), args ); + ok( status == STATUS_SUCCESS, "NtReadFile returned %lx\n", status ); + ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); + ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == STATUS_SUCCESS, "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == sizeof("data"), "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + ok( !memcmp( buffer, "data", iosb64.Information ), + "got wrong data %s\n", debugstr_an(buffer, iosb64.Information) ); + + thread = CreateThread( NULL, 0, iosb_delayed_write_thread, client, 0, NULL ); + + memset( buffer, 0xcc, sizeof(buffer) ); + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + args[0] = (LONG_PTR)server; + status = call_func64( read_func, ARRAY_SIZE(args), args ); + ok( status == STATUS_SUCCESS, "NtReadFile returned %lx\n", status ); + todo_wine + { + ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); + ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == STATUS_SUCCESS, "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == sizeof("data"), "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + ok( !memcmp( buffer, "data", iosb64.Information ), + "got wrong data %s\n", debugstr_an(buffer, iosb64.Information) ); + } + + ret = WaitForSingleObject( thread, 1000 ); + ok(!ret, "got %d\n", ret ); + CloseHandle( thread ); + + memset( &iosb32, 0x55, sizeof(iosb32) ); + iosb64.Pointer = PtrToUlong( &iosb32 ); + iosb64.Information = 0xdeadbeef; + + flush_args[0] = (LONG_PTR)server; + status = call_func64( flush_func, ARRAY_SIZE(flush_args), flush_args ); + ok( status == STATUS_SUCCESS, "NtFlushBuffersFile returned %lx\n", status ); + ok( iosb32.Status == 0x55555555, "status changed to %lx\n", iosb32.Status ); + ok( iosb32.Information == 0x55555555, "info changed to %Ix\n", iosb32.Information ); + ok( iosb64.Pointer == STATUS_SUCCESS, "pointer changed to %I64x\n", iosb64.Pointer ); + ok( iosb64.Information == 0, "info changed to %Ix\n", (ULONG_PTR)iosb64.Information ); + + CloseHandle( client ); + CloseHandle( server ); +} + +static NTSTATUS invoke_syscall( const char *name, ULONG args32[] ) +{ + ULONG64 args64[] = { -1, PtrToUlong( args32 ) }; + ULONG64 func = get_proc_address64( wow64_module, "Wow64SystemServiceEx" ); + BYTE *syscall = (BYTE *)GetProcAddress( GetModuleHandleA("ntdll.dll"), name ); + + ok( syscall != NULL, "syscall %s not found\n", name ); + if (syscall[0] == 0xb8) + args64[0] = *(DWORD *)(syscall + 1); + else + win_skip( "syscall thunk %s not recognized\n", name ); + + return call_func64( func, ARRAY_SIZE(args64), args64 ); +} + +static void test_syscalls(void) +{ + ULONG64 func; + ULONG args32[8]; + HANDLE event, event2; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING name; + NTSTATUS status; + + if (!is_wow64) return; + if (!code_mem) return; + if (!ntdll_module) return; + + func = get_proc_address64( wow64_module, "Wow64SystemServiceEx" ); + ok( func, "Wow64SystemServiceEx not found\n" ); + + event = CreateEventA( NULL, FALSE, FALSE, NULL ); + + status = NtSetEvent( event, NULL ); + ok( !status, "NtSetEvent failed %lx\n", status ); + args32[0] = HandleToLong( event ); + status = invoke_syscall( "NtClose", args32 ); + ok( !status, "syscall failed %lx\n", status ); + status = NtSetEvent( event, NULL ); + ok( status == STATUS_INVALID_HANDLE, "NtSetEvent failed %lx\n", status ); + status = invoke_syscall( "NtClose", args32 ); + ok( status == STATUS_INVALID_HANDLE, "syscall failed %lx\n", status ); + args32[0] = 0xdeadbeef; + status = invoke_syscall( "NtClose", args32 ); + ok( status == STATUS_INVALID_HANDLE, "syscall failed %lx\n", status ); + + RtlInitUnicodeString( &name, L"\\BaseNamedObjects\\wow64-test"); + InitializeObjectAttributes( &attr, &name, OBJ_OPENIF, 0, NULL ); + event = (HANDLE)0xdeadbeef; + args32[0] = PtrToUlong(&event ); + args32[1] = EVENT_ALL_ACCESS; + args32[2] = PtrToUlong( &attr ); + args32[3] = NotificationEvent; + args32[4] = 0; + status = invoke_syscall( "NtCreateEvent", args32 ); + ok( !status, "syscall failed %lx\n", status ); + status = NtSetEvent( event, NULL ); + ok( !status, "NtSetEvent failed %lx\n", status ); + + event2 = (HANDLE)0xdeadbeef; + args32[0] = PtrToUlong( &event2 ); + status = invoke_syscall( "NtOpenEvent", args32 ); + ok( !status, "syscall failed %lx\n", status ); + status = NtSetEvent( event2, NULL ); + ok( !status, "NtSetEvent failed %lx\n", status ); + args32[0] = HandleToLong( event2 ); + status = invoke_syscall( "NtClose", args32 ); + ok( !status, "syscall failed %lx\n", status ); + + event2 = (HANDLE)0xdeadbeef; + args32[0] = PtrToUlong( &event2 ); + status = invoke_syscall( "NtCreateEvent", args32 ); + ok( status == STATUS_OBJECT_NAME_EXISTS, "syscall failed %lx\n", status ); + status = NtSetEvent( event2, NULL ); + ok( !status, "NtSetEvent failed %lx\n", status ); + args32[0] = HandleToLong( event2 ); + status = invoke_syscall( "NtClose", args32 ); + ok( !status, "syscall failed %lx\n", status ); + + status = NtClose( event ); + ok( !status, "NtClose failed %lx\n", status ); + + if (pNtWow64ReadVirtualMemory64) + { + TEB64 *teb64 = (TEB64 *)NtCurrentTeb()->GdiBatchCount; + PEB64 peb64, peb64_2; + ULONG64 res, res2; + HANDLE process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + ULONG args32[] = { HandleToLong( process ), (ULONG)teb64->Peb, teb64->Peb >> 32, + PtrToUlong(&peb64_2), sizeof(peb64_2), 0, PtrToUlong(&res2) }; + + ok( process != 0, "failed to open current process %lu\n", GetLastError() ); + status = pNtWow64ReadVirtualMemory64( process, teb64->Peb, &peb64, sizeof(peb64), &res ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + status = invoke_syscall( "NtWow64ReadVirtualMemory64", args32 ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + ok( res2 == res, "wrong len %s / %s\n", wine_dbgstr_longlong(res), wine_dbgstr_longlong(res2) ); + ok( !memcmp( &peb64, &peb64_2, res ), "data is different\n" ); + NtClose( process ); + } +} + +static void test_cpu_area(void) +{ + TEB64 *teb64 = (TEB64 *)NtCurrentTeb()->GdiBatchCount; + ULONG64 ptr; + NTSTATUS status; + + if (!is_wow64) return; + if (!code_mem) return; + if (!ntdll_module) return; + + if ((ptr = get_proc_address64( ntdll_module, "RtlWow64GetCurrentCpuArea" ))) + { + USHORT machine = 0xdead; + ULONG64 context, context_ex; + ULONG64 args[] = { (ULONG_PTR)&machine, (ULONG_PTR)&context, (ULONG_PTR)&context_ex }; + + status = call_func64( ptr, ARRAY_SIZE(args), args ); + ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); + ok( machine == IMAGE_FILE_MACHINE_I386, "wrong machine %x\n", machine ); + ok( context == teb64->TlsSlots[WOW64_TLS_CPURESERVED] + 4, "wrong context %s / %s\n", + wine_dbgstr_longlong(context), wine_dbgstr_longlong(teb64->TlsSlots[WOW64_TLS_CPURESERVED]) ); + ok( !context_ex, "got context_ex %s\n", wine_dbgstr_longlong(context_ex) ); + args[0] = args[1] = args[2] = 0; + status = call_func64( ptr, ARRAY_SIZE(args), args ); + ok( !status, "RtlWow64GetCpuAreaInfo failed %lx\n", status ); + } + else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" ); + +} + +static void test_exception_dispatcher(void) +{ + ULONG64 ptr, hook_ptr, hook, expect, res; + NTSTATUS status; + BYTE code[8]; + + if (!is_wow64) return; + if (!code_mem) return; + if (!ntdll_module) return; + + ptr = get_proc_address64( ntdll_module, "KiUserExceptionDispatcher" ); + ok( ptr, "KiUserExceptionDispatcher not found\n" ); + + if (pNtWow64ReadVirtualMemory64) + { + HANDLE process = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId() ); + + ok( process != 0, "failed to open current process %lu\n", GetLastError() ); + status = pNtWow64ReadVirtualMemory64( process, ptr, &code, sizeof(code), &res ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + + /* cld; mov xxx(%rip),%rax */ + ok( code[0] == 0xfc && code[1] == 0x48 && code[2] == 0x8b && code[3] == 0x05, + "wrong opcodes %02x %02x %02x %02x\n", code[0], code[1], code[2], code[3] ); + hook_ptr = ptr + 8 + *(int *)(code + 4); + status = pNtWow64ReadVirtualMemory64( process, hook_ptr, &hook, sizeof(hook), &res ); + ok( !status, "NtWow64ReadVirtualMemory64 failed %lx\n", status ); + + expect = get_proc_address64( wow64_module, "Wow64PrepareForException" ); + ok( hook == expect, "hook %I64x set to %I64x / %I64x\n", hook_ptr, hook, expect ); + NtClose( process ); + } +} + +#endif /* _WIN64 */ + +static void test_arm64ec(void) +{ +#ifdef __aarch64__ + PROCESS_INFORMATION pi; + char cmdline[MAX_PATH]; + char **argv; + + trace( "restarting test as arm64ec\n" ); + + winetest_get_mainargs( &argv ); + sprintf( cmdline, "%s %s", argv[0], argv[1] ); + if (create_process_machine( cmdline, 0, IMAGE_FILE_MACHINE_AMD64, &pi )) + { + DWORD exit_code, ret = WaitForSingleObject( pi.hProcess, 10000 ); + ok( ret == 0, "wait failed %lx\n", ret ); + GetExitCodeProcess( pi.hProcess, &exit_code ); + ok( exit_code == 0xbeef, "wrong exit code %lx\n", exit_code ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } + else skip( "could not start arm64ec process: %lu\n", GetLastError() ); +#endif +} + +START_TEST(wow64) +{ + init(); +#if !defined (__REACTOS__) || (DLL_EXPORT_VERSION >= 0x600) + test_query_architectures(); +#endif + test_peb_teb(); + test_selectors(); + test_image_mappings(); +#ifdef _WIN64 + test_xtajit64(); + test_cross_process_work_list(); +#else + test_nt_wow64(); + test_modules(); + test_init_block(); + test_iosb(); + test_syscalls(); +#endif + test_memory_notifications(); + test_cpu_area(); + test_exception_dispatcher(); + test_arm64ec(); +}