From: Chip Davis Subject: [PATCH 3/4] server: Delay completing a synchronous IRP. Message-Id: <20191125015235.49004-3-cdavis@codeweavers.com> Date: Sun, 24 Nov 2019 19:52:34 -0600 In-Reply-To: <20191125015235.49004-2-cdavis@codeweavers.com> References: <20191125015235.49004-1-cdavis@codeweavers.com> <20191125015235.49004-2-cdavis@codeweavers.com> 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 --- 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