mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
140 lines
5.8 KiB
Diff
140 lines
5.8 KiB
Diff
From 3ad0085d6003a4521dfd7089f34c431f105835df Mon Sep 17 00:00:00 2001
|
|
From: Chip Davis <cdavis@codeweavers.com>
|
|
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 <cdavis@codeweavers.com>
|
|
---
|
|
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
|
|
|