1 /* Unit test suite for Ntdll NamedPipe API functions
3 * Copyright 2011 Bernhard Loos
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define WIN32_NO_STATUS
30 #include "wine/test.h"
34 #ifndef __WINE_WINTERNL_H
38 ULONG NamedPipeConfiguration;
39 ULONG MaximumInstances;
40 ULONG CurrentInstances;
42 ULONG ReadDataAvailable;
44 ULONG WriteQuotaAvailable;
47 } FILE_PIPE_LOCAL_INFORMATION;
49 #ifndef FILE_SYNCHRONOUS_IO_ALERT
50 #define FILE_SYNCHRONOUS_IO_ALERT 0x10
53 #ifndef FILE_SYNCHRONOUS_IO_NONALERT
54 #define FILE_SYNCHRONOUS_IO_NONALERT 0x20
57 #ifndef FSCTL_PIPE_LISTEN
58 #define FSCTL_PIPE_LISTEN CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
62 static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size);
63 static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access,
64 POBJECT_ATTRIBUTES attr, PIO_STATUS_BLOCK iosb,
65 ULONG sharing, ULONG dispo, ULONG options,
66 ULONG pipe_type, ULONG read_mode,
67 ULONG completion_mode, ULONG max_inst,
68 ULONG inbound_quota, ULONG outbound_quota,
69 PLARGE_INTEGER timeout);
70 static NTSTATUS (WINAPI *pNtQueryInformationFile) (IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass);
71 static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status);
72 static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source);
74 static HANDLE (WINAPI *pOpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId);
75 static DWORD (WINAPI *pQueueUserAPC)(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData);
78 static BOOL init_func_ptrs(void)
80 HMODULE module = GetModuleHandle("ntdll.dll");
82 #define loadfunc(name) if (!(p##name = (void *)GetProcAddress(module, #name))) { \
83 trace("GetProcAddress(%s) failed\n", #name); \
87 loadfunc(NtFsControlFile)
88 loadfunc(NtCreateNamedPipeFile)
89 loadfunc(NtQueryInformationFile)
90 loadfunc(NtCancelIoFile)
91 loadfunc(RtlInitUnicodeString)
94 module = GetModuleHandle("kernel32.dll");
95 pOpenThread = (void *)GetProcAddress(module, "OpenThread");
96 pQueueUserAPC = (void *)GetProcAddress(module, "QueueUserAPC");
100 static const WCHAR testpipe[] = { '\\', '\\', '.', '\\', 'p', 'i', 'p', 'e', '\\',
101 't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 };
102 static const WCHAR testpipe_nt[] = { '\\', '?', '?', '\\', 'p', 'i', 'p', 'e', '\\',
103 't', 'e', 's', 't', 'p', 'i', 'p', 'e', 0 };
105 static NTSTATUS create_pipe(PHANDLE handle, ULONG sharing, ULONG options)
107 IO_STATUS_BLOCK iosb;
108 OBJECT_ATTRIBUTES attr;
110 LARGE_INTEGER timeout;
113 pRtlInitUnicodeString(&name, testpipe_nt);
115 attr.Length = sizeof(attr);
116 attr.RootDirectory = 0;
117 attr.ObjectName = &name;
118 attr.Attributes = 0x40; /*case insensitive */
119 attr.SecurityDescriptor = NULL;
120 attr.SecurityQualityOfService = NULL;
122 timeout.QuadPart = -100000000000ll;
124 res = pNtCreateNamedPipeFile(handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, sharing, 2 /*FILE_CREATE*/,
125 options, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
129 static void test_create_invalid(void)
131 IO_STATUS_BLOCK iosb;
132 OBJECT_ATTRIBUTES attr;
134 LARGE_INTEGER timeout;
136 HANDLE handle, handle2;
137 FILE_PIPE_LOCAL_INFORMATION info;
139 pRtlInitUnicodeString(&name, testpipe_nt);
141 attr.Length = sizeof(attr);
142 attr.RootDirectory = 0;
143 attr.ObjectName = &name;
144 attr.Attributes = 0x40; /*case insensitive */
145 attr.SecurityDescriptor = NULL;
146 attr.SecurityQualityOfService = NULL;
148 timeout.QuadPart = -100000000000ll;
150 /* create a pipe with FILE_OVERWRITE */
151 res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ, 4 /*FILE_OVERWRITE*/,
152 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
153 todo_wine ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res);
157 /* create a pipe with FILE_OVERWRITE_IF */
158 res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ, 5 /*FILE_OVERWRITE_IF*/,
159 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
160 todo_wine ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res);
164 /* create a pipe with sharing = 0 */
165 res = pNtCreateNamedPipeFile(&handle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &attr, &iosb, 0, 2 /*FILE_CREATE*/,
166 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
167 ok(res == STATUS_INVALID_PARAMETER, "NtCreateNamedPipeFile returned %x\n", res);
171 /* create a pipe without r/w access */
172 res = pNtCreateNamedPipeFile(&handle, SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /*FILE_CREATE*/,
173 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
174 ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
176 res = pNtQueryInformationFile(handle, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24);
177 ok(res == STATUS_ACCESS_DENIED, "NtQueryInformationFile returned %x\n", res);
179 /* test FILE_CREATE creation disposition */
180 res = pNtCreateNamedPipeFile(&handle2, SYNCHRONIZE, &attr, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE, 2 /*FILE_CREATE*/,
181 0, 1, 0, 0, 0xFFFFFFFF, 500, 500, &timeout);
182 todo_wine ok(res == STATUS_ACCESS_DENIED, "NtCreateNamedPipeFile returned %x\n", res);
184 CloseHandle(handle2);
189 static void test_create(void)
194 FILE_PIPE_LOCAL_INFORMATION info;
195 IO_STATUS_BLOCK iosb;
197 static const DWORD access[] = { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE};
198 static const DWORD sharing[] = { FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE };
199 static const DWORD pipe_config[]= { 1, 0, 2 };
201 for (j = 0; j < sizeof(sharing) / sizeof(DWORD); j++) {
202 for (k = 0; k < sizeof(access) / sizeof(DWORD); k++) {
204 BOOL should_succeed = TRUE;
206 res = create_pipe(&hserver, sharing[j], FILE_SYNCHRONOUS_IO_NONALERT);
208 ok(0, "NtCreateNamedPipeFile returned %x, sharing: %x\n", res, sharing[j]);
212 res = pNtQueryInformationFile(hserver, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24);
213 ok(!res, "NtQueryInformationFile for server returned %x, sharing: %x\n", res, sharing[j]);
214 ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n",
215 info.NamedPipeConfiguration, pipe_config[j]);
217 hclient = CreateFileW(testpipe, access[k], 0, 0, OPEN_EXISTING, 0, 0);
218 if (hclient != INVALID_HANDLE_VALUE) {
219 res = pNtQueryInformationFile(hclient, &iosb, &info, sizeof(info), (FILE_INFORMATION_CLASS)24);
220 ok(!res, "NtQueryInformationFile for client returned %x, access: %x, sharing: %x\n",
221 res, access[k], sharing[j]);
222 ok(info.NamedPipeConfiguration == pipe_config[j], "wrong duplex status for pipe: %d, expected %d\n",
223 info.NamedPipeConfiguration, pipe_config[j]);
224 CloseHandle(hclient);
227 if (access[k] & GENERIC_WRITE)
228 should_succeed &= !!(sharing[j] & FILE_SHARE_WRITE);
229 if (access[k] & GENERIC_READ)
230 should_succeed &= !!(sharing[j] & FILE_SHARE_READ);
233 ok(hclient != INVALID_HANDLE_VALUE, "CreateFile failed for sharing %x, access: %x, GetLastError: %d\n",
234 sharing[j], access[k], GetLastError());
236 ok(hclient == INVALID_HANDLE_VALUE, "CreateFile succeeded for sharing %x, access: %x\n", sharing[j], access[k]);
238 CloseHandle(hserver);
243 static BOOL ioapc_called;
244 static void CALLBACK ioapc(void *arg, PIO_STATUS_BLOCK io, ULONG reserved)
249 static NTSTATUS listen_pipe(HANDLE hPipe, HANDLE hEvent, PIO_STATUS_BLOCK iosb, BOOL use_apc)
253 ioapc_called = FALSE;
255 return pNtFsControlFile(hPipe, hEvent, use_apc ? &ioapc: NULL, use_apc ? &dummy: NULL, iosb, FSCTL_PIPE_LISTEN, 0, 0, 0, 0);
258 static void test_overlapped(void)
260 IO_STATUS_BLOCK iosb;
266 hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
267 ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError());
269 res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */);
270 ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
272 memset(&iosb, 0x55, sizeof(iosb));
274 /* try with event and apc */
275 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
276 ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res);
278 hClient = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
279 ok(hClient != INVALID_HANDLE_VALUE, "can't open pipe, GetLastError: %x\n", GetLastError());
281 ok(U(iosb).Status == 0, "Wrong iostatus %x\n", U(iosb).Status);
282 ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n");
284 ok(!ioapc_called, "IOAPC ran too early\n");
286 SleepEx(0, TRUE); /* alertable wait state */
288 ok(ioapc_called, "IOAPC didn't run\n");
292 CloseHandle(hClient);
295 static BOOL userapc_called;
296 static void CALLBACK userapc(ULONG_PTR dwParam)
298 userapc_called = TRUE;
301 static BOOL open_succeeded;
302 static DWORD WINAPI thread(PVOID main_thread)
310 userapc_called = FALSE;
311 ret = pQueueUserAPC(&userapc, main_thread, 0);
312 ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError());
313 CloseHandle(main_thread);
318 h = CreateFileW(testpipe, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
320 if (h != INVALID_HANDLE_VALUE) {
321 open_succeeded = TRUE;
325 open_succeeded = FALSE;
330 static void test_alertable(void)
332 IO_STATUS_BLOCK iosb;
339 memset(&iosb, 0x55, sizeof(iosb));
341 hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
342 ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError());
344 res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
345 ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
347 /* queue an user apc before calling listen */
348 userapc_called = FALSE;
349 ret = pQueueUserAPC(&userapc, GetCurrentThread(), 0);
350 ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError());
352 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
353 todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %x\n", res);
355 todo_wine ok(userapc_called, "user apc didn't run\n");
356 ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status);
357 todo_wine ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_TIMEOUT, "hEvent signaled\n");
358 ok(!ioapc_called, "IOAPC ran\n");
360 /* queue an user apc from a different thread */
361 hThread = CreateThread(NULL, 0, &thread, pOpenThread(MAXIMUM_ALLOWED, FALSE, GetCurrentThreadId()), 0, 0);
362 ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError());
364 /* wine_todo: the earlier NtFsControlFile call gets cancelled after the pipe gets set into listen state
365 instead of before, so this NtFsControlFile will fail STATUS_INVALID_HANDLE */
366 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
367 todo_wine ok(res == STATUS_CANCELLED, "NtFsControlFile returned %x\n", res);
369 ok(userapc_called, "user apc didn't run\n");
370 todo_wine ok(U(iosb).Status == 0x55555555, "iosb.Status got changed to %x\n", U(iosb).Status);
371 ok(WaitForSingleObjectEx(hEvent, 0, TRUE) == WAIT_TIMEOUT, "hEvent signaled\n");
372 ok(!ioapc_called, "IOAPC ran\n");
374 WaitForSingleObject(hThread, INFINITE);
376 SleepEx(0, TRUE); /* get rid of the userapc, if NtFsControlFile failed */
378 ok(open_succeeded, "couldn't open client side pipe\n");
380 CloseHandle(hThread);
381 DisconnectNamedPipe(hPipe);
383 /* finally try without an apc */
384 hThread = CreateThread(NULL, 0, &thread, 0, 0, 0);
385 ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError());
387 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
388 todo_wine ok(!res, "NtFsControlFile returned %x\n", res);
390 ok(open_succeeded, "couldn't open client side pipe\n");
391 ok(!U(iosb).Status, "Wrong iostatus %x\n", U(iosb).Status);
392 todo_wine ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n");
394 WaitForSingleObject(hThread, INFINITE);
395 CloseHandle(hThread);
400 static void test_nonalertable(void)
402 IO_STATUS_BLOCK iosb;
409 memset(&iosb, 0x55, sizeof(iosb));
411 hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
412 ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError());
414 res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT);
415 ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
417 hThread = CreateThread(NULL, 0, &thread, 0, 0, 0);
418 ok(hThread != INVALID_HANDLE_VALUE, "can't create thread, GetLastError: %x\n", GetLastError());
420 userapc_called = FALSE;
421 ret = pQueueUserAPC(&userapc, GetCurrentThread(), 0);
422 ok(ret, "can't queue user apc, GetLastError: %x\n", GetLastError());
424 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
425 todo_wine ok(!res, "NtFsControlFile returned %x\n", res);
427 ok(open_succeeded, "couldn't open client side pipe\n");
428 todo_wine ok(!U(iosb).Status, "Wrong iostatus %x\n", U(iosb).Status);
429 todo_wine ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n");
431 ok(!ioapc_called, "IOAPC ran too early\n");
432 ok(!userapc_called, "user apc ran too early\n");
434 SleepEx(0, TRUE); /* alertable wait state */
436 ok(ioapc_called, "IOAPC didn't run\n");
437 ok(userapc_called, "user apc didn't run\n");
439 WaitForSingleObject(hThread, INFINITE);
440 CloseHandle(hThread);
445 static void test_cancelio(void)
447 IO_STATUS_BLOCK iosb;
448 IO_STATUS_BLOCK cancel_sb;
453 hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
454 ok(hEvent != INVALID_HANDLE_VALUE, "can't create event, GetLastError: %x\n", GetLastError());
456 res = create_pipe(&hPipe, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */);
457 ok(!res, "NtCreateNamedPipeFile returned %x\n", res);
459 memset(&iosb, 0x55, sizeof(iosb));
461 res = listen_pipe(hPipe, hEvent, &iosb, TRUE);
462 ok(res == STATUS_PENDING, "NtFsControlFile returned %x\n", res);
464 res = pNtCancelIoFile(hPipe, &cancel_sb);
465 todo_wine ok(!res, "NtCancelIoFile returned %x\n", res);
468 ok(U(iosb).Status == STATUS_CANCELLED, "Wrong iostatus %x\n", U(iosb).Status);
469 ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n");
472 ok(!ioapc_called, "IOAPC ran too early\n");
474 SleepEx(0, TRUE); /* alertable wait state */
476 ok(ioapc_called, "IOAPC didn't run\n");
484 if (!init_func_ptrs())
487 trace("starting invalid create tests\n");
488 test_create_invalid();
490 trace("starting create tests\n");
493 trace("starting overlapped tests\n");
496 if (!pOpenThread || !pQueueUserAPC)
499 trace("starting alertable tests\n");
502 trace("starting nonalertable tests\n");
505 trace("starting cancelio tests\n");