[PATCH 3/4] server: Delay completing a synchronous IRP.

Chip Davis cdavis at codeweavers.com
Sun Nov 24 19:52:34 CST 2019


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 at codeweavers.com>
---
This patch is an absolute kluge. I hate it.
---

 server/device.c | 54 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 15 deletions(-)

diff --git a/server/device.c b/server/device.c
index 117dd99261a..c1464a66a5e 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,27 +369,44 @@ 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 (debug_level) fprintf( stderr, "%04x: set_irp_result_buffer(%p, %p, %u, %u)\n", current->id, irp, out_data, out_size, result );
+
+    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 )
 {
     struct device_file *file = irp->file;
     struct iosb *iosb = irp->iosb;
 
+    if (debug_level) fprintf( stderr, "%04x: set_irp_result(%p, %08x, %p, %u, %u)\n", current->id, irp, status, out_data, out_size, result );
+
     if (!file) return;  /* already finished */
 
+    if (debug_level) fprintf( stderr, "%04x: completing IRP\n", current->id );
+
     /* 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 (status != STATUS_SUCCESS || !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) status = STATUS_ALERTED;
         async_terminate( irp->async, status );
         release_object( irp->async );
         irp->async = NULL;
@@ -933,13 +952,15 @@ DECL_HANDLER(get_next_device_request)
         irp = manager->current_call;
         irp->user_ptr = req->user_ptr;
 
-        if (req->status)
+        if (req->status != STATUS_PENDING)
             set_irp_result( irp, req->status, NULL, 0, 0 );
+        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 +1012,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.21.0




More information about the wine-devel mailing list