[NTDLL_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Sep 2014 20:08:51 +0000 (20:08 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Sep 2014 20:08:51 +0000 (20:08 +0000)
* Sync with Wine 1.7.27.
CORE-8540

svn path=/trunk/; revision=64209

rostests/winetests/ntdll/exception.c
rostests/winetests/ntdll/file.c
rostests/winetests/ntdll/pipe.c
rostests/winetests/ntdll/rtl.c

index 1e45d82..04cea9c 100644 (file)
@@ -43,6 +43,7 @@ static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
 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 NTSTATUS  (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
@@ -390,6 +391,83 @@ static void test_rtlraiseexception(void)
     run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
 }
 
+static DWORD unwind_expected_eax;
+
+static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+                             CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
+{
+    trace("exception: %08x flags:%x addr:%p context: Eip:%x\n",
+          rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip);
+
+    ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08x instead of %08x\n",
+       rec->ExceptionCode, STATUS_UNWIND);
+    ok(rec->ExceptionAddress == (char *)code_mem + 0x22, "ExceptionAddress at %p instead of %p\n",
+       rec->ExceptionAddress, (char *)code_mem + 0x22);
+    ok(context->Eax == unwind_expected_eax, "context->Eax is %08x instead of %08x\n",
+       context->Eax, unwind_expected_eax);
+
+    context->Eax += 1;
+    return ExceptionContinueSearch;
+}
+
+static const BYTE call_unwind_code[] = {
+    0x55,                           /* push %ebp */
+    0x53,                           /* push %ebx */
+    0x56,                           /* push %esi */
+    0x57,                           /* push %edi */
+    0xe8, 0x00, 0x00, 0x00, 0x00,   /* call 0 */
+    0x58,                           /* 0: pop %eax */
+    0x05, 0x1e, 0x00, 0x00, 0x00,   /* add $0x1e,%eax */
+    0xff, 0x74, 0x24, 0x20,         /* push 0x20(%esp) */
+    0xff, 0x74, 0x24, 0x20,         /* push 0x20(%esp) */
+    0x50,                           /* push %eax */
+    0xff, 0x74, 0x24, 0x24,         /* push 0x24(%esp) */
+    0x8B, 0x44, 0x24, 0x24,         /* mov 0x24(%esp),%eax */
+    0xff, 0xd0,                     /* call *%eax */
+    0x5f,                           /* pop %edi */
+    0x5e,                           /* pop %esi */
+    0x5b,                           /* pop %ebx */
+    0x5d,                           /* pop %ebp */
+    0xc3,                           /* ret */
+    0xcc,                           /* int $3 */
+};
+
+static void test_unwind(void)
+{
+    EXCEPTION_REGISTRATION_RECORD frames[2], *frame2 = &frames[0], *frame1 = &frames[1];
+    DWORD (*func)(void* function, EXCEPTION_REGISTRATION_RECORD *pEndFrame, EXCEPTION_RECORD* record, DWORD retval) = code_mem;
+    DWORD retval;
+
+    memcpy(code_mem, call_unwind_code, sizeof(call_unwind_code));
+
+    /* add first unwind handler */
+    frame1->Handler = unwind_handler;
+    frame1->Prev = pNtCurrentTeb()->Tib.ExceptionList;
+    pNtCurrentTeb()->Tib.ExceptionList = frame1;
+
+    /* add second unwind handler */
+    frame2->Handler = unwind_handler;
+    frame2->Prev = pNtCurrentTeb()->Tib.ExceptionList;
+    pNtCurrentTeb()->Tib.ExceptionList = frame2;
+
+    /* test unwind to current frame */
+    unwind_expected_eax = 0xDEAD0000;
+    retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
+    ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000);
+    ok(pNtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
+       pNtCurrentTeb()->Tib.ExceptionList, frame2);
+
+    /* unwind to frame1 */
+    unwind_expected_eax = 0xDEAD0000;
+    retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
+    ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001);
+    ok(pNtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
+       pNtCurrentTeb()->Tib.ExceptionList, frame1);
+
+    /* restore original handler */
+    pNtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
+}
+
 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
 {
@@ -865,6 +943,51 @@ static void test_debugger(void)
                 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
             }
         }
