From: Daniel Lehman dlehman25@gmail.com
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/ntdll/ntdll.spec | 2 + dlls/ntdll/tests/pipe.c | 154 +++++++++++++++++++++++++++++++++++++++ dlls/ntdll/unix/file.c | 9 +++ dlls/ntdll/unix/loader.c | 1 + dlls/wow64/file.c | 18 +++++ dlls/wow64/syscall.h | 1 + include/winternl.h | 1 + 7 files changed, 186 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 244f9acd2ab..2432307e686 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -153,6 +153,7 @@ # @ stub NtCancelDeviceWakeupRequest @ stdcall -syscall NtCancelIoFile(long ptr) @ stdcall -syscall NtCancelIoFileEx(long ptr ptr) +@ stdcall -syscall NtCancelSynchronousIoFile(long ptr ptr) @ stdcall -syscall NtCancelTimer(long ptr) @ stdcall -syscall NtClearEvent(long) @ stdcall -syscall NtClose(long) @@ -1186,6 +1187,7 @@ # @ stub ZwCancelDeviceWakeupRequest @ stdcall -private -syscall ZwCancelIoFile(long ptr) NtCancelIoFile @ stdcall -private -syscall ZwCancelIoFileEx(long ptr ptr) NtCancelIoFileEx +@ stdcall -private -syscall ZwCancelSynchronousIoFile(long ptr ptr) NtCancelSynchronousIoFile @ stdcall -private -syscall ZwCancelTimer(long ptr) NtCancelTimer @ stdcall -private -syscall ZwClearEvent(long) NtClearEvent @ stdcall -private -syscall ZwClose(long) NtClose diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 0ad09daaa82..2a044f6acea 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -75,6 +75,9 @@ typedef struct _FILE_PIPE_WAIT_FOR_BUFFER { #endif #endif
+static BOOL is_wow64; +static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *); + static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, ULONG code, PVOID in_buffer, ULONG in_size, PVOID out_buffer, ULONG out_size); static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *); static NTSTATUS (WINAPI *pNtCreateNamedPipeFile) (PHANDLE handle, ULONG access, @@ -90,6 +93,7 @@ static NTSTATUS (WINAPI *pNtQueryVolumeInformationFile)(HANDLE handle, PIO_STATU static NTSTATUS (WINAPI *pNtSetInformationFile) (HANDLE handle, PIO_STATUS_BLOCK io, PVOID ptr, ULONG len, FILE_INFORMATION_CLASS class); static NTSTATUS (WINAPI *pNtCancelIoFile) (HANDLE hFile, PIO_STATUS_BLOCK io_status); static NTSTATUS (WINAPI *pNtCancelIoFileEx) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status); +static NTSTATUS (WINAPI *pNtCancelSynchronousIoFile) (HANDLE hFile, IO_STATUS_BLOCK *iosb, IO_STATUS_BLOCK *io_status); static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER); static void (WINAPI *pRtlInitUnicodeString) (PUNICODE_STRING target, PCWSTR source);
@@ -114,6 +118,7 @@ static BOOL init_func_ptrs(void) loadfunc(NtQueryVolumeInformationFile) loadfunc(NtSetInformationFile) loadfunc(NtCancelIoFile) + loadfunc(NtCancelSynchronousIoFile) loadfunc(RtlInitUnicodeString) loadfunc(NtRemoveIoCompletion)
@@ -122,6 +127,7 @@ static BOOL init_func_ptrs(void) module = GetModuleHandleA("kernel32.dll"); pOpenThread = (void *)GetProcAddress(module, "OpenThread"); pQueueUserAPC = (void *)GetProcAddress(module, "QueueUserAPC"); + pIsWow64Process = (void *)GetProcAddress(module, "IsWow64Process"); return TRUE; }
@@ -615,6 +621,149 @@ static void test_cancelio(void) CloseHandle(hEvent); }
+struct synchronousio_thread_args +{ + HANDLE pipe; + IO_STATUS_BLOCK iosb; +}; + +static DWORD WINAPI synchronousio_thread(void *arg) +{ + struct synchronousio_thread_args *ctx = arg; + NTSTATUS res; + + res = listen_pipe(ctx->pipe, NULL, &ctx->iosb, FALSE); + ok(res == STATUS_CANCELLED, "NtFsControlFile returned %lx\n", res); + return 0; +} + +static void test_cancelsynchronousio(void) +{ + DWORD ret; + NTSTATUS res; + HANDLE event; + HANDLE thread; + HANDLE client; + IO_STATUS_BLOCK iosb; + struct synchronousio_thread_args ctx; + + /* bogus values */ + todo_wine { + res = pNtCancelSynchronousIoFile((HANDLE)0xdeadbeef, NULL, &iosb); + ok(res == STATUS_INVALID_HANDLE, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, NULL); + ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, (IO_STATUS_BLOCK*)0xdeadbeef); + ok(res == STATUS_ACCESS_VIOLATION, "NtCancelSynchronousIoFile returned %lx\n", res); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), (IO_STATUS_BLOCK*)0xdeadbeef, &iosb); + ok(res == STATUS_NOT_FOUND || broken(is_wow64 && res == STATUS_ACCESS_VIOLATION), + "NtCancelSynchronousIoFile returned %lx\n", res); + if (res != STATUS_ACCESS_VIOLATION) + { + ok(U(iosb).Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", U(iosb).Status); + ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information); + } + } + + /* synchronous i/o */ + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + + /* NULL io */ + U(ctx.iosb).Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + thread = CreateThread(NULL, 0, synchronousio_thread, &ctx, 0, 0); + /* wait for I/O to start, which transitions the pipe handle from signaled to nonsignaled state. */ + while ((ret = WaitForSingleObject(ctx.pipe, 0)) == WAIT_OBJECT_0) Sleep(1); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %lu (error %lu)\n", ret, GetLastError()); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(thread, NULL, &iosb); + todo_wine { + ok(res == STATUS_SUCCESS, "Failed to cancel I/O\n"); + ok(U(iosb).Status == STATUS_SUCCESS, "iosb.Status got changed to %lx\n", U(iosb).Status); + ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information); + } + if (res == STATUS_NOT_IMPLEMENTED) + pNtCancelIoFileEx(ctx.pipe, NULL, &iosb); + ret = WaitForSingleObject(thread, 1000); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(thread); + CloseHandle(ctx.pipe); + ok(U(ctx.iosb).Status == 0xdeadbabe, "wrong status %lx\n", U(ctx.iosb).Status); + ok(ctx.iosb.Information == 0xdeadbeef, "wrong info %Iu\n", ctx.iosb.Information); + + /* specified io */ + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + + U(ctx.iosb).Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + thread = CreateThread(NULL, 0, synchronousio_thread, &ctx, 0, 0); + /* wait for I/O to start, which transitions the pipe handle from signaled to nonsignaled state. */ + while ((ret = WaitForSingleObject(ctx.pipe, 0)) == WAIT_OBJECT_0) Sleep(1); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %lu (error %lu)\n", ret, GetLastError()); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(thread, &iosb, &iosb); + todo_wine { + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(NULL, &ctx.iosb, &iosb); + ok(res == STATUS_INVALID_HANDLE, "NtCancelSynchronousIoFile returned %lx\n", res); + res = pNtCancelSynchronousIoFile(thread, &ctx.iosb, &iosb); + ok(res == STATUS_SUCCESS || broken(is_wow64 && res == STATUS_NOT_FOUND), + "Failed to cancel I/O\n"); + ok(U(iosb).Status == STATUS_SUCCESS || broken(is_wow64 && U(iosb).Status == STATUS_NOT_FOUND), + "iosb.Status got changed to %lx\n", U(iosb).Status); + ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information); + } + if (res == STATUS_NOT_FOUND) + { + res = pNtCancelSynchronousIoFile(thread, NULL, &iosb); + ok(res == STATUS_SUCCESS, "Failed to cancel I/O\n"); + ok(U(iosb).Status == STATUS_SUCCESS, "iosb.Status got changed to %lx\n", U(iosb).Status); + } + if (res == STATUS_NOT_IMPLEMENTED) + pNtCancelIoFileEx(ctx.pipe, NULL, &iosb); + ret = WaitForSingleObject(thread, 1000); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(thread); + CloseHandle(ctx.pipe); + ok(U(ctx.iosb).Status == 0xdeadbabe, "wrong status %lx\n", U(ctx.iosb).Status); + ok(ctx.iosb.Information == 0xdeadbeef, "wrong info %Iu\n", ctx.iosb.Information); + + /* asynchronous i/o */ + U(ctx.iosb).Status = 0xdeadbabe; + ctx.iosb.Information = 0xdeadbeef; + res = create_pipe(&ctx.pipe, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 /* OVERLAPPED */); + ok(!res, "NtCreateNamedPipeFile returned %lx\n", res); + event = CreateEventW(NULL, TRUE, FALSE, NULL); + ok(event != INVALID_HANDLE_VALUE, "Can't create event, GetLastError: %lx\n", GetLastError()); + res = listen_pipe(ctx.pipe, event, &ctx.iosb, FALSE); + ok(res == STATUS_PENDING, "NtFsControlFile returned %lx\n", res); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), NULL, &iosb); + todo_wine { + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + ok(U(iosb).Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", U(iosb).Status); + ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information); + memset(&iosb, 0x55, sizeof(iosb)); + res = pNtCancelSynchronousIoFile(GetCurrentThread(), &ctx.iosb, &iosb); + ok(res == STATUS_NOT_FOUND, "NtCancelSynchronousIoFile returned %lx\n", res); + ok(U(iosb).Status == STATUS_NOT_FOUND, "iosb.Status got changed to %lx\n", U(iosb).Status); + ok(U(iosb).Information == 0, "iosb.Information got changed to %Iu\n", U(iosb).Information); + } + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_TIMEOUT, "wait returned %lx\n", ret); + client = CreateFileW(testpipe, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, 0); + ok(client != INVALID_HANDLE_VALUE, "can't open pipe: %lu\n", GetLastError()); + ret = WaitForSingleObject(event, 0); + ok(ret == WAIT_OBJECT_0, "wait returned %lx\n", ret); + CloseHandle(ctx.pipe); + CloseHandle(event); + CloseHandle(client); +} + static void _check_pipe_handle_state(int line, HANDLE handle, ULONG read, ULONG completion) { IO_STATUS_BLOCK iosb; @@ -2663,6 +2812,8 @@ START_TEST(pipe) if (!init_func_ptrs()) return;
+ if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + trace("starting invalid create tests\n"); test_create_invalid();
@@ -2694,6 +2845,9 @@ START_TEST(pipe) trace("starting cancelio tests\n"); test_cancelio();
+ trace("starting cancelsynchronousio tests\n"); + test_cancelsynchronousio(); + trace("starting byte read in byte mode client -> server\n"); read_pipe_test(PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE); trace("starting byte read in message mode client -> server\n"); diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 7eb8dbe7ad4..269f96c6ea8 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -6028,6 +6028,15 @@ NTSTATUS WINAPI NtCancelIoFileEx( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_ }
+/************************************************************************** + * NtCancelSynchronousIoFile (NTDLL.@) + */ +NTSTATUS WINAPI NtCancelSynchronousIoFile( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status ) +{ + FIXME( "(%p,%p,%p) stub\n", handle, io, io_status ); + return STATUS_NOT_IMPLEMENTED; +} + /****************************************************************** * NtLockFile (NTDLL.@) */ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index b78efddee1f..c21d32ea811 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -139,6 +139,7 @@ static void * const syscalls[] = NtCallbackReturn, NtCancelIoFile, NtCancelIoFileEx, + NtCancelSynchronousIoFile, NtCancelTimer, NtClearEvent, NtClose, diff --git a/dlls/wow64/file.c b/dlls/wow64/file.c index 7d1c736e48c..ebe0e9b3a77 100644 --- a/dlls/wow64/file.c +++ b/dlls/wow64/file.c @@ -227,6 +227,24 @@ NTSTATUS WINAPI wow64_NtCancelIoFileEx( UINT *args ) }
+/********************************************************************** + * wow64_NtCancelSynchronousIoFile + */ +NTSTATUS WINAPI wow64_NtCancelSynchronousIoFile( UINT *args ) +{ + HANDLE handle = get_handle( &args ); + IO_STATUS_BLOCK32 *io_ptr = get_ptr( &args ); + IO_STATUS_BLOCK32 *io32 = get_ptr( &args ); + + IO_STATUS_BLOCK io; + NTSTATUS status; + + status = NtCancelSynchronousIoFile( handle, (IO_STATUS_BLOCK *)io_ptr, iosb_32to64( &io, io32 )); + put_iosb( io32, &io ); + return status; +} + + /********************************************************************** * wow64_NtCreateFile */ diff --git a/dlls/wow64/syscall.h b/dlls/wow64/syscall.h index b2b1ad6c2a8..935d2bc7fce 100644 --- a/dlls/wow64/syscall.h +++ b/dlls/wow64/syscall.h @@ -40,6 +40,7 @@ SYSCALL_ENTRY( NtCallbackReturn ) \ SYSCALL_ENTRY( NtCancelIoFile ) \ SYSCALL_ENTRY( NtCancelIoFileEx ) \ + SYSCALL_ENTRY( NtCancelSynchronousIoFile ) \ SYSCALL_ENTRY( NtCancelTimer ) \ SYSCALL_ENTRY( NtClearEvent ) \ SYSCALL_ENTRY( NtClose ) \ diff --git a/include/winternl.h b/include/winternl.h index beb0cb7aa05..fea26bf26c6 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3948,6 +3948,7 @@ NTSYSAPI NTSTATUS WINAPI NtAssignProcessToJobObject(HANDLE,HANDLE); NTSYSAPI NTSTATUS WINAPI NtCallbackReturn(PVOID,ULONG,NTSTATUS); NTSYSAPI NTSTATUS WINAPI NtCancelIoFile(HANDLE,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelIoFileEx(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); +NTSYSAPI NTSTATUS WINAPI NtCancelSynchronousIoFile(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelTimer(HANDLE, BOOLEAN*); NTSYSAPI NTSTATUS WINAPI NtClearEvent(HANDLE); NTSYSAPI NTSTATUS WINAPI NtClose(HANDLE);