diff --git a/rostests/winetests/ntdll/directory.c b/rostests/winetests/ntdll/directory.c index f190ff452d5..68b54066214 100644 --- a/rostests/winetests/ntdll/directory.c +++ b/rostests/winetests/ntdll/directory.c @@ -51,7 +51,6 @@ static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG disable, ULONG * /* The attribute sets to test */ static struct testfile_s { - BOOL todo; /* set if it doesn't work on wine yet */ BOOL attr_done; /* set if attributes were tested for this file already */ const DWORD attr; /* desired attribute */ const char *name; /* filename to use */ @@ -60,13 +59,13 @@ static struct testfile_s { int nfound; /* How many were found (expect 1) */ WCHAR nameW[20]; /* unicode version of name (filled in later) */ } testfiles[] = { - { 0, 0, FILE_ATTRIBUTE_NORMAL, "n.tmp", NULL, "normal" }, - { 1, 0, FILE_ATTRIBUTE_HIDDEN, "h.tmp", NULL, "hidden" }, - { 1, 0, FILE_ATTRIBUTE_SYSTEM, "s.tmp", NULL, "system" }, - { 0, 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" }, - { 0, 0, FILE_ATTRIBUTE_DIRECTORY, ".", NULL, ". directory" }, - { 0, 0, FILE_ATTRIBUTE_DIRECTORY, "..", NULL, ".. directory" }, - { 0, 0, 0, NULL } + { 0, FILE_ATTRIBUTE_NORMAL, "n.tmp", NULL, "normal" }, + { 0, FILE_ATTRIBUTE_HIDDEN, "h.tmp", NULL, "hidden" }, + { 0, FILE_ATTRIBUTE_SYSTEM, "s.tmp", NULL, "system" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, "d.tmp", NULL, "directory" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, ".", NULL, ". directory" }, + { 0, FILE_ATTRIBUTE_DIRECTORY, "..", NULL, ".. directory" }, + { 0, 0, NULL } }; static const int max_test_dir_size = 20; /* size of above plus some for .. etc */ @@ -147,12 +146,7 @@ static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info) if (namelen != len || memcmp(nameW, testfiles[i].nameW, len*sizeof(WCHAR))) continue; if (!testfiles[i].attr_done) { - if (testfiles[i].todo) { - todo_wine - ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib); - } else { - ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib); - } + ok (attrib == (testfiles[i].attr & attribmask), "file %s: expected %s (%x), got %x (is your linux new enough?)\n", testfiles[i].name, testfiles[i].description, testfiles[i].attr, attrib); testfiles[i].attr_done = TRUE; } testfiles[i].nfound++; diff --git a/rostests/winetests/ntdll/exception.c b/rostests/winetests/ntdll/exception.c index 04cea9c6afb..1092393e3f8 100644 --- a/rostests/winetests/ntdll/exception.c +++ b/rostests/winetests/ntdll/exception.c @@ -46,11 +46,14 @@ static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec); static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID); static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func); static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler); +static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func); +static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler); 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 *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG); static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); +static NTSTATUS (WINAPI *pNtClose)(HANDLE); #if defined(__x86_64__) static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); @@ -936,6 +939,16 @@ static void test_debugger(void) /* here we handle exception */ } } + else if (stage == 7 || stage == 8) + { + ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE, + "unexpected exception code %08x, expected %08x\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); + + if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED; + } else ok(FALSE, "unexpected stage %x\n", stage); @@ -1695,6 +1708,7 @@ static void test_dynamic_unwind(void) #endif /* __x86_64__ */ +#if defined(__i386__) || defined(__x86_64__) static DWORD outputdebugstring_exceptions; static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo) @@ -1713,7 +1727,7 @@ static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *Exce return EXCEPTION_CONTINUE_SEARCH; } -static void test_outputdebugstring(DWORD numexc, BOOL todo) +static void test_outputdebugstring(DWORD numexc) { PVOID vectored_handler; @@ -1728,13 +1742,8 @@ static void test_outputdebugstring(DWORD numexc, BOOL todo) outputdebugstring_exceptions = 0; OutputDebugStringA("Hello World"); - if (todo) - todo_wine - ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n", - outputdebugstring_exceptions, numexc); - else - ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n", - outputdebugstring_exceptions, numexc); + ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n", + outputdebugstring_exceptions, numexc); pRtlRemoveVectoredExceptionHandler(vectored_handler); } @@ -1788,6 +1797,91 @@ static void test_ripevent(DWORD numexc) pRtlRemoveVectoredExceptionHandler(vectored_handler); } +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", + rec->ExceptionCode, EXCEPTION_INVALID_HANDLE); + ok(rec->NumberParameters == 0, "ExceptionParameters is %d 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) +{ + PVOID vectored_handler; + NTSTATUS status; + DWORD res; + + if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException) + { + skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n"); + return; + } + + vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler); + 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", + GetLastError(), ERROR_INVALID_HANDLE); + ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n", + invalid_handle_exceptions, numexc); + + 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); + + pRtlRemoveVectoredExceptionHandler(vectored_handler); +} + +static void test_vectored_continue_handler(void) +{ + PVOID handler1, handler2; + ULONG ret; + + if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler) + { + skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n"); + return; + } + + handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef); + ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n"); + + handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef); + ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n"); + ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n"); + + if (pRtlRemoveVectoredExceptionHandler) + { + ret = pRtlRemoveVectoredExceptionHandler(handler1); + ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n"); + } + + ret = pRtlRemoveVectoredContinueHandler(handler1); + ok(ret, "RtlRemoveVectoredContinueHandler failed\n"); + + ret = pRtlRemoveVectoredContinueHandler(handler2); + ok(ret, "RtlRemoveVectoredContinueHandler failed\n"); + + ret = pRtlRemoveVectoredContinueHandler(handler1); + ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n"); + + ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344); + ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n"); +} +#endif /* defined(__i386__) || defined(__x86_64__) */ + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -1802,6 +1896,7 @@ START_TEST(exception) 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" ); pNtTerminateProcess = (void *)GetProcAddress( hntdll, "NtTerminateProcess" ); @@ -1809,6 +1904,10 @@ START_TEST(exception) "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, @@ -1858,13 +1957,17 @@ START_TEST(exception) run_rtlraiseexception_test(EXCEPTION_BREAKPOINT); run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE); test_stage = 3; - test_outputdebugstring(0, FALSE); + test_outputdebugstring(0); test_stage = 4; - test_outputdebugstring(2, TRUE); /* is this a Windows bug? */ + test_outputdebugstring(2); test_stage = 5; test_ripevent(0); test_stage = 6; test_ripevent(1); + test_stage = 7; + test_closehandle(0); + test_stage = 8; + test_closehandle(1); } else skip( "RtlRaiseException not found\n" ); @@ -1876,8 +1979,10 @@ START_TEST(exception) test_unwind(); test_exceptions(); test_rtlraiseexception(); - test_outputdebugstring(1, FALSE); + test_outputdebugstring(1); test_ripevent(1); + test_closehandle(0); + test_vectored_continue_handler(); test_debugger(); test_simd_exceptions(); test_fpu_exceptions(); @@ -1894,8 +1999,10 @@ START_TEST(exception) pRtlLookupFunctionEntry = (void *)GetProcAddress( hntdll, "RtlLookupFunctionEntry" ); - test_outputdebugstring(1, FALSE); + test_outputdebugstring(1); test_ripevent(1); + test_closehandle(0); + test_vectored_continue_handler(); test_virtual_unwind(); if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) diff --git a/rostests/winetests/ntdll/file.c b/rostests/winetests/ntdll/file.c index 0d526098c65..5ba1ed037f6 100644 --- a/rostests/winetests/ntdll/file.c +++ b/rostests/winetests/ntdll/file.c @@ -38,6 +38,33 @@ #include "winuser.h" #include "wine/winioctl.h" +/* FIXME */ +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; + #ifndef IO_COMPLETION_ALL_ACCESS #define IO_COMPLETION_ALL_ACCESS 0x001F0003 #endif @@ -1234,7 +1261,7 @@ static void test_file_basic_information(void) 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_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes ); + ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM (ok in old linux without xattr)\n", fbi.FileAttributes ); /* Then HIDDEN */ memset(&fbi, 0, sizeof(fbi)); @@ -1247,7 +1274,7 @@ static void test_file_basic_information(void) 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_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes ); + ok ( (fbi.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN (ok in old linux without xattr)\n", fbi.FileAttributes ); /* Check NORMAL last of all (to make sure we can clear attributes) */ memset(&fbi, 0, sizeof(fbi)); @@ -1304,7 +1331,7 @@ static void test_file_all_information(void) 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); - todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fai_buf.fai.BasicInformation.FileAttributes ); + 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 ); /* Then HIDDEN */ memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation)); @@ -1317,7 +1344,7 @@ static void test_file_all_information(void) 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_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fai_buf.fai.BasicInformation.FileAttributes ); + 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 ); /* Check NORMAL last of all (to make sure we can clear attributes) */ memset(&fai_buf.fai.BasicInformation, 0, sizeof(fai_buf.fai.BasicInformation)); @@ -1371,7 +1398,6 @@ static void test_file_disposition_information(void) ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "Unexpected NtQueryInformationFile result (expected STATUS_INVALID_INFO_CLASS, got %x)\n", res ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_ACCESS_DENIED, "unexpected FileDispositionInformation result (expected STATUS_ACCESS_DENIED, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1384,14 +1410,26 @@ 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 ); - todo_wine 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; - todo_wine ok( fileDeleted, "File should have been deleted\n" ); DeleteFileA( buffer ); + /* cannot set disposition on readonly file */ + 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" ); + 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" ); + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); + /* cannot set disposition on readonly file */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); @@ -1402,6 +1440,21 @@ static void test_file_disposition_information(void) 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; + todo_wine + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); + + /* cannot set disposition on readonly file */ + 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" ); + 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" ); SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); DeleteFileA( buffer ); @@ -1412,11 +1465,9 @@ 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 ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine 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; @@ -1429,7 +1480,6 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine 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; @@ -1444,7 +1494,6 @@ static void test_file_disposition_information(void) CloseHandle( handle ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle2, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle2 ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1459,11 +1508,9 @@ 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 ); - todo_wine 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; - todo_wine ok( fileDeleted, "Directory should have been deleted\n" ); RemoveDirectoryA( buffer ); @@ -1476,7 +1523,6 @@ static void test_file_disposition_information(void) RemoveDirectoryA( buffer ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine 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; @@ -1501,6 +1547,7 @@ static void test_file_disposition_information(void) 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 ); } @@ -2683,6 +2730,178 @@ todo_wine CloseHandle(hfile); } +static INT build_reparse_buffer(WCHAR *filename, REPARSE_DATA_BUFFER **pbuffer) +{ + 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; + UNICODE_STRING nameW; + HANDLE hJunction; + WCHAR *dest; + BOOL bret; + + /* Create a temporary folder for the junction point tests */ + GetTempFileNameW(dotW, 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)) + { + skip("File system does not support junction points.\n"); + RemoveDirectoryW(path); + 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"); + + /* 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); + + /* 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()); + + /* 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); + + /* 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)); + + /* 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 (0x%llx != 0x%llx).\n", + new_attrib.LastAccessTime.QuadPart, old_attrib.LastAccessTime.QuadPart); + CloseHandle(hJunction); + + /* 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); + + /* 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); + + /* 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"); + +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); +} + START_TEST(file) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -2736,4 +2955,5 @@ START_TEST(file) test_file_disposition_information(); test_query_volume_information_file(); test_query_attribute_information_file(); + test_junction_points(); } diff --git a/rostests/winetests/ntdll/info.c b/rostests/winetests/ntdll/info.c index af0040166f8..5fff4908683 100755 --- a/rostests/winetests/ntdll/info.c +++ b/rostests/winetests/ntdll/info.c @@ -737,6 +737,84 @@ static void test_query_processor_power_info(void) HeapFree(GetProcessHeap(), 0, ppi); } +static void test_query_process_wow64(void) +{ + NTSTATUS status; + ULONG ReturnLength; + ULONG_PTR pbi[2], dummy; + + 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); + + /* Use a correct info class and buffer size, but still no handle and buffer */ + status = pNtQueryInformationProcess(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); + + /* 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); + + /* 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]); + + /* 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]); + + /* 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]); + /* Test written size on 64 bit by checking high 32 bit buffer */ + if (sizeof(ULONG_PTR) > sizeof(DWORD)) + { + DWORD *ptr = (DWORD *)pbi; + ok( ptr[1] != (DWORD)dummy, "ptr[1] unchanged!\n"); + } + + /* 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); + + /* 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); + + /* 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); +} + static void test_query_process_basic(void) { NTSTATUS status; @@ -1714,6 +1792,10 @@ START_TEST(info) 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(); diff --git a/rostests/winetests/ntdll/om.c b/rostests/winetests/ntdll/om.c index ca843993294..2cf59cf7402 100644 --- a/rostests/winetests/ntdll/om.c +++ b/rostests/winetests/ntdll/om.c @@ -835,14 +835,14 @@ static void test_event(void) status = pNtOpenEvent(&Event2, GENERIC_ALL, &attr); ok( status == STATUS_SUCCESS, "NtOpenEvent failed %08x\n", status ); - status = pNtClose(Event); + 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 ); - status = pNtClose(Event2); + pNtClose(Event2); } static const WCHAR keyed_nameW[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s', diff --git a/rostests/winetests/ntdll/reg.c b/rostests/winetests/ntdll/reg.c index 159466576eb..b325434270d 100755 --- a/rostests/winetests/ntdll/reg.c +++ b/rostests/winetests/ntdll/reg.c @@ -129,11 +129,14 @@ static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE); static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, ULONG TitleIndex, const UNICODE_STRING *class, ULONG options, 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 * 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); static LPVOID (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG); static NTSTATUS (WINAPI * pRtlAppendUnicodeToString)(PUNICODE_STRING, PCWSTR); @@ -166,7 +169,6 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz) NTDLL_GET_PROC(RtlCreateUnicodeString) NTDLL_GET_PROC(RtlFreeUnicodeString) - NTDLL_GET_PROC(NtDeleteValueKey) NTDLL_GET_PROC(RtlQueryRegistryValues) NTDLL_GET_PROC(RtlCheckRegistryKey) NTDLL_GET_PROC(RtlOpenCurrentUser) @@ -175,11 +177,13 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(NtCreateKey) NTDLL_GET_PROC(NtFlushKey) NTDLL_GET_PROC(NtDeleteKey) + NTDLL_GET_PROC(NtQueryKey) NTDLL_GET_PROC(NtQueryValueKey) NTDLL_GET_PROC(NtQueryInformationProcess) NTDLL_GET_PROC(NtSetValueKey) NTDLL_GET_PROC(NtOpenKey) NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath) + NTDLL_GET_PROC(RtlCompareUnicodeString) NTDLL_GET_PROC(RtlReAllocateHeap) NTDLL_GET_PROC(RtlAppendUnicodeToString) NTDLL_GET_PROC(RtlUnicodeStringToAnsiString) @@ -188,6 +192,10 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(RtlZeroMemory) NTDLL_GET_PROC(RtlpNtQueryValueKey) NTDLL_GET_PROC(RtlOpenCurrentUser) + + /* optional functions */ + pNtQueryLicenseValue = (void *)GetProcAddress(hntdll, "NtQueryLicenseValue"); + return TRUE; } #undef NTDLL_GET_PROC @@ -642,6 +650,180 @@ static void test_NtDeleteKey(void) ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status); } +static void test_NtQueryLicenseKey(void) +{ + static const WCHAR emptyW[] = {'E','M','P','T','Y',0}; + UNICODE_STRING name; + WORD buffer[32]; + NTSTATUS status; + ULONG type, len; + DWORD value; + + if (!pNtQueryLicenseValue) + { + win_skip("NtQueryLicenseValue not found, skipping tests\n"); + return; + } + + type = 0xdead; + 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); + + /* test with empty key */ + pRtlCreateUnicodeStringFromAsciiz(&name, ""); + + 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); + + 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); + + 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); + + 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); + + pRtlFreeUnicodeString(&name); + + /* test with nonexistent licence key */ + pRtlCreateUnicodeStringFromAsciiz(&name, "Nonexistent-License-Value"); + + 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); + + 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); + + 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); + + type = 0xdead; + len = 0xbeef; + status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len); + ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue unexpected suceeded\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); + + pRtlFreeUnicodeString(&name); + + /* test with REG_SZ license key */ + pRtlCreateUnicodeStringFromAsciiz(&name, "Kernel-MUI-Language-Allowed"); + + 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); + + 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); + + 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); + + 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); + + 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(!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(buffer[0] == 0x1111, "expected buffer[0] = 0x1111, got %u\n", buffer[0]); + + pRtlFreeUnicodeString(&name); + + /* test with REG_DWORD license key */ + pRtlCreateUnicodeStringFromAsciiz(&name, "Kernel-MUI-Number-Allowed"); + + 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); + + 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); + + 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); + + 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); + + 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(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); + + pRtlFreeUnicodeString(&name); +} + static void test_RtlpNtQueryValueKey(void) { NTSTATUS status; @@ -1277,6 +1459,51 @@ static void test_long_value_name(void) pNtClose(key); } +static void test_NtQueryKey(void) +{ + HANDLE key; + NTSTATUS status; + OBJECT_ATTRIBUTES attr; + ULONG length, len; + KEY_NAME_INFORMATION *info = NULL; + UNICODE_STRING str; + + InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0); + status = pNtOpenKey(&key, KEY_READ, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status); + + status = pNtQueryKey(key, KeyNameInformation, NULL, 0, &length); + if (status == STATUS_INVALID_PARAMETER) { + win_skip("KeyNameInformation is not supported\n"); + pNtClose(key); + return; + } + todo_wine ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08x\n", status); + info = HeapAlloc(GetProcessHeap(), 0, length); + + /* non-zero buffer size, but insufficient */ + 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", + info->NameLength, winetestpath.Length); + + /* correct buffer size */ + 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); + + str.Buffer = info->Name; + str.Length = info->NameLength; + ok(pRtlCompareUnicodeString(&winetestpath, &str, TRUE) == 0, + "got %s, expected %s\n", + wine_dbgstr_wn(str.Buffer, str.Length/sizeof(WCHAR)), + wine_dbgstr_wn(winetestpath.Buffer, winetestpath.Length/sizeof(WCHAR))); + + HeapFree(GetProcessHeap(), 0, info); + pNtClose(key); +} + START_TEST(reg) { static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0}; @@ -1297,7 +1524,9 @@ START_TEST(reg) test_RtlQueryRegistryValues(); test_RtlpNtQueryValueKey(); test_NtFlushKey(); + test_NtQueryKey(); test_NtQueryValueKey(); + test_NtQueryLicenseKey(); test_long_value_name(); test_NtDeleteKey(); test_symlinks(); diff --git a/rostests/winetests/ntdll/rtl.c b/rostests/winetests/ntdll/rtl.c index e8eb04af76f..c335a7bf838 100755 --- a/rostests/winetests/ntdll/rtl.c +++ b/rostests/winetests/ntdll/rtl.c @@ -62,6 +62,8 @@ static inline USHORT __my_ushort_swap(USHORT s) /* Function ptrs for ntdll calls */ static HMODULE hntdll = 0; +static PVOID (WINAPI *pWinSqmStartSession)(PVOID unknown1, DWORD unknown2, DWORD unknown3); +static NTSTATUS (WINAPI *pWinSqmEndSession)(PVOID unknown1); 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); @@ -89,9 +91,14 @@ static IMAGE_BASE_RELOCATION *(WINAPI *pLdrProcessRelocationBlock)(void*,UINT,US 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 NTSTATUS (WINAPI *pLdrAddRefDll)(ULONG, HMODULE); static NTSTATUS (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG*, ULONG_PTR*); static NTSTATUS (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR); +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 HMODULE hkernel32 = 0; static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); @@ -109,6 +116,8 @@ static void InitFunctionPtrs(void) hntdll = LoadLibraryA("ntdll.dll"); ok(hntdll != 0, "LoadLibrary failed\n"); if (hntdll) { + pWinSqmStartSession = (void *)GetProcAddress(hntdll, "WinSqmStartSession"); + pWinSqmEndSession = (void *)GetProcAddress(hntdll, "WinSqmEndSession"); pRtlCompareMemory = (void *)GetProcAddress(hntdll, "RtlCompareMemory"); pRtlCompareMemoryUlong = (void *)GetProcAddress(hntdll, "RtlCompareMemoryUlong"); pRtlDeleteTimer = (void *)GetProcAddress(hntdll, "RtlDeleteTimer"); @@ -136,9 +145,14 @@ static void InitFunctionPtrs(void) pRtlIpv4AddressToStringA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringA"); pRtlIpv4AddressToStringExA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringExA"); pRtlIpv4StringToAddressA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressA"); + pRtlIpv4StringToAddressExA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressExA"); pLdrAddRefDll = (void *)GetProcAddress(hntdll, "LdrAddRefDll"); pLdrLockLoaderLock = (void *)GetProcAddress(hntdll, "LdrLockLoaderLock"); pLdrUnlockLoaderLock = (void *)GetProcAddress(hntdll, "LdrUnlockLoaderLock"); + pRtlGetCompressionWorkSpaceSize = (void *)GetProcAddress(hntdll, "RtlGetCompressionWorkSpaceSize"); + pRtlDecompressBuffer = (void *)GetProcAddress(hntdll, "RtlDecompressBuffer"); + pRtlDecompressFragment = (void *)GetProcAddress(hntdll, "RtlDecompressFragment"); + pRtlCompressBuffer = (void *)GetProcAddress(hntdll, "RtlCompressBuffer"); } hkernel32 = LoadLibraryA("kernel32.dll"); ok(hkernel32 != 0, "LoadLibrary failed\n"); @@ -149,6 +163,46 @@ static void InitFunctionPtrs(void) ok(strlen(src) == 15, "Source must be 16 bytes long!\n"); } +#ifdef __i386__ +const char stdcall3_thunk[] = + "\x56" /* push %esi */ + "\x89\xE6" /* mov %esp, %esi */ + "\xFF\x74\x24\x14" /* pushl 20(%esp) */ + "\xFF\x74\x24\x14" /* pushl 20(%esp) */ + "\xFF\x74\x24\x14" /* pushl 20(%esp) */ + "\xFF\x54\x24\x14" /* calll 20(%esp) */ + "\x89\xF0" /* mov %esi, %eax */ + "\x29\xE0" /* sub %esp, %eax */ + "\x89\xF4" /* mov %esi, %esp */ + "\x5E" /* pop %esi */ + "\xC2\x10\x00" /* ret $16 */ +; + +static INT (WINAPI *call_stdcall_func3)(PVOID func, PVOID arg0, DWORD arg1, DWORD arg2) = NULL; + +static void test_WinSqm(void) +{ + INT args; + + if (!pWinSqmStartSession) + { + win_skip("WinSqmStartSession() is not available\n"); + return; + } + + call_stdcall_func3 = (void*) VirtualAlloc( NULL, sizeof(stdcall3_thunk) - 1, MEM_COMMIT, + PAGE_EXECUTE_READWRITE ); + memcpy( call_stdcall_func3, stdcall3_thunk, sizeof(stdcall3_thunk) - 1 ); + + args = 3 - call_stdcall_func3( pWinSqmStartSession, NULL, 0, 0 ) / 4; + ok(args == 3, "WinSqmStartSession expected to take %d arguments instead of 3\n", args); + args = 3 - call_stdcall_func3( pWinSqmEndSession, NULL, 0, 0 ) / 4; + ok(args == 1, "WinSqmEndSession expected to take %d arguments instead of 1\n", args); + + VirtualFree( call_stdcall_func3, 0, MEM_RELEASE ); +} +#endif + #define COMP(str1,str2,cmplen,len) size = pRtlCompareMemory(str1, str2, cmplen); \ ok(size == len, "Expected %ld, got %ld\n", size, (SIZE_T)len) @@ -1492,6 +1546,94 @@ static void test_RtlIpv4StringToAddress(void) } } +static void test_RtlIpv4StringToAddressEx(void) +{ + NTSTATUS res; + IN_ADDR ip, expected_ip; + USHORT port; + struct + { + PCSTR address; + NTSTATUS res; + int ip[4]; + USHORT port; + } tests[] = + { + { "", STATUS_INVALID_PARAMETER, { -1 }, 0xdead }, + { " ", STATUS_INVALID_PARAMETER, { -1 }, 0xdead }, + { "1.1.1.1:", STATUS_INVALID_PARAMETER, { 1, 1, 1, 1 }, 0xdead }, + { "1.1.1.1+", STATUS_INVALID_PARAMETER, { 1, 1, 1, 1 }, 0xdead }, + { "1.1.1.1:1", STATUS_SUCCESS, { 1, 1, 1, 1 }, 0x100 }, + { "0.0.0.0:0", STATUS_INVALID_PARAMETER, { 0, 0, 0, 0 }, 0xdead }, + { "0.0.0.0:1", STATUS_SUCCESS, { 0, 0, 0, 0 }, 0x100 }, + { "1.2.3.4:65535", STATUS_SUCCESS, { 1, 2, 3, 4 }, 65535 }, + { "1.2.3.4:65536", STATUS_INVALID_PARAMETER, { 1, 2, 3, 4 }, 0xdead }, + { "1.2.3.4:0xffff", STATUS_SUCCESS, { 1, 2, 3, 4 }, 65535 }, + { "1.2.3.4:0XfFfF", STATUS_SUCCESS, { 1, 2, 3, 4 }, 65535 }, + { "1.2.3.4:011064", STATUS_SUCCESS, { 1, 2, 3, 4 }, 0x3412 }, + { "1.2.3.4:1234a", STATUS_INVALID_PARAMETER, { 1, 2, 3, 4 }, 0xdead }, + { "1.2.3.4:1234+", STATUS_INVALID_PARAMETER, { 1, 2, 3, 4 }, 0xdead }, + { "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 int testcount = sizeof(tests) / sizeof(tests[0]); + int i, Strict; + + if (!pRtlIpv4StringToAddressExA) + { + skip("RtlIpv4StringToAddressEx not available\n"); + return; + } + + /* do not crash, and do not touch the ip / port. */ + 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", 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(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", 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", 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(port == 0xdead, "RtlIpv4StringToAddressExA should not touch the port!, port == %x\n", port); + + for (i = 0; i < testcount; 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++) + { + ip.S_un.S_addr = 0xabababab; + port = 0xdead; + res = pRtlIpv4StringToAddressExA(tests[i].address, Strict, &ip, &port); + if (tests[i].ip[0] == -1) + { + expected_ip.S_un.S_addr = 0xabababab; + } + else + { + expected_ip.S_un.S_un_b.s_b1 = tests[i].ip[0]; + expected_ip.S_un.S_un_b.s_b2 = tests[i].ip[1]; + expected_ip.S_un.S_un_b.s_b3 = tests[i].ip[2]; + expected_ip.S_un.S_un_b.s_b4 = tests[i].ip[3]; + } + ok(res == tests[i].res, "[%s] res = 0x%08x, expected 0x%08x\n", + tests[i].address, res, tests[i].res); + ok(ip.S_un.S_addr == expected_ip.S_un.S_addr, "[%s] ip = %08x, expected %08x\n", + tests[i].address, ip.S_un.S_addr, expected_ip.S_un.S_addr); + ok(port == tests[i].port, "[%s] port = %u, expected %u\n", + tests[i].address, port, tests[i].port); + } + } +} + static void test_LdrAddRefDll(void) { HMODULE mod, mod2; @@ -1599,10 +1741,745 @@ static void test_LdrLockLoaderLock(void) pLdrUnlockLoaderLock(0, magic); } +static void test_RtlGetCompressionWorkSpaceSize(void) +{ + ULONG compress_workspace, decompress_workspace; + NTSTATUS status; + + if (!pRtlGetCompressionWorkSpaceSize) + { + win_skip("RtlGetCompressionWorkSpaceSize is not available\n"); + return; + } + + status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_NONE, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); + + status = pRtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_DEFAULT, &compress_workspace, + &decompress_workspace); + ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status); + + status = pRtlGetCompressionWorkSpaceSize(0xFF, &compress_workspace, &decompress_workspace); + ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status); + + 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 %d\n", compress_workspace); + ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %d\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 %d\n", compress_workspace); + ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %d\n", decompress_workspace); +} + +/* helper for test_RtlDecompressBuffer, checks if a chunk is incomplete */ +static BOOL is_incomplete_chunk(const UCHAR *compressed, ULONG compressed_size, BOOL check_all) +{ + ULONG chunk_size; + if (compressed_size <= sizeof(WORD)) + return TRUE; + while (compressed_size >= sizeof(WORD)) + { + chunk_size = (*(WORD *)compressed & 0xFFF) + 1; + if (compressed_size < sizeof(WORD) + chunk_size) + return TRUE; + if (!check_all) + break; + compressed += sizeof(WORD) + chunk_size; + compressed_size -= sizeof(WORD) + chunk_size; + } + return FALSE; +} + +#define DECOMPRESS_BROKEN_TRUNCATED 1 +#define DECOMPRESS_BROKEN_FRAGMENT0 2 +#define DECOMPRESS_BROKEN_FRAGMENT1 4 +#define DECOMPRESS_BROKEN_FRAGMENT4095 8 + +static void test_RtlDecompressBuffer(void) +{ + static const UCHAR test_multiple_chunks[] = {0x03, 0x30, 'W', 'i', 'n', 'e', + 0x03, 0x30, 'W', 'i', 'n', 'e'}; + static const struct + { + UCHAR compressed[32]; + ULONG compressed_size; + NTSTATUS status; + UCHAR uncompressed[32]; + ULONG uncompressed_size; + DWORD broken_flags; + } + test_lznt[] = + { + /* 4 byte uncompressed chunk */ + { + {0x03, 0x30, 'W', 'i', 'n', 'e'}, + 6, + STATUS_SUCCESS, + "Wine", + 4, + DECOMPRESS_BROKEN_FRAGMENT4095 | + DECOMPRESS_BROKEN_FRAGMENT1 | + DECOMPRESS_BROKEN_FRAGMENT0 + }, + /* 8 byte uncompressed chunk */ + { + {0x07, 0x30, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'}, + 10, + STATUS_SUCCESS, + "WineWine", + 8, + DECOMPRESS_BROKEN_FRAGMENT4095 | + DECOMPRESS_BROKEN_FRAGMENT1 | + DECOMPRESS_BROKEN_FRAGMENT0 + }, + /* 4 byte compressed chunk */ + { + {0x04, 0xB0, 0x00, 'W', 'i', 'n', 'e'}, + 7, + STATUS_SUCCESS, + "Wine", + 4 + }, + /* 8 byte compressed chunk */ + { + {0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'}, + 11, + STATUS_SUCCESS, + "WineWine", + 8 + }, + /* compressed chunk using backwards reference */ + { + {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x01, 0x30}, + 9, + STATUS_SUCCESS, + "WineWine", + 8, + DECOMPRESS_BROKEN_TRUNCATED + }, + /* compressed chunk using backwards reference with length > bytes_read */ + { + {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x30}, + 9, + STATUS_SUCCESS, + "WineWineWine", + 12, + DECOMPRESS_BROKEN_TRUNCATED + }, + /* same as above, but unused bits != 0 */ + { + {0x06, 0xB0, 0x30, 'W', 'i', 'n', 'e', 0x01, 0x30}, + 9, + STATUS_SUCCESS, + "WineWine", + 8, + DECOMPRESS_BROKEN_TRUNCATED + }, + /* compressed chunk without backwards reference and unused bits != 0 */ + { + {0x01, 0xB0, 0x02, 'W'}, + 4, + STATUS_SUCCESS, + "W", + 1 + }, + /* termination sequence after first chunk */ + { + {0x03, 0x30, 'W', 'i', 'n', 'e', 0x00, 0x00, 0x03, 0x30, 'W', 'i', 'n', 'e'}, + 14, + STATUS_SUCCESS, + "Wine", + 4, + DECOMPRESS_BROKEN_FRAGMENT4095 | + DECOMPRESS_BROKEN_FRAGMENT1 | + DECOMPRESS_BROKEN_FRAGMENT0 + }, + /* compressed chunk using backwards reference with 4 bit offset, 12 bit length */ + { + {0x14, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 0x01, 0x01, 0xF0}, + 23, + STATUS_SUCCESS, + "ABCDEFGHIJKLMNOPABCD", + 20, + DECOMPRESS_BROKEN_TRUNCATED + }, + /* compressed chunk using backwards reference with 5 bit offset, 11 bit length */ + { + {0x15, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 0x02, 'A', 0x00, 0x78}, + 24, + STATUS_SUCCESS, + "ABCDEFGHIJKLMNOPABCD", + 20, + DECOMPRESS_BROKEN_TRUNCATED + }, + /* uncompressed chunk with invalid magic */ + { + {0x03, 0x20, 'W', 'i', 'n', 'e'}, + 6, + STATUS_SUCCESS, + "Wine", + 4, + DECOMPRESS_BROKEN_FRAGMENT4095 | + DECOMPRESS_BROKEN_FRAGMENT1 | + DECOMPRESS_BROKEN_FRAGMENT0 + }, + /* compressed chunk with invalid magic */ + { + {0x04, 0xA0, 0x00, 'W', 'i', 'n', 'e'}, + 7, + STATUS_SUCCESS, + "Wine", + 4 + }, + /* garbage byte after end of buffer */ + { + {0x00, 0xB0, 0x02, 0x01}, + 4, + STATUS_SUCCESS, + "", + 0 + }, + /* empty compressed chunk */ + { + {0x00, 0xB0, 0x00}, + 3, + STATUS_SUCCESS, + "", + 0 + }, + /* empty compressed chunk with unused bits != 0 */ + { + {0x00, 0xB0, 0x01}, + 3, + STATUS_SUCCESS, + "", + 0 + }, + /* empty input buffer */ + { + {0}, + 0, + STATUS_BAD_COMPRESSION_BUFFER, + }, + /* incomplete chunk header */ + { + {0x01}, + 1, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* incomplete chunk header */ + { + {0x00, 0x30}, + 2, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* compressed chunk with invalid backwards reference */ + { + {0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x40}, + 9, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* compressed chunk with incomplete backwards reference */ + { + {0x05, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05}, + 8, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* incomplete uncompressed chunk */ + { + {0x07, 0x30, 'W', 'i', 'n', 'e'}, + 6, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* incomplete compressed chunk */ + { + {0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e'}, + 7, + STATUS_BAD_COMPRESSION_BUFFER + }, + /* two compressed chunks, the second one incomplete */ + { + {0x00, 0xB0, 0x02, 0x00, 0xB0}, + 5, + STATUS_BAD_COMPRESSION_BUFFER, + } + }; + + static UCHAR buf[0x2000], workspace[0x1000]; + NTSTATUS status, expected_status; + 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) - 1, 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 %d\n", final_size); + + final_size = 0xdeadbeef; + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_DEFAULT, buf, sizeof(buf) - 1, 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 %d\n", final_size); + + final_size = 0xdeadbeef; + status = pRtlDecompressBuffer(0xFF, buf, sizeof(buf) - 1, 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 %d\n", final_size); + + /* regular tests for RtlDecompressBuffer */ + for (i = 0; i < sizeof(test_lznt) / sizeof(test_lznt[0]); i++) + { + trace("Running test %d (compressed_size=%d, compressed_size=%d, status=%d)\n", + i, test_lznt[i].compressed_size, test_lznt[i].compressed_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); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + if (!status) + { + ok(final_size == test_lznt[i].uncompressed_size, + "%d: got wrong final_size %d\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[%d] overwritten\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); + ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status); + if (!status) + { + ok(final_size == test_lznt[i].uncompressed_size, + "%d: got wrong final_size %d\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[%d] overwritten\n", i, test_lznt[i].uncompressed_size); + } + + /* test with expected output size */ + if (test_lznt[i].uncompressed_size > 0) + { + 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); + if (!status) + { + ok(final_size == test_lznt[i].uncompressed_size, + "%d: got wrong final_size %d\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[%d] overwritten\n", i, test_lznt[i].uncompressed_size); + } + } + + /* test with smaller output size */ + if (test_lznt[i].uncompressed_size > 1) + { + 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); + 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%08x\n", i, status); + if (!status) + { + ok(final_size == test_lznt[i].uncompressed_size - 1, + "%d: got wrong final_size %d\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[%d] overwritten\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); + 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); + } + else + { + ok(status == STATUS_SUCCESS, "%d: got wrong status 0x%08x\n", i, status); + ok(final_size == 0, "%d: got wrong final_size %d\n", i, final_size); + ok(buf[0] == 0x11, "%d: buf[%d] overwritten\n", i, test_lznt[i].uncompressed_size); + } + + /* test RtlDecompressBuffer 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); + ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER && + (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT0)), "%d: got wrong status 0x%08x\n", i, status); + if (!status) + { + ok(final_size == test_lznt[i].uncompressed_size, + "%d: got wrong final_size %d\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[%d] overwritten\n", i, test_lznt[i].uncompressed_size); + } + + /* test RtlDecompressBuffer 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); + ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER && + (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT1)), "%d: got wrong status 0x%08x\n", i, status); + if (!status) + { + if (test_lznt[i].uncompressed_size == 0) + { + todo_wine + ok(final_size == 4095, + "%d: got wrong final size %d\n", i, final_size); + /* Buffer doesn't contain any useful value on Windows */ + ok(buf[4095] == 0x11, + "%d: buf[4095] overwritten\n", i); + } + else + { + ok(final_size == test_lznt[i].uncompressed_size - 1, + "%d: got wrong final_size %d\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[%d] overwritten\n", i, test_lznt[i].uncompressed_size - 1); + } + } + + /* test RtlDecompressBuffer 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); + ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER && + (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT4095)), "%d: got wrong status 0x%08x\n", i, status); + if (!status) + { + todo_wine + ok(final_size == 1, + "%d: got wrong final size %d\n", i, final_size); + todo_wine + ok(buf[0] == 0, + "%d: padding is not zero\n", i); + ok(buf[1] == 0x11, + "%d: buf[1] overwritten\n", i); + } + + /* test RtlDecompressBuffer 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); + 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); + if (!status) + { + ok(final_size == 0, + "%d: got wrong final size %d\n", i, final_size); + ok(buf[0] == 0x11, + "%d: buf[4096] overwritten\n", i); + } + } + + /* test decoding of multiple chunks with pRtlDecompressBuffer */ + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4100, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0 && buf[4095] == 0, "padding is not zero\n"); + ok(!memcmp(buf + 4096, "Wine", 4), "got wrong decoded data at offset 4096\n"); + ok(buf[4100] == 0x11, "buf[4100] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4097, test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4097, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0 && buf[4095] == 0, "padding is not zero\n"); + ok(buf[4096], "got wrong decoded data at offset 4096\n"); + ok(buf[4097] == 0x11, "buf[4097] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4096, test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0x11, "buf[4] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 4, test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0x11, "buf[4] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 3, test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 3, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 3), "got wrong decoded data at offset 0\n"); + ok(buf[3] == 0x11, "buf[3] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_multiple_chunks, + sizeof(test_multiple_chunks), &final_size); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 0, "got wrong final_size %d\n", final_size); + ok(buf[0] == 0x11, "buf[0] overwritten\n"); + } + + /* test multiple chunks in combination with RtlDecompressBuffer and offset=1 */ + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks, + sizeof(test_multiple_chunks), 1, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4099, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n"); + ok(buf[3] == 0 && buf[4094] == 0, "padding is not zero\n"); + ok(!memcmp(buf + 4095, "Wine", 4), "got wrong decoded data at offset 4095\n"); + ok(buf[4099] == 0x11, "buf[4099] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 4096, test_multiple_chunks, + sizeof(test_multiple_chunks), 1, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4096, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n"); + ok(buf[3] == 0 && buf[4094] == 0, "padding is not zero\n"); + ok(buf[4095] == 'W', "got wrong decoded data at offset 4095\n"); + ok(buf[4096] == 0x11, "buf[4096] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 4095, test_multiple_chunks, + sizeof(test_multiple_chunks), 1, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 3, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0x11, "buf[4] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, 3, test_multiple_chunks, + sizeof(test_multiple_chunks), 1, &final_size, workspace); + ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 3, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "ine", 3), "got wrong decoded data at offset 0\n"); + ok(buf[3] == 0x11, "buf[3] overwritten\n"); + } + + /* test multiple chunks in combination with RtlDecompressBuffer and offset=4 */ + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks, + sizeof(test_multiple_chunks), 4, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4096, "got wrong final_size %d\n", final_size); + ok(buf[0] == 0 && buf[4091] == 0, "padding is not zero\n"); + ok(!memcmp(buf + 4092, "Wine", 4), "got wrong decoded data at offset 4092\n"); + ok(buf[4096] == 0x11, "buf[4096] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks, + sizeof(test_multiple_chunks), 4095, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 5, "got wrong final_size %d\n", final_size); + ok(buf[0] == 0, "padding is not zero\n"); + ok(!memcmp(buf + 1, "Wine", 4), "got wrong decoded data at offset 1\n"); + ok(buf[5] == 0x11, "buf[5] overwritten\n"); + } + + final_size = 0xdeadbeef; + memset(buf, 0x11, sizeof(buf)); + status = pRtlDecompressFragment(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_multiple_chunks, + sizeof(test_multiple_chunks), 4096, &final_size, workspace); + ok(status == STATUS_SUCCESS || broken(status == STATUS_BAD_COMPRESSION_BUFFER), + "got wrong status 0x%08x\n", status); + if (!status) + { + ok(final_size == 4, "got wrong final_size %d\n", final_size); + ok(!memcmp(buf, "Wine", 4), "got wrong decoded data at offset 0\n"); + ok(buf[4] == 0x11, "buf[4] overwritten\n"); + } + +} + +static void test_RtlCompressBuffer(void) +{ + ULONG compress_workspace, decompress_workspace; + static const UCHAR test_buffer[] = "WineWineWine"; + static UCHAR buf1[0x1000], buf2[0x1000], *workspace; + ULONG final_size, buf_size; + NTSTATUS status; + + if (!pRtlCompressBuffer || !pRtlGetCompressionWorkSpaceSize) + { + win_skip("RtlCompressBuffer or RtlGetCompressionWorkSpaceSize is 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 %d\n", compress_workspace); + + workspace = HeapAlloc( GetProcessHeap(), 0, compress_workspace ); + ok(workspace != NULL, "HeapAlloc failed %x\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 %d\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 %d\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 %d\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); + ok((*(WORD *)buf1 & 0x7000) == 0x3000, "no chunk signature found %04x\n", *(WORD *)buf1); + buf_size = final_size; + todo_wine + ok(final_size < sizeof(test_buffer), "got wrong final_size %d\n", final_size); + + /* test decompression */ + 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 %d\n", final_size); + ok(!memcmp(buf2, test_buffer, sizeof(test_buffer)), "got wrong decoded data\n"); + ok(buf2[sizeof(test_buffer)] == 0x11, "buf[%u] overwritten\n", (DWORD)sizeof(test_buffer)); + + /* 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); + + HeapFree(GetProcessHeap(), 0, workspace); +} + START_TEST(rtl) { InitFunctionPtrs(); +#ifdef __i386__ + test_WinSqm(); +#else + skip("stdcall-style parameter checks are not supported on this platform.\n"); +#endif + test_RtlCompareMemory(); test_RtlCompareMemoryUlong(); test_RtlMoveMemory(); @@ -1623,6 +2500,10 @@ START_TEST(rtl) test_RtlIpv4AddressToString(); test_RtlIpv4AddressToStringEx(); test_RtlIpv4StringToAddress(); + test_RtlIpv4StringToAddressEx(); test_LdrAddRefDll(); test_LdrLockLoaderLock(); + test_RtlGetCompressionWorkSpaceSize(); + test_RtlDecompressBuffer(); + test_RtlCompressBuffer(); } diff --git a/rostests/winetests/ntdll/string.c b/rostests/winetests/ntdll/string.c index 56b75849747..adaa7c3ab3b 100755 --- a/rostests/winetests/ntdll/string.c +++ b/rostests/winetests/ntdll/string.c @@ -1083,8 +1083,8 @@ static void test_atoi64(void) result = p_atoi64(str2longlong[test_num].str); if (str2longlong[test_num].overflow) ok(result == str2longlong[test_num].value || - (result == (str2longlong[test_num].overflow == -1) ? - ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff)), + (result == ((str2longlong[test_num].overflow == -1) ? + ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff))), "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n", test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result, (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value); @@ -1108,8 +1108,8 @@ static void test_wtoi64(void) result = p_wtoi64(uni.Buffer); if (str2longlong[test_num].overflow) ok(result == str2longlong[test_num].value || - (result == (str2longlong[test_num].overflow == -1) ? - ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff)), + (result == ((str2longlong[test_num].overflow == -1) ? + ULL(0x80000000,0x00000000) : ULL(0x7fffffff,0xffffffff))), "(test %d): call failed: _atoi64(\"%s\") has result 0x%x%08x, expected: 0x%x%08x\n", test_num, str2longlong[test_num].str, (DWORD)(result >> 32), (DWORD)result, (DWORD)(str2longlong[test_num].value >> 32), (DWORD)str2longlong[test_num].value);