ntoskrnl/tests: Add more tests for IRP status handling.
Signed-off-by: Zebediah Figura <zfigura@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a5b1d51594
commit
8d37962b43
|
@ -2395,22 +2395,64 @@ static NTSTATUS get_fscontext(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *inf
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS return_status(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
||||
static NTSTATUS return_status(IRP *irp, IO_STACK_LOCATION *stack, ULONG code)
|
||||
{
|
||||
char *buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
NTSTATUS ret;
|
||||
ULONG input_length = stack->Parameters.DeviceIoControl.InputBufferLength;
|
||||
ULONG output_length = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
const struct return_status_params *input_buffer;
|
||||
struct return_status_params params;
|
||||
void *output_buffer;
|
||||
|
||||
if (!buffer)
|
||||
if (code == IOCTL_WINETEST_RETURN_STATUS_NEITHER)
|
||||
{
|
||||
input_buffer = stack->Parameters.DeviceIoControl.Type3InputBuffer;
|
||||
output_buffer = irp->UserBuffer;
|
||||
}
|
||||
else if (code == IOCTL_WINETEST_RETURN_STATUS_DIRECT)
|
||||
{
|
||||
input_buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
output_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
input_buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
output_buffer = irp->AssociatedIrp.SystemBuffer;
|
||||
}
|
||||
|
||||
if (!input_buffer || !output_buffer)
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
|
||||
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DWORD)
|
||||
|| stack->Parameters.DeviceIoControl.OutputBufferLength < 3)
|
||||
if (input_length < sizeof(params) || output_length < 6)
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
ret = *(DWORD *)irp->AssociatedIrp.SystemBuffer;
|
||||
memcpy(buffer, "ghi", 3);
|
||||
*info = 3;
|
||||
return ret;
|
||||
params = *input_buffer;
|
||||
|
||||
if (params.ret_status == STATUS_PENDING && !params.pending)
|
||||
{
|
||||
/* this causes kernel hangs under certain conditions */
|
||||
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (params.pending)
|
||||
IoMarkIrpPending(irp);
|
||||
|
||||
/* intentionally report the wrong information (and status) */
|
||||
memcpy(output_buffer, "ghijkl", 6);
|
||||
irp->IoStatus.Information = 3;
|
||||
irp->IoStatus.Status = params.iosb_status;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
|
||||
return params.ret_status;
|
||||
}
|
||||
|
||||
static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
||||
|
@ -2430,34 +2472,6 @@ static NTSTATUS test_load_driver_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG
|
|||
return ZwUnloadDriver(&name);
|
||||
}
|
||||
|
||||
static NTSTATUS test_mismatched_status_ioctl(IRP *irp, IO_STACK_LOCATION *stack, ULONG_PTR *info)
|
||||
{
|
||||
ULONG length = stack->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
char *buffer = irp->UserBuffer;
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_ACCESS_VIOLATION;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
|
||||
if (length < sizeof(teststr))
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
memcpy(buffer, teststr, sizeof(teststr));
|
||||
|
||||
/* This is deliberate; some broken drivers do this */
|
||||
*info = 0;
|
||||
irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS completion_ioctl(DEVICE_OBJECT *device, IRP *irp, IO_STACK_LOCATION *stack)
|
||||
{
|
||||
if (device == upper_device)
|
||||
|
@ -2564,15 +2578,14 @@ static NTSTATUS WINAPI driver_IoControl(DEVICE_OBJECT *device, IRP *irp)
|
|||
case IOCTL_WINETEST_GET_FSCONTEXT:
|
||||
status = get_fscontext(irp, stack, &irp->IoStatus.Information);
|
||||
break;
|
||||
case IOCTL_WINETEST_RETURN_STATUS:
|
||||
status = return_status(irp, stack, &irp->IoStatus.Information);
|
||||
break;
|
||||
case IOCTL_WINETEST_RETURN_STATUS_BUFFERED:
|
||||
case IOCTL_WINETEST_RETURN_STATUS_DIRECT:
|
||||
case IOCTL_WINETEST_RETURN_STATUS_NEITHER:
|
||||
return return_status(irp, stack, stack->Parameters.DeviceIoControl.IoControlCode);
|
||||
case IOCTL_WINETEST_DETACH:
|
||||
IoDetachDevice(lower_device);
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
case IOCTL_WINETEST_MISMATCHED_STATUS:
|
||||
return test_mismatched_status_ioctl(irp, stack, &irp->IoStatus.Information);
|
||||
case IOCTL_WINETEST_COMPLETION:
|
||||
return completion_ioctl(device, irp, stack);
|
||||
default:
|
||||
|
|
|
@ -22,21 +22,20 @@
|
|||
|
||||
|
||||
/* All custom IOCTLs need to have a function value >= 0x800. */
|
||||
#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_LOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RESET_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_TEST_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CANCEL_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CREATE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CLOSE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_FSCONTEXT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RETURN_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MISMATCHED_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80b, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_COMPLETION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80c, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MARK_PENDING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80d, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_CHECK_REMOVED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80e, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BASIC_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_MAIN_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_LOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RESET_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_TEST_CANCEL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CANCEL_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_DETACH CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CREATE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_CLOSE_COUNT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_GET_FSCONTEXT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RETURN_STATUS_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RETURN_STATUS_DIRECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_RETURN_STATUS_NEITHER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80a, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_COMPLETION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80c, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_WINETEST_BUS_MAIN CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_REGISTER_IFACE CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
@ -45,7 +44,9 @@
|
|||
#define IOCTL_WINETEST_BUS_ADD_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_BUS_REMOVE_CHILD CTL_CODE(FILE_DEVICE_BUS_EXTENDER, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_WINETEST_CHILD_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_CHILD_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_CHILD_MARK_PENDING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_WINETEST_CHILD_CHECK_REMOVED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
static const char teststr[] = "Wine is not an emulator";
|
||||
|
||||
|
@ -65,6 +66,13 @@ struct main_test_input
|
|||
ULONG64 *modified_value;
|
||||
};
|
||||
|
||||
struct return_status_params
|
||||
{
|
||||
NTSTATUS ret_status;
|
||||
NTSTATUS iosb_status;
|
||||
BOOL pending;
|
||||
};
|
||||
|
||||
static const GUID control_class = {0xdeadbeef, 0x29ef, 0x4538, {0xa5, 0xfd, 0xb6, 0x95, 0x73, 0xa3, 0x62, 0xc0}};
|
||||
|
||||
#define SERVER_LISTEN_PORT 9374
|
||||
|
|
|
@ -276,7 +276,7 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
|
|||
}
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
/* should've been checked and reset by IOCTL_WINETEST_CHECK_REMOVED */
|
||||
/* should've been checked and reset by IOCTL_WINETEST_CHILD_CHECK_REMOVED */
|
||||
ok(remove_device_count == 0, "expected no IRP_MN_REMOVE_DEVICE\n");
|
||||
todo_wine ok(surprise_removal_count == 0, "expected no IRP_MN_SURPRISE_REMOVAL\n");
|
||||
ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
|
||||
|
@ -665,12 +665,12 @@ static NTSTATUS pdo_ioctl(DEVICE_OBJECT *device_obj, IRP *irp, IO_STACK_LOCATION
|
|||
irp->IoStatus.Information = sizeof(device->id);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case IOCTL_WINETEST_MARK_PENDING:
|
||||
case IOCTL_WINETEST_CHILD_MARK_PENDING:
|
||||
IoMarkIrpPending(irp);
|
||||
irp_queue_push(&device->irp_queue, irp);
|
||||
return STATUS_PENDING;
|
||||
|
||||
case IOCTL_WINETEST_CHECK_REMOVED:
|
||||
case IOCTL_WINETEST_CHILD_CHECK_REMOVED:
|
||||
ok(remove_device_count == 0, "expected IRP_MN_REMOVE_DEVICE\n");
|
||||
ok(surprise_removal_count == 1, "expected IRP_MN_SURPRISE_REMOVAL\n");
|
||||
ok(query_remove_device_count == 0, "expected no IRP_MN_QUERY_REMOVE_DEVICE\n");
|
||||
|
|
|
@ -430,18 +430,6 @@ static void test_basic_ioctl(void)
|
|||
ok(!strcmp(buf, "Wine is no"), "got '%s'\n", buf);
|
||||
}
|
||||
|
||||
static void test_mismatched_status_ioctl(void)
|
||||
{
|
||||
DWORD written;
|
||||
char buf[32];
|
||||
BOOL res;
|
||||
|
||||
res = DeviceIoControl(device, IOCTL_WINETEST_MISMATCHED_STATUS, NULL, 0, buf,
|
||||
sizeof(buf), &written, NULL);
|
||||
todo_wine ok(res, "DeviceIoControl failed: %u\n", GetLastError());
|
||||
todo_wine ok(!strcmp(buf, teststr), "got '%s'\n", buf);
|
||||
}
|
||||
|
||||
static void test_overlapped(void)
|
||||
{
|
||||
OVERLAPPED overlapped, overlapped2, *o;
|
||||
|
@ -631,72 +619,376 @@ static void test_file_handles(void)
|
|||
ok(count == 3, "got %u\n", count);
|
||||
}
|
||||
|
||||
static void test_return_status(void)
|
||||
static unsigned int got_return_status_apc;
|
||||
|
||||
static void WINAPI return_status_apc(void *apc_user, IO_STATUS_BLOCK *io, ULONG reserved)
|
||||
{
|
||||
NTSTATUS status;
|
||||
++got_return_status_apc;
|
||||
ok(apc_user == (void *)456, "got %p\n", apc_user);
|
||||
ok(!reserved, "got reserved %#x\n", reserved);
|
||||
}
|
||||
|
||||
static void do_return_status(ULONG ioctl, struct return_status_params *params)
|
||||
{
|
||||
const char *expect_buffer;
|
||||
LARGE_INTEGER zero = {{0}};
|
||||
HANDLE file, port, event;
|
||||
NTSTATUS expect_status;
|
||||
ULONG_PTR key, value;
|
||||
IO_STATUS_BLOCK io;
|
||||
char buffer[7];
|
||||
DWORD ret_size;
|
||||
DWORD size;
|
||||
BOOL ret;
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = STATUS_SUCCESS;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
ok(ret, "ioctl failed\n");
|
||||
ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
if (params->ret_status == STATUS_PENDING && !params->pending)
|
||||
{
|
||||
/* this causes kernel hangs under certain conditions */
|
||||
return;
|
||||
}
|
||||
|
||||
event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
expect_buffer = "ghijkl";
|
||||
else if (NT_ERROR(params->iosb_status))
|
||||
expect_buffer = "abcdef";
|
||||
else
|
||||
expect_buffer = "ghidef";
|
||||
|
||||
/* Test the non-overlapped case. */
|
||||
|
||||
expect_status = (params->ret_status == STATUS_PENDING ? params->iosb_status : params->ret_status);
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = STATUS_TIMEOUT;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
todo_wine ok(ret, "ioctl failed\n");
|
||||
todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
size = 0xdeadf00d;
|
||||
SetLastError(0xdeadf00d);
|
||||
ret = DeviceIoControl(device, ioctl, params, sizeof(*params), buffer, sizeof(buffer), &size, NULL);
|
||||
todo_wine_if (NT_SUCCESS(expect_status) != !params->iosb_status)
|
||||
ok(ret == NT_SUCCESS(expect_status), "got %d\n", ret);
|
||||
if (NT_SUCCESS(expect_status))
|
||||
{
|
||||
todo_wine_if (params->iosb_status)
|
||||
ok(GetLastError() == 0xdeadf00d, "got error %u\n", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (RtlNtStatusToDosError(expect_status) != RtlNtStatusToDosError(params->iosb_status)
|
||||
|| params->iosb_status == STATUS_PENDING)
|
||||
ok(GetLastError() == RtlNtStatusToDosError(expect_status), "got error %u\n", GetLastError());
|
||||
}
|
||||
if (NT_ERROR(expect_status))
|
||||
todo_wine ok(size == 0xdeadf00d, "got size %u\n", size);
|
||||
else if (!NT_ERROR(params->iosb_status))
|
||||
ok(size == 3, "got size %u\n", size);
|
||||
/* size is garbage if !NT_ERROR(expect_status) && NT_ERROR(iosb_status) */
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = 0x0eadbeef;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
todo_wine ok(ret, "ioctl failed\n");
|
||||
todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(device, NULL, NULL, NULL, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if ((params->ret_status != params->iosb_status && params->ret_status != STATUS_PENDING)
|
||||
|| params->iosb_status == STATUS_PENDING)
|
||||
ok(ret == expect_status, "got %#x\n", ret);
|
||||
if (NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
/* Test the overlapped case. */
|
||||
|
||||
file = CreateFileA("\\\\.\\WineTestDriver", FILE_ALL_ACCESS,
|
||||
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
ok(file != INVALID_HANDLE_VALUE, "failed to open device, error %u\n", GetLastError());
|
||||
port = CreateIoCompletionPort(file, NULL, 123, 0);
|
||||
ok(port != NULL, "failed to create port, error %u\n", GetLastError());
|
||||
|
||||
ret = WaitForSingleObject(file, 0);
|
||||
todo_wine ok(!ret, "got %d\n", ret);
|
||||
|
||||
ResetEvent(event);
|
||||
strcpy(buffer, "abcdef");
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(file, event, NULL, (void *)456, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
|
||||
ok(ret == params->ret_status
|
||||
|| broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
|
||||
"got %#x\n", ret);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
ok(!ret, "got %d\n", ret);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
ret = WaitForSingleObject(file, 0);
|
||||
ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
|
||||
key = 0xdeadf00d;
|
||||
value = 0xdeadf00d;
|
||||
memset(&io, 0xcc, sizeof(io));
|
||||
ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(ret == STATUS_TIMEOUT, "got %#x\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ok(!ret, "got %#x\n", ret);
|
||||
ok(key == 123, "got key %Iu\n", key);
|
||||
ok(value == 456, "got value %Iu\n", value);
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got iosb status %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got information %Iu\n", io.Information);
|
||||
}
|
||||
|
||||
/* As above, but set the event first, to show that the event is always
|
||||
* reset. */
|
||||
ResetEvent(event);
|
||||
strcpy(buffer, "abcdef");
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(file, event, NULL, NULL, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
|
||||
ok(ret == params->ret_status
|
||||
|| broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
|
||||
"got %#x\n", ret);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
ok(!ret, "got %d\n", ret);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
/* As above, but use the file handle instead of an event. */
|
||||
ret = WaitForSingleObject(file, 0);
|
||||
ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = 0x4eadbeef;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
todo_wine ok(ret, "ioctl failed\n");
|
||||
todo_wine ok(GetLastError() == 0xdeadbeef, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(file, NULL, NULL, NULL, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
|
||||
ok(ret == params->ret_status
|
||||
|| broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
|
||||
"got %#x\n", ret);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(file, 0);
|
||||
todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(file, 0);
|
||||
ok(!ret, "got %d\n", ret);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
/* Test FILE_SKIP_COMPLETION_PORT_ON_SUCCESS. */
|
||||
|
||||
if (pSetFileCompletionNotificationModes)
|
||||
{
|
||||
ret = pSetFileCompletionNotificationModes(file, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS);
|
||||
ok(ret, "got error %u\n", GetLastError());
|
||||
|
||||
SetEvent(event);
|
||||
strcpy(buffer, "abcdef");
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(file, event, NULL, (void *)456, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
|
||||
ok(ret == params->ret_status
|
||||
|| broken(NT_WARNING(params->ret_status) && ret == STATUS_PENDING), /* win10 */
|
||||
"got %#x\n", ret);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
todo_wine ok(ret == WAIT_TIMEOUT, "got %d\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
ret = WaitForSingleObject(event, 0);
|
||||
ok(!ret, "got %d\n", ret);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
key = 0xdeadf00d;
|
||||
value = 0xdeadf00d;
|
||||
memset(&io, 0xcc, sizeof(io));
|
||||
ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero);
|
||||
if (!params->pending)
|
||||
{
|
||||
/* Completion is skipped on non-pending NT_ERROR regardless of file
|
||||
* options. Windows < 8 interprets
|
||||
* FILE_SKIP_COMPLETION_PORT_ON_SUCCESS to mean that !NT_ERROR
|
||||
* should also be skipped. Windows >= 8 restricts this to
|
||||
* NT_SUCCESS, which has the weird effect that non-pending
|
||||
* NT_WARNING does *not* skip completion. It's not clear whether
|
||||
* this is a bug or not—it looks like one, but on the other hand it
|
||||
* arguably follows the letter of the documentation more closely. */
|
||||
ok(ret == STATUS_TIMEOUT || (NT_WARNING(params->iosb_status) && !ret), "got %#x\n", ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine ok(!ret, "got %#x\n", ret);
|
||||
}
|
||||
if (!ret)
|
||||
{
|
||||
ok(key == 123, "got key %Iu\n", key);
|
||||
ok(value == 456, "got value %Iu\n", value);
|
||||
ok(io.Status == params->iosb_status, "got iosb status %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got information %Iu\n", io.Information);
|
||||
}
|
||||
}
|
||||
|
||||
ret = CloseHandle(file);
|
||||
ok(ret, "failed to close file, error %u\n", GetLastError());
|
||||
ret = CloseHandle(port);
|
||||
ok(ret, "failed to close port, error %u\n", GetLastError());
|
||||
|
||||
/* Test with an APC. */
|
||||
|
||||
got_return_status_apc = 0;
|
||||
|
||||
file = CreateFileA("\\\\.\\WineTestDriver", FILE_ALL_ACCESS,
|
||||
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
ok(file != INVALID_HANDLE_VALUE, "failed to open device, error %u\n", GetLastError());
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = 0x8eadbeef;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
ok(!ret, "ioctl succeeded\n");
|
||||
ok(GetLastError() == ERROR_MR_MID_NOT_FOUND, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "ghidef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
ret = NtDeviceIoControlFile(file, NULL, return_status_apc, (void *)456, &io,
|
||||
ioctl, params, sizeof(*params), buffer, sizeof(buffer));
|
||||
todo_wine_if (params->ret_status != params->iosb_status || params->ret_status == STATUS_PENDING)
|
||||
ok(ret == params->ret_status, "got %#x\n", ret);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(io.Status == 0xdeadf00d, "got %#x\n", io.Status);
|
||||
todo_wine ok(io.Information == 0xdeadf00d, "got size %Iu\n", io.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
todo_wine_if (params->iosb_status == STATUS_PENDING)
|
||||
ok(io.Status == params->iosb_status, "got %#x\n", io.Status);
|
||||
ok(io.Information == 3, "got size %Iu\n", io.Information);
|
||||
}
|
||||
todo_wine_if (ioctl != IOCTL_WINETEST_RETURN_STATUS_BUFFERED)
|
||||
ok(!strcmp(buffer, expect_buffer), "got buffer %s\n", buffer);
|
||||
|
||||
strcpy(buffer, "abcdef");
|
||||
status = 0xceadbeef;
|
||||
SetLastError(0xdeadbeef);
|
||||
ret = DeviceIoControl(device, IOCTL_WINETEST_RETURN_STATUS, &status,
|
||||
sizeof(status), buffer, sizeof(buffer), &ret_size, NULL);
|
||||
ok(!ret, "ioctl succeeded\n");
|
||||
ok(GetLastError() == ERROR_MR_MID_NOT_FOUND, "got error %u\n", GetLastError());
|
||||
ok(!strcmp(buffer, "abcdef"), "got buffer %s\n", buffer);
|
||||
ok(ret_size == 3, "got size %u\n", ret_size);
|
||||
ret = SleepEx(0, TRUE);
|
||||
if (!params->pending && NT_ERROR(params->iosb_status))
|
||||
{
|
||||
todo_wine ok(!ret, "got %d\n", ret);
|
||||
todo_wine ok(!got_return_status_apc, "got %u APC calls\n", got_return_status_apc);
|
||||
}
|
||||
else
|
||||
{
|
||||
ok(ret == WAIT_IO_COMPLETION, "got %d\n", ret);
|
||||
ok(got_return_status_apc == 1, "got %u APC calls\n", got_return_status_apc);
|
||||
}
|
||||
|
||||
ret = CloseHandle(file);
|
||||
ok(ret, "failed to close file, error %u\n", GetLastError());
|
||||
|
||||
CloseHandle(event);
|
||||
}
|
||||
|
||||
static void test_return_status(void)
|
||||
{
|
||||
struct return_status_params params;
|
||||
unsigned int i, j, k;
|
||||
|
||||
static const ULONG method_tests[] =
|
||||
{
|
||||
IOCTL_WINETEST_RETURN_STATUS_BUFFERED,
|
||||
IOCTL_WINETEST_RETURN_STATUS_DIRECT,
|
||||
IOCTL_WINETEST_RETURN_STATUS_NEITHER,
|
||||
};
|
||||
|
||||
static const NTSTATUS status_tests[] =
|
||||
{
|
||||
STATUS_SUCCESS,
|
||||
STATUS_PENDING,
|
||||
STATUS_TIMEOUT,
|
||||
0x0eadbeef,
|
||||
0x4eadbeef,
|
||||
STATUS_BUFFER_OVERFLOW,
|
||||
0x8eadbeef,
|
||||
STATUS_NOT_IMPLEMENTED,
|
||||
0xceadbeef,
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status_tests); ++i)
|
||||
{
|
||||
for (j = 0; j < ARRAY_SIZE(status_tests); ++j)
|
||||
{
|
||||
for (params.pending = 0; params.pending <= 1; ++params.pending)
|
||||
{
|
||||
for (k = 0; k < ARRAY_SIZE(method_tests); ++k)
|
||||
{
|
||||
params.ret_status = status_tests[i];
|
||||
params.iosb_status = status_tests[j];
|
||||
|
||||
winetest_push_context("return 0x%08x, iosb 0x%08x, pending %d, method %u",
|
||||
params.ret_status, params.iosb_status, params.pending, method_tests[k] & 3);
|
||||
|
||||
do_return_status(method_tests[k], ¶ms);
|
||||
|
||||
winetest_pop_context();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL compare_unicode_string(const WCHAR *buffer, ULONG len, const WCHAR *expect)
|
||||
|
@ -765,13 +1057,25 @@ static void test_object_info(void)
|
|||
ok(compare_unicode_string(file_info->FileName, file_info->FileNameLength, L"\\subfile"),
|
||||
"wrong name %s\n", debugstr_wn(file_info->FileName, file_info->FileNameLength / sizeof(WCHAR)));
|
||||
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
status = NtQueryVolumeInformationFile(file, &io, buffer, sizeof(buffer), FileFsVolumeInformation);
|
||||
ok(!status, "got %#x\n", status);
|
||||
ok(!io.Status, "got status %#x\n", io.Status);
|
||||
size = offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + volume_info->VolumeLabelLength;
|
||||
ok(io.Information == size, "expected information %Iu, got %Iu\n", size, io.Information);
|
||||
ok(volume_info->VolumeSerialNumber == 0xdeadbeef,
|
||||
"wrong serial number 0x%08x\n", volume_info->VolumeSerialNumber);
|
||||
ok(compare_unicode_string(volume_info->VolumeLabel, volume_info->VolumeLabelLength, L"WineTestDriver"),
|
||||
"wrong name %s\n", debugstr_wn(volume_info->VolumeLabel, volume_info->VolumeLabelLength / sizeof(WCHAR)));
|
||||
|
||||
io.Status = 0xdeadf00d;
|
||||
io.Information = 0xdeadf00d;
|
||||
status = NtQueryVolumeInformationFile(file, &io, buffer, sizeof(buffer), FileFsAttributeInformation);
|
||||
ok(status == STATUS_NOT_IMPLEMENTED, "got %#x\n", status);
|
||||
ok(io.Status == 0xdeadf00d, "got status %#x\n", io.Status);
|
||||
ok(io.Information == 0xdeadf00d, "got information %Iu\n", io.Information);
|
||||
|
||||
CloseHandle(file);
|
||||
|
||||
file = CreateFileA("\\\\.\\WineTestDriver\\notimpl", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
@ -1354,7 +1658,7 @@ static void test_pnp_devices(void)
|
|||
ret = NtOpenFile(&child, SYNCHRONIZE, &attr, &io, 0, 0);
|
||||
ok(!ret, "failed to open child: %#x\n", ret);
|
||||
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_MARK_PENDING, NULL, 0, NULL, 0, &size, &ovl);
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_MARK_PENDING, NULL, 0, NULL, 0, &size, &ovl);
|
||||
ok(!ret, "DeviceIoControl succeeded\n");
|
||||
ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError());
|
||||
ok(size == 0, "got size %u\n", size);
|
||||
|
@ -1367,7 +1671,7 @@ static void test_pnp_devices(void)
|
|||
ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival);
|
||||
ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal);
|
||||
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL);
|
||||
ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL);
|
||||
todo_wine ok(ret, "got error %u\n", GetLastError());
|
||||
|
||||
ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
|
@ -3162,7 +3466,6 @@ START_TEST(ntoskrnl)
|
|||
ok(device != INVALID_HANDLE_VALUE, "failed to open device: %u\n", GetLastError());
|
||||
|
||||
test_basic_ioctl();
|
||||
test_mismatched_status_ioctl();
|
||||
|
||||
main_test();
|
||||
todo_wine ok(modified_value == 0xdeadbeeffeedcafe, "Got unexpected value %#I64x.\n", modified_value);
|
||||
|
|
Loading…
Reference in New Issue