+        else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
+        {
+            int stage;
+            char buffer[64];
+
+            status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
+                                          sizeof(stage), &size_read);
+            ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
+
+            ok(!de.u.DebugString.fUnicode, "unepxected unicode debug string event\n");
+            ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
+               de.u.DebugString.nDebugStringLength);
+
+            memset(buffer, 0, sizeof(buffer));
+            status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
+                                          de.u.DebugString.nDebugStringLength, &size_read);
+            ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
+
+            if (stage == 3 || stage == 4)
+                ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
+            else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
+                ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
+
+            if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
+        }
+        else if (de.dwDebugEventCode == RIP_EVENT)
+        {
+            int stage;
+
+            status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
+                                          sizeof(stage), &size_read);
+            ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
+
+            if (stage == 5 || stage == 6)
+            {
+                ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
+                   de.u.RipInfo.dwError, 0x11223344);
+                ok(de.u.RipInfo.dwType  == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
+                   de.u.RipInfo.dwType, 0x55667788);
+            }
+            else
+                ok(FALSE, "unexpected stage %x\n", stage);
+
+            if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
+        }
 
         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
 
@@ -1487,8 +1610,8 @@ static void test_dynamic_unwind(void)
     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
     ok( func == NULL,
         "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
-    ok( base == 0xdeadbeef,
-        "RtlLookupFunctionEntry modified base address, expected: 0xdeadbeef, got: %lx\n", base );
+    ok( !base || broken(base == 0xdeadbeef),
+        "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
 
     /* Test with pointer inside of our function */
     base = 0xdeadbeef;
@@ -1546,8 +1669,8 @@ static void test_dynamic_unwind(void)
     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
     ok( func == NULL,
         "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
-    ok( base == 0xdeadbeef,
-        "RtlLookupFunctionEntry modified base address, expected: 0xdeadbeef, got: %lx\n", base );
+    ok( !base || broken(base == 0xdeadbeef),
+        "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
     ok( !count,
         "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
 
@@ -1572,6 +1695,99 @@ static void test_dynamic_unwind(void)
 
 #endif  /* __x86_64__ */
 
+static DWORD outputdebugstring_exceptions;
+
+static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
+{
+    PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
+    trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
+
+    ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n",
+        rec->ExceptionCode, DBG_PRINTEXCEPTION_C);
+    ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
+    ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
+    ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
+        "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
+
+    outputdebugstring_exceptions++;
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+static void test_outputdebugstring(DWORD numexc, BOOL todo)
+{
+    PVOID vectored_handler;
+
+    if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
+    {
+        skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
+        return;
+    }
+
+    vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
+    ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
+
+    outputdebugstring_exceptions = 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);
+
+    pRtlRemoveVectoredExceptionHandler(vectored_handler);
+}
+
+static DWORD ripevent_exceptions;
+
+static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
+{
+    PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
+    trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
+
+    ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n",
+       rec->ExceptionCode, DBG_RIPEXCEPTION);
+    ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
+    ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n",
+       (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
+    ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n",
+       (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
+
+    ripevent_exceptions++;
+    return (rec->ExceptionCode == DBG_RIPEXCEPTION) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
+}
+
+static void test_ripevent(DWORD numexc)
+{
+    EXCEPTION_RECORD record;
+    PVOID vectored_handler;
+
+    if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
+    {
+        skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
+        return;
+    }
+
+    vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
+    ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
+
+    record.ExceptionCode = DBG_RIPEXCEPTION;
+    record.ExceptionFlags = 0;
+    record.ExceptionRecord = NULL;
+    record.ExceptionAddress = NULL;
+    record.NumberParameters = 2;
+    record.ExceptionInformation[0] = 0x11223344;
+    record.ExceptionInformation[1] = 0x55667788;
+
+    ripevent_exceptions = 0;
+    pRtlRaiseException(&record);
+    ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n",
+       ripevent_exceptions, numexc);
+
+    pRtlRemoveVectoredExceptionHandler(vectored_handler);
+}
+
 START_TEST(exception)
 {
     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -1586,6 +1802,7 @@ START_TEST(exception)
     pNtGetContextThread  = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
     pNtSetContextThread  = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
     pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
+    pRtlUnwind           = (void *)GetProcAddress( hntdll, "RtlUnwind" );
     pRtlRaiseException   = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
     pNtTerminateProcess  = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
     pRtlAddVectoredExceptionHandler    = (void *)GetProcAddress( hntdll,
@@ -1640,6 +1857,14 @@ START_TEST(exception)
             run_rtlraiseexception_test(0x12345);
             run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
             run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
+            test_stage = 3;
+            test_outputdebugstring(0, FALSE);
+            test_stage = 4;
+            test_outputdebugstring(2, TRUE); /* is this a Windows bug? */
+            test_stage = 5;
+            test_ripevent(0);
+            test_stage = 6;
+            test_ripevent(1);
         }
         else
             skip( "RtlRaiseException not found\n" );
@@ -1648,13 +1873,16 @@ START_TEST(exception)
         return;
     }
 
-    test_prot_fault();
+    test_unwind();
     test_exceptions();
     test_rtlraiseexception();
+    test_outputdebugstring(1, FALSE);
+    test_ripevent(1);
     test_debugger();
     test_simd_exceptions();
     test_fpu_exceptions();
     test_dpe_exceptions();
+    test_prot_fault();
 
 #elif defined(__x86_64__)
     pRtlAddFunctionTable               = (void *)GetProcAddress( hntdll,
@@ -1666,6 +1894,8 @@ START_TEST(exception)
     pRtlLookupFunctionEntry            = (void *)GetProcAddress( hntdll,
                                                                  "RtlLookupFunctionEntry" );
 
+    test_outputdebugstring(1, FALSE);
+    test_ripevent(1);
     test_virtual_unwind();
 
     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
index de64a85..0d52609 100644 (file)
@@ -267,6 +267,7 @@ static void create_file_test(void)
                            FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
     ok(status == STATUS_OBJECT_NAME_INVALID,
        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status);
+    pRtlFreeUnicodeString(&nameW);
 
     pRtlDosPathNameToNtPathName_U(pipeInvalidNameW, &nameW, NULL, NULL);
     attr.ObjectName = &nameW;
@@ -281,6 +282,7 @@ static void create_file_test(void)
                            FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
     ok(status == STATUS_OBJECT_NAME_INVALID,
        "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status);
+    pRtlFreeUnicodeString(&nameW);
 }
 
 static void open_file_test(void)
@@ -1966,6 +1968,7 @@ static void test_NtCreateFile(void)
         }
     }
 
+    pRtlFreeUnicodeString( &nameW );
     SetFileAttributesW(path, FILE_ATTRIBUTE_ARCHIVE);
     DeleteFileW( path );
 }
index c275e3d..4bf2ad2 100644 (file)
 
 #ifndef __WINE_WINTERNL_H
 
+typedef struct {
+  ULONG ReadMode;
+  ULONG CompletionMode;
+} FILE_PIPE_INFORMATION;
+
 typedef struct {
   ULONG NamedPipeType;
   ULONG NamedPipeConfiguration;
@@ -68,6 +73,7 @@ static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access,
                                         ULONG inbound_quota, ULONG outbound_quota,
                                         PLARGE_INTEGER timeout);
 static NTSTATUS (WINAPI *pNtQueryInformationFile) (IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
+static NTSTATUS (WINAPI *pNtSetInformationFile) (HANDLE handle, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class);
 static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status);
 static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source);
 
@@ -87,6 +93,7 @@ static BOOL init_func_ptrs(void)
     loadfunc(NtFsControlFile)
     loadfunc(NtCreateNamedPipeFile)
     loadfunc(NtQueryInformationFile)
+    loadfunc(NtSetInformationFile)
     loadfunc(NtCancelIoFile)
     loadfunc(RtlInitUnicodeString)
 
@@ -479,6 +486,190 @@ static void test_cancelio(void)
     CloseHandle(hPipe);
 }
 
