From 3ad0085d6003a4521dfd7089f34c431f105835df Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Wed, 10 Jul 2019 19:14:45 -0500 Subject: [PATCH 3/4] server: Delay completing a synchronous IRP. Wait until the client fetches the result. Save any buffer that the driver provided so it can be copied back to the client. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30155 Signed-off-by: Chip Davis --- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 4 +-- server/device.c | 50 ++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 2535ed903e..1765ae7ddf 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -216,8 +216,8 @@ static void test_mismatched_status_ioctl(void) 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); + ok(res, "DeviceIoControl failed: %u\n", GetLastError()); + ok(!strcmp(buf, teststr), "got '%s'\n", buf); } static void test_overlapped(void) diff --git a/server/device.c b/server/device.c index d3e2a84c1e..99492f8202 100644 --- a/server/device.c +++ b/server/device.c @@ -52,6 +52,7 @@ struct irp_call struct async *async; /* pending async op */ irp_params_t params; /* irp parameters */ struct iosb *iosb; /* I/O status block */ + int dispatched; /* the call's dispatch returned */ int canceled; /* the call was canceled */ client_ptr_t user_ptr; /* client side pointer */ }; @@ -349,13 +350,14 @@ static struct irp_call *create_irp( struct device_file *file, const irp_params_t if ((irp = alloc_object( &irp_call_ops ))) { - irp->file = file ? (struct device_file *)grab_object( file ) : NULL; - irp->thread = NULL; - irp->async = NULL; - irp->params = *params; - irp->iosb = NULL; - irp->canceled = 0; - irp->user_ptr = 0; + irp->file = file ? (struct device_file *)grab_object( file ) : NULL; + irp->thread = NULL; + irp->async = NULL; + irp->params = *params; + irp->iosb = NULL; + irp->dispatched = 0; + irp->canceled = 0; + irp->user_ptr = 0; if (async) irp->iosb = async_get_iosb( async ); if (!irp->iosb && !(irp->iosb = create_iosb( NULL, 0, 0 ))) @@ -367,6 +369,19 @@ static struct irp_call *create_irp( struct device_file *file, const irp_params_t return irp; } +static void set_irp_result_buffer( struct irp_call *irp, const void *out_data, + data_size_t out_size, data_size_t result ) +{ + struct iosb *iosb = irp->iosb; + + if (!irp->file) return; /* already finished */ + + iosb->result = result; + iosb->out_size = min( iosb->out_size, out_size ); + if (iosb->out_size && !(iosb->out_data = memdup( out_data, iosb->out_size ))) + iosb->out_size = 0; +} + static void set_irp_result( struct irp_call *irp, unsigned int status, const void *out_data, data_size_t out_size, data_size_t result ) { @@ -377,17 +392,15 @@ static void set_irp_result( struct irp_call *irp, unsigned int status, /* FIXME: handle the STATUS_PENDING case */ iosb->status = status; - iosb->result = result; - iosb->out_size = min( iosb->out_size, out_size ); - if (iosb->out_size && !(iosb->out_data = memdup( out_data, iosb->out_size ))) - iosb->out_size = 0; + if (!iosb->out_data || irp->dispatched) + set_irp_result_buffer( irp, out_data, out_size, result ); /* remove it from the device queue */ list_remove( &irp->dev_entry ); irp->file = NULL; if (irp->async) { - if (result) status = STATUS_ALERTED; + if (iosb->out_size || result) status = STATUS_ALERTED; async_terminate( irp->async, status ); release_object( irp->async ); irp->async = NULL; @@ -933,13 +946,15 @@ DECL_HANDLER(get_next_device_request) irp = manager->current_call; irp->user_ptr = req->user_ptr; - if (req->status) - set_irp_result( irp, req->status, NULL, 0, 0 ); + if (req->status != STATUS_PENDING) + set_irp_result( irp, req->status, NULL, 0, irp->iosb->result ); + else + irp->dispatched = 1; if (irp->canceled) /* if it was canceled during dispatch, we couldn't queue cancel call without client pointer, * so we need to do it now */ cancel_irp_call( irp ); - else if (irp->async) + else if (irp->async && req->status == STATUS_PENDING) set_async_pending( irp->async, irp->file && is_fd_overlapped( irp->file->fd ) ); free_irp_params( irp ); @@ -991,7 +1006,10 @@ DECL_HANDLER(set_irp_result) if ((irp = (struct irp_call *)get_handle_obj( current->process, req->handle, 0, &irp_call_ops ))) { - if (!irp->canceled) + if (!irp->canceled && !irp->dispatched && irp->file && !is_fd_overlapped( irp->file->fd )) + /* Don't complete the IRP right away, but save the buffer. */ + set_irp_result_buffer( irp, get_req_data(), get_req_data_size(), req->size ); + else if (!irp->canceled) set_irp_result( irp, req->status, get_req_data(), get_req_data_size(), req->size ); else if(irp->user_ptr) /* cancel already queued */ set_error( STATUS_MORE_PROCESSING_REQUIRED ); -- 2.17.1