# # Three patches taken from: # http://appdb.winehq.org/objectManager.php?sClass=version&iId=25953 # to make Diablo 3 installer work. # From: "Erich E. Hoover" Subject: [PATCH 1/3] server: Update stored completion information even after an async IO is queued (try 2). Message-Id: Date: Mon, 14 May 2012 16:12:03 -0600 Real Name: Erich Hoover Description: The completion information for a file handle can be updated after an async IO has been queued and this information will still be valid for the async operation (even if the handle is closed before the async completes!). The attached patch adds a file operation to update the completion information for a queued async operation so that such a change can be properly propagated to the async object. This version of the patch has been rebased to not deal with a stored completion handle. Changelog: server: Update stored completion information even after an async IO is queued. From 777ab1f5de8ed8d7a702197fe824afb3fb0650bd Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 14 May 2012 15:31:14 -0600 Subject: server: Update stored completion information even after an async IO is queued. --- server/async.c | 20 +++++++++++++++++++- server/fd.c | 9 +++++++++ server/file.c | 3 ++- server/file.h | 4 ++++ server/mailslot.c | 9 ++++++--- server/mapping.c | 3 ++- server/named_pipe.c | 7 +++++-- server/serial.c | 3 ++- server/signal.c | 3 ++- server/sock.c | 13 ++++++++++++- server/thread.c | 3 ++- 11 files changed, 65 insertions(+), 12 deletions(-) diff --git a/server/async.c b/server/async.c index dd28dff..01d1ca1 100644 --- a/server/async.c +++ b/server/async.c @@ -214,11 +214,12 @@ struct async *create_async( struct thread *thread, struct async_queue *queue, co async->timeout = NULL; async->queue = (struct async_queue *)grab_object( queue ); async->completion = NULL; - if (queue->fd) async->completion = fd_get_completion( queue->fd, &async->comp_key ); list_add_tail( &queue->queue, &async->queue_entry ); grab_object( async ); + async_update( async->queue ); + if (queue->fd) set_fd_signaled( queue->fd, 0 ); if (event) reset_event( event ); return async; @@ -330,3 +331,20 @@ void async_wake_up( struct async_queue *queue, unsigned int status ) if (status == STATUS_ALERTED) break; /* only wake up the first one */ } } + +/* update an async to correspond to new file object information */ +void async_update( struct async_queue *queue ) +{ + struct list *ptr, *next; + + if (!queue) return; + + LIST_FOR_EACH_SAFE( ptr, next, &queue->queue ) + { + struct async *async = LIST_ENTRY( ptr, struct async, queue_entry ); + + if (!async->completion && async->queue->fd) + async->completion = fd_get_completion( async->queue->fd, &async->comp_key ); + } +} + diff --git a/server/fd.c b/server/fd.c index a8b3a5f..07db476 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2103,6 +2103,14 @@ void default_fd_cancel_async( struct fd *fd, struct process *process, struct thr set_error( STATUS_NOT_FOUND ); } +/* default update_async() fd routine */ +void default_fd_update_async( struct fd *fd ) +{ + async_update( fd->read_q ); + async_update( fd->write_q ); + async_update( fd->wait_q ); +} + /* default flush() routine */ void no_flush( struct fd *fd, struct event **event ) { @@ -2350,6 +2358,7 @@ DECL_HANDLER(set_completion_info) { fd->completion = get_completion_obj( current->process, req->chandle, IO_COMPLETION_MODIFY_STATE ); fd->comp_key = req->ckey; + fd->fd_ops->update_async( fd ); } else set_error( STATUS_INVALID_PARAMETER ); release_object( fd ); diff --git a/server/file.c b/server/file.c index 02a1e37..83634c5 100644 --- a/server/file.c +++ b/server/file.c @@ -102,7 +102,8 @@ static const struct fd_ops file_fd_ops = default_fd_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; static inline int is_overlapped( const struct file *file ) diff --git a/server/file.h b/server/file.h index ead356d..bb3c420 100644 --- a/server/file.h +++ b/server/file.h @@ -48,6 +48,8 @@ struct fd_ops void (*reselect_async)( struct fd *, struct async_queue *queue ); /* cancel an async operation */ void (*cancel_async)(struct fd *, struct process *process, struct thread *thread, client_ptr_t iosb); + /* update an async operation to correspond to changes in the file object */ + void (*update_async)(struct fd *); }; /* file descriptor functions */ @@ -92,6 +94,7 @@ extern void no_fd_queue_async( struct fd *fd, const async_data_t *data, int type extern void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count ); extern void default_fd_reselect_async( struct fd *fd, struct async_queue *queue ); extern void default_fd_cancel_async( struct fd *fd, struct process *process, struct thread *thread, client_ptr_t iosb ); +extern void default_fd_update_async( struct fd *fd ); extern void no_flush( struct fd *fd, struct event **event ); extern void main_loop(void); extern void remove_process_locks( struct process *process ); @@ -164,6 +167,7 @@ extern void async_terminate( struct async *async, unsigned int status ); extern int async_wake_up_by( struct async_queue *queue, struct process *process, struct thread *thread, client_ptr_t iosb, unsigned int status ); extern void async_wake_up( struct async_queue *queue, unsigned int status ); +extern void async_update( struct async_queue *queue ); extern struct completion *fd_get_completion( struct fd *fd, apc_param_t *p_key ); extern void fd_copy_completion( struct fd *src, struct fd *dst ); diff --git a/server/mailslot.c b/server/mailslot.c index 051f0ad..b29c168 100644 --- a/server/mailslot.c +++ b/server/mailslot.c @@ -102,7 +102,8 @@ static const struct fd_ops mailslot_fd_ops = default_fd_ioctl, /* ioctl */ mailslot_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; @@ -152,7 +153,8 @@ static const struct fd_ops mail_writer_fd_ops = default_fd_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; @@ -202,7 +204,8 @@ static const struct fd_ops mailslot_device_fd_ops = default_fd_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; static void mailslot_destroy( struct object *obj) diff --git a/server/mapping.c b/server/mapping.c index 90956e9..60e9c5f 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -103,7 +103,8 @@ static const struct fd_ops mapping_fd_ops = no_fd_ioctl, /* ioctl */ no_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; static struct list shared_list = LIST_INIT(shared_list); diff --git a/server/named_pipe.c b/server/named_pipe.c index 590adca..2bd3827 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -173,6 +173,7 @@ static const struct fd_ops pipe_server_fd_ops = default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; /* client end functions */ @@ -212,7 +213,8 @@ static const struct fd_ops pipe_client_fd_ops = default_fd_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; static void named_pipe_device_dump( struct object *obj, int verbose ); @@ -256,7 +258,8 @@ static const struct fd_ops named_pipe_device_fd_ops = named_pipe_device_ioctl, /* ioctl */ default_fd_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; static void named_pipe_dump( struct object *obj, int verbose ) diff --git a/server/serial.c b/server/serial.c index 587fee1..57ba51c 100644 --- a/server/serial.c +++ b/server/serial.c @@ -112,7 +112,8 @@ static const struct fd_ops serial_fd_ops = default_fd_ioctl, /* ioctl */ serial_queue_async, /* queue_async */ default_fd_reselect_async, /* reselect_async */ - default_fd_cancel_async /* cancel_async */ + default_fd_cancel_async, /* cancel_async */ + default_fd_update_async /* update_async */ }; /* check if the given fd is a serial port */ diff --git a/server/signal.c b/server/signal.c index 5e4fe33..79dee43 100644 --- a/server/signal.c +++ b/server/signal.c @@ -90,7 +90,8 @@ static const struct fd_ops handler_fd_ops = NULL, /* ioctl */ NULL, /* queue_async */ NULL, /* reselect_async */ - NULL /* cancel_async */ + NULL, /* cancel_async */ + NULL /* update_async */ }; static struct handler *handler_sighup; diff --git a/server/sock.c b/server/sock.c index 7e4acd8..7d21a1e 100644 --- a/server/sock.c +++ b/server/sock.c @@ -119,6 +119,7 @@ static enum server_fd_type sock_get_fd_type( struct fd *fd ); static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count ); static void sock_reselect_async( struct fd *fd, struct async_queue *queue ); static void sock_cancel_async( struct fd *fd, struct process *process, struct thread *thread, client_ptr_t iosb ); +static void sock_update_async( struct fd *fd ); static int sock_get_ntstatus( int err ); static int sock_get_error( int err ); @@ -153,7 +154,8 @@ static const struct fd_ops sock_fd_ops = default_fd_ioctl, /* ioctl */ sock_queue_async, /* queue_async */ sock_reselect_async, /* reselect_async */ - sock_cancel_async /* cancel_async */ + sock_cancel_async, /* cancel_async */ + sock_update_async /* update_async */ }; @@ -573,6 +575,15 @@ static void sock_cancel_async( struct fd *fd, struct process *process, struct th set_error( STATUS_NOT_FOUND ); } +static void sock_update_async( struct fd *fd ) +{ + struct sock *sock = get_fd_user( fd ); + assert( sock->obj.ops == &sock_ops ); + + async_update( sock->read_q ); + async_update( sock->write_q ); +} + static struct fd *sock_get_fd( struct object *obj ) { struct sock *sock = (struct sock *)obj; diff --git a/server/thread.c b/server/thread.c index f9a575e..3ee9486 100644 --- a/server/thread.c +++ b/server/thread.c @@ -157,7 +157,8 @@ static const struct fd_ops thread_fd_ops = NULL, /* ioctl */ NULL, /* queue_async */ NULL, /* reselect_async */ - NULL /* cancel_async */ + NULL, /* cancel_async */ + NULL /* update_async */ }; static struct list thread_list = LIST_INIT(thread_list); -- 1.7.5.4 From: "Erich E. Hoover" Subject: [PATCH 2/3] server: STATUS_MORE_PROCESSING_REQUIRED does not indicate that an async operation is complete (try 2). Message-Id: Date: Mon, 14 May 2012 16:13:29 -0600 Real Name: Erich Hoover Description: Several MSDN articles indicate that kernel drivers can respond with STATUS_MORE_PROCESSING_REQUIRED to indicate that an IRP is incomplete and that a completion should not be processed. Handling the return value of our async operations in this way permits us to handle completions for closed AcceptEx socket handles (part 3). I realize that my previous attempt at this (using STATUS_ALERTED) was considered to be hackish, but I now have some evidence to support this method and I have added a variety of tests that establish that AcceptEx needs to work this way. Since there isn't enough room to add the completion port and the completion key to apc_call_t and it would be unacceptable to use the handle to the APC to send the completion, I can't see any other way to solve this issue except to have the callback return a value that means "I'm not done yet." Changelog: server: STATUS_MORE_PROCESSING_REQUIRED does not indicate that an async operation is complete. From 44442aada83546eb64a35f35105565d2ed4c1c31 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 14 May 2012 15:41:19 -0600 Subject: server: STATUS_MORE_PROCESSING_REQUIRED does not indicate that an async operation is complete. --- server/async.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/server/async.c b/server/async.c index 01d1ca1..0e6fe05 100644 --- a/server/async.c +++ b/server/async.c @@ -259,7 +259,9 @@ void async_set_result( struct object *obj, unsigned int status, unsigned int tot if (async->timeout) remove_timeout_user( async->timeout ); async->timeout = NULL; async->status = status; - if (async->completion && async->data.cvalue) + if (status == STATUS_MORE_PROCESSING_REQUIRED) + async->status = STATUS_PENDING; + else if (async->completion && async->data.cvalue) add_completion( async->completion, async->comp_key, async->data.cvalue, status, total ); if (apc) { -- 1.7.5.4 From: "Erich E. Hoover" Subject: [PATCH 3/3] ws2_32: Use STATUS_MORE_PROCESSING_REQUIRED to indicate that the AcceptEx async is incomplete (try 3). Message-Id: Date: Mon, 14 May 2012 16:14:24 -0600 Real Name: Erich Hoover Description: This patch changes the way AcceptEx handles completions so that canceled sockets can still send completion notifications. The patch fixes a significant issue with the WoW launcher and the Diablo 3 installer (Bug #28898). Changelog: ws2_32: Use STATUS_MORE_PROCESSING_REQUIRED to indicate that the AcceptEx async is incomplete. From bd886c015470c786e464f92e2ea7b75c84d73b91 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 14 May 2012 15:50:16 -0600 Subject: ws2_32: Use STATUS_MORE_PROCESSING_REQUIRED to indicate that the AcceptEx async is incomplete. --- dlls/ws2_32/socket.c | 8 +++--- dlls/ws2_32/tests/sock.c | 50 +++++++++++++++++++++++----------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 21e084b..007514c 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1706,7 +1706,7 @@ static NTSTATUS WS2_async_accept( void *arg, IO_STATUS_BLOCK *iosb, NTSTATUS sta if (status != STATUS_PENDING) goto finish; - return STATUS_SUCCESS; + return STATUS_MORE_PROCESSING_REQUIRED; finish: iosb->u.Status = status; @@ -1714,8 +1714,6 @@ finish: if (wsa->user_overlapped->hEvent) SetEvent(wsa->user_overlapped->hEvent); - if (wsa->cvalue) - WS_AddCompletion( HANDLE2SOCKET(wsa->listen_socket), wsa->cvalue, iosb->u.Status, iosb->Information ); *apc = ws2_async_accept_apc; return status; @@ -2046,7 +2044,9 @@ static BOOL WINAPI WS2_AcceptEx(SOCKET listener, SOCKET acceptor, PVOID dest, DW req->async.callback = wine_server_client_ptr( WS2_async_accept ); req->async.iosb = wine_server_client_ptr( overlapped ); req->async.arg = wine_server_client_ptr( wsa ); - /* We don't set event or completion since we may also have to read */ + req->async.cvalue = cvalue; + /* We don't set event since we may also have to read, the APC returns STATUS_MORE_PROCESSING_REQUIRED + * to indicate that the async operation is incomplete and that no completion should be queued. */ status = wine_server_call( req ); } SERVER_END_REQ; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index e9814e3..29616b2 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -5494,11 +5494,11 @@ static void test_completion_port(void) bret = GetQueuedCompletionStatus(io_port, &num_bytes, &key, &olp, 100); ok(bret == FALSE, "failed to get completion status %u\n", bret); - todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); - todo_wine ok(key == 125, "Key is %lu\n", key); - todo_wine ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); - todo_wine ok(olp == &ov, "Overlapped structure is at %p\n", olp); - todo_wine ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); + ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); + ok(key == 125, "Key is %lu\n", key); + ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); + ok(olp == &ov, "Overlapped structure is at %p\n", olp); + ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); SetLastError(0xdeadbeef); key = 0xdeadbeef; @@ -5537,11 +5537,11 @@ static void test_completion_port(void) bret = GetQueuedCompletionStatus(io_port, &num_bytes, &key, &olp, 100); ok(bret == FALSE, "failed to get completion status %u\n", bret); - todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); - todo_wine ok(key == 125, "Key is %lu\n", key); - todo_wine ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); - todo_wine ok(olp == &ov, "Overlapped structure is at %p\n", olp); - todo_wine ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); + ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); + ok(key == 125, "Key is %lu\n", key); + ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); + ok(olp == &ov, "Overlapped structure is at %p\n", olp); + ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); SetLastError(0xdeadbeef); key = 0xdeadbeef; @@ -5596,11 +5596,11 @@ static void test_completion_port(void) olp = (WSAOVERLAPPED *)0xdeadbeef; bret = GetQueuedCompletionStatus(io_port, &num_bytes, &key, &olp, 100); ok(bret == FALSE, "failed to get completion status %u\n", bret); - todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); - todo_wine ok(key == 125, "Key is %lu\n", key); - todo_wine ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); - todo_wine ok(olp == &ov, "Overlapped structure is at %p\n", olp); - todo_wine ok(olp && olp->Internal == (ULONG)STATUS_CANCELLED, "Internal status is %lx\n", olp ? olp->Internal : 0); + ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); + ok(key == 125, "Key is %lu\n", key); + ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); + ok(olp == &ov, "Overlapped structure is at %p\n", olp); + ok(olp && olp->Internal == (ULONG)STATUS_CANCELLED, "Internal status is %lx\n", olp ? olp->Internal : 0); SetLastError(0xdeadbeef); key = 0xdeadbeef; @@ -5663,11 +5663,11 @@ static void test_completion_port(void) bret = GetQueuedCompletionStatus(io_port, &num_bytes, &key, &olp, 100); ok(bret == FALSE, "failed to get completion status %u\n", bret); - todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); - todo_wine ok(key == 125, "Key is %lu\n", key); - todo_wine ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); - todo_wine ok(olp == &ov, "Overlapped structure is at %p\n", olp); - todo_wine ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); + ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); + ok(key == 125, "Key is %lu\n", key); + ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); + ok(olp == &ov, "Overlapped structure is at %p\n", olp); + ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); SetLastError(0xdeadbeef); key = 0xdeadbeef; @@ -5719,11 +5719,11 @@ static void test_completion_port(void) bret = GetQueuedCompletionStatus(io_port, &num_bytes, &key, &olp, 100); ok(bret == FALSE, "failed to get completion status %u\n", bret); - todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); - todo_wine ok(key == 125, "Key is %lu\n", key); - todo_wine ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); - todo_wine ok(olp == &ov, "Overlapped structure is at %p\n", olp); - todo_wine ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); + ok(GetLastError() == ERROR_OPERATION_ABORTED, "Last error was %d\n", GetLastError()); + ok(key == 125, "Key is %lu\n", key); + ok(num_bytes == 0, "Number of bytes transferred is %u\n", num_bytes); + ok(olp == &ov, "Overlapped structure is at %p\n", olp); + ok(olp && (olp->Internal == (ULONG)STATUS_CANCELLED), "Internal status is %lx\n", olp ? olp->Internal : 0); SetLastError(0xdeadbeef); key = 0xdeadbeef; -- 1.7.5.4