+static void _check_pipe_handle_state(int line, HANDLE handle, ULONG read, ULONG completion)
+{
+    IO_STATUS_BLOCK iosb;
+    FILE_PIPE_INFORMATION fpi;
+    NTSTATUS res;
+    if (handle != INVALID_HANDLE_VALUE)
+    {
+        memset(&fpi, 0x55, sizeof(fpi));
+        res = pNtQueryInformationFile(handle, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok_(__FILE__, line)(!res, "NtQueryInformationFile returned %x\n", res);
+        ok_(__FILE__, line)(fpi.ReadMode == read, "Unexpected ReadMode, expected %x, got %x\n",
+                            read, fpi.ReadMode);
+        ok_(__FILE__, line)(fpi.CompletionMode == completion, "Unexpected CompletionMode, expected %x, got %x\n",
+                            completion, fpi.CompletionMode);
+    }
+}
+#define check_pipe_handle_state(handle, r, c) _check_pipe_handle_state(__LINE__, handle, r, c)
+
+static void test_filepipeinfo(void)
+{
+    IO_STATUS_BLOCK iosb;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING name;
+    LARGE_INTEGER timeout;
+    HANDLE hServer, hClient;
+    FILE_PIPE_INFORMATION fpi;
+    NTSTATUS res;
+
+    pRtlInitUnicodeString(&name, testpipe_nt);
+
+    attr.Length                   = sizeof(attr);
+    attr.RootDirectory            = 0;
+    attr.ObjectName               = &name;
+    attr.Attributes               = 0x40; /* case insensitive */
+    attr.SecurityDescriptor       = NULL;
+    attr.SecurityQualityOfService = NULL;
+
+    timeout.QuadPart = -100000000000ll;
+
+    /* test with INVALID_HANDLE_VALUE */
+    res = pNtQueryInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtQueryInformationFile returned %x\n", res);
+
+    fpi.ReadMode = 0;
+    fpi.CompletionMode = 0;
+    res = pNtSetInformationFile(INVALID_HANDLE_VALUE, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(res == STATUS_OBJECT_TYPE_MISMATCH, "NtSetInformationFile returned %x\n", res);
+
+    /* server end with read-only attributes */
+    res = pNtCreateNamedPipeFile(&hServer, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE,  2 /* FILE_CREATE */,
+                                 0, 0, 0, 1, 0xFFFFFFFF, 500, 500, &timeout);
+    ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 0, 1);
+
+    hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
+    ok(hClient != INVALID_HANDLE_VALUE, "can't open pipe, GetLastError: %x\n", GetLastError());
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 0, 0);
+
+    fpi.ReadMode = 0;
+    fpi.CompletionMode = 0;
+    res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 0, 0);
+
+    fpi.ReadMode = 1; /* invalid on a byte stream pipe */
+    fpi.CompletionMode = 1;
+    res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 0, 0);
+
+    if (hClient != INVALID_HANDLE_VALUE)
+    {
+        fpi.ReadMode = 1; /* invalid on a byte stream pipe */
+        fpi.CompletionMode = 1;
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(res == STATUS_INVALID_PARAMETER, "NtSetInformationFile returned %x\n", res);
+    }
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 0, 0);
+
+    if (hClient != INVALID_HANDLE_VALUE)
+    {
+        fpi.ReadMode = 0;
+        fpi.CompletionMode = 1;
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(!res, "NtSetInformationFile returned %x\n", res);
+    }
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 0, 1);
+
+    if (hClient != INVALID_HANDLE_VALUE)
+    {
+        fpi.ReadMode = 0;
+        fpi.CompletionMode = 2; /* not in range 0-1 */
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res);
+
+        fpi.ReadMode = 2; /* not in range 0-1 */
+        fpi.CompletionMode = 0;
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res);
+    }
+
+    CloseHandle(hClient);
+
+    check_pipe_handle_state(hServer, 0, 1);
+
+    fpi.ReadMode = 0;
+    fpi.CompletionMode = 0;
+    res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(res == STATUS_ACCESS_DENIED, "NtSetInformationFile returned %x\n", res);
+
+    CloseHandle(hServer);
+
+    /* message mode server with read/write attributes */
+    res = pNtCreateNamedPipeFile(&hServer, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE,  2 /* FILE_CREATE */,
+                                 0, 1, 1, 0, 0xFFFFFFFF, 500, 500, &timeout);
+    ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 1, 0);
+
+    hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
+    ok(hClient != INVALID_HANDLE_VALUE, "can't open pipe, GetLastError: %x\n", GetLastError());
+
+    check_pipe_handle_state(hServer, 1, 0);
+    check_pipe_handle_state(hClient, 0, 0);
+
+    if (hClient != INVALID_HANDLE_VALUE)
+    {
+        fpi.ReadMode = 1;
+        fpi.CompletionMode = 1;
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(!res, "NtSetInformationFile returned %x\n", res);
+    }
+
+    check_pipe_handle_state(hServer, 1, 0);
+    check_pipe_handle_state(hClient, 1, 1);
+
+    fpi.ReadMode = 0;
+    fpi.CompletionMode = 1;
+    res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(!res, "NtSetInformationFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 0, 1);
+    check_pipe_handle_state(hClient, 1, 1);
+
+    if (hClient != INVALID_HANDLE_VALUE)
+    {
+        fpi.ReadMode = 0;
+        fpi.CompletionMode = 2; /* not in range 0-1 */
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res);
+
+        fpi.ReadMode = 2; /* not in range 0-1 */
+        fpi.CompletionMode = 0;
+        res = pNtSetInformationFile(hClient, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+        ok(res == STATUS_INVALID_PARAMETER || broken(!res) /* < Vista */, "NtSetInformationFile returned %x\n", res);
+    }
+
+    CloseHandle(hClient);
+
+    check_pipe_handle_state(hServer, 0, 1);
+
+    fpi.ReadMode = 1;
+    fpi.CompletionMode = 0;
+    res = pNtSetInformationFile(hServer, &iosb, &fpi, sizeof(fpi), (FILE_INFORMATION_CLASS)23);
+    ok(!res, "NtSetInformationFile returned %x\n", res);
+
+    check_pipe_handle_state(hServer, 1, 0);
+
+    CloseHandle(hServer);
+}
+
 START_TEST(pipe)
 {
     if (!init_func_ptrs())
@@ -493,6 +684,9 @@ START_TEST(pipe)
     trace("starting overlapped tests\n");
     test_overlapped();
 
+    trace("starting FILE_PIPE_INFORMATION tests\n");
+    test_filepipeinfo();
+
     if (!pOpenThread || !pQueueUserAPC)
         return;
 
index 917bc29..04254ce 100755 (executable)
@@ -90,6 +90,8 @@ 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 *pLdrAddRefDll)(ULONG, HMODULE);
+static NTSTATUS  (WINAPI *pLdrLockLoaderLock)(ULONG, ULONG*, ULONG_PTR*);
+static NTSTATUS  (WINAPI *pLdrUnlockLoaderLock)(ULONG, ULONG_PTR);
 
 static HMODULE hkernel32 = 0;
 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@@ -135,6 +137,8 @@ static void InitFunctionPtrs(void)
         pRtlIpv4AddressToStringExA = (void *)GetProcAddress(hntdll, "RtlIpv4AddressToStringExA");
         pRtlIpv4StringToAddressA = (void *)GetProcAddress(hntdll, "RtlIpv4StringToAddressA");
         pLdrAddRefDll = (void *)GetProcAddress(hntdll, "LdrAddRefDll");
