[NTDLL_WINETEST] Sync with Wine Staging 1.7.37. CORE-9246
authorAmine Khaldi <amine.khaldi@reactos.org>
Tue, 10 Mar 2015 10:10:04 +0000 (10:10 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Tue, 10 Mar 2015 10:10:04 +0000 (10:10 +0000)
svn path=/trunk/; revision=66658

rostests/winetests/ntdll/directory.c
rostests/winetests/ntdll/exception.c
rostests/winetests/ntdll/file.c
rostests/winetests/ntdll/info.c
rostests/winetests/ntdll/om.c
rostests/winetests/ntdll/reg.c
rostests/winetests/ntdll/rtl.c
rostests/winetests/ntdll/string.c

index f190ff4..68b5406 100644 (file)
@@ -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++;
index 04cea9c..1092393 100644 (file)
@@ -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)
index 0d52609..5ba1ed0 100644 (file)
 #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();
 }
index af00401..5fff490 100755 (executable)
@@ -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();
index ca84399..2cf59cf 100644 (file)
@@ -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',
index 1594665..b325434 100755 (executable)
@@ -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();
index e8eb04a..c335a7b 100755 (executable)
@@ -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();
 }
index 56b7584..adaa7c3 100755 (executable)
@@ -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);