+        pLdrLockLoaderLock = (void *)GetProcAddress(hntdll, "LdrLockLoaderLock");
+        pLdrUnlockLoaderLock = (void *)GetProcAddress(hntdll, "LdrUnlockLoaderLock");
     }
     hkernel32 = LoadLibraryA("kernel32.dll");
     ok(hkernel32 != 0, "LoadLibrary failed\n");
@@ -1551,6 +1555,58 @@ static void test_LdrAddRefDll(void)
     ok(mod2 != NULL, "got %p\n", mod2);
 }
 
+static void test_LdrLockLoaderLock(void)
+{
+    ULONG_PTR magic;
+    ULONG result;
+    NTSTATUS status;
+
+    if (!pLdrLockLoaderLock)
+    {
+        win_skip("LdrLockLoaderLock() is not available\n");
+        return;
+    }
+
+    /* invalid flags */
+    result = 10;
+    magic = 0xdeadbeef;
+    status = pLdrLockLoaderLock(0x10, &result, &magic);
+    ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status);
+    ok(result == 0, "got %d\n", result);
+    ok(magic == 0, "got %lx\n", magic);
+
+    magic = 0xdeadbeef;
+    status = pLdrLockLoaderLock(0x10, NULL, &magic);
+    ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status);
+    ok(magic == 0, "got %lx\n", magic);
+
+    result = 10;
+    status = pLdrLockLoaderLock(0x10, &result, NULL);
+    ok(status == STATUS_INVALID_PARAMETER_1, "got 0x%08x\n", status);
+    ok(result == 0, "got %d\n", result);
+
+    /* non-blocking mode, result is null */
+    magic = 0xdeadbeef;
+    status = pLdrLockLoaderLock(0x2, NULL, &magic);
+    ok(status == STATUS_INVALID_PARAMETER_2, "got 0x%08x\n", status);
+    ok(magic == 0, "got %lx\n", magic);
+
+    /* magic pointer is null */
+    result = 10;
+    status = pLdrLockLoaderLock(0, &result, NULL);
+    ok(status == STATUS_INVALID_PARAMETER_3, "got 0x%08x\n", status);
+    ok(result == 0, "got %d\n", result);
+
+    /* lock in non-blocking mode */
+    result = 0;
+    magic = 0;
+    status = pLdrLockLoaderLock(0x2, &result, &magic);
+    ok(status == STATUS_SUCCESS, "got 0x%08x\n", status);
+    ok(result == 1, "got %d\n", result);
+    ok(magic != 0, "got %lx\n", magic);
+    pLdrUnlockLoaderLock(0, magic);
+}
+
 START_TEST(rtl)
 {
     InitFunctionPtrs();
@@ -1576,4 +1632,5 @@ START_TEST(rtl)
     test_RtlIpv4AddressToStringEx();
     test_RtlIpv4StringToAddress();
     test_LdrAddRefDll();
+    test_LdrLockLoaderLock();
 }