FileReplaceCompletionInformation is the officially-documented way of changing or removing completion objects from a file descriptor.
--- Generally, as long as one has already been created with CreateIoCompletionPort, the function will succeed. This is used in some libraries that wrap sockets and use overlapped I/O upon releasing the native handle.
Signed-off-by: Andrew Buck mrelectrify@warsaw-revamped.com --- dlls/ntdll/unix/file.c | 16 ++++++++++++++++ include/wine/server_protocol.h | 22 +++++++++++++++++++++- server/fd.c | 22 ++++++++++++++++++++++ server/protocol.def | 10 ++++++++++ server/request.h | 6 ++++++ server/trace.c | 10 ++++++++++ 6 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index cc8bf0c6e82..ecedf070a05 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4603,6 +4603,22 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, else status = STATUS_INVALID_PARAMETER_3; break;
+ case FileReplaceCompletionInformation: + if (len >= sizeof(FILE_COMPLETION_INFORMATION)) + { + FILE_COMPLETION_INFORMATION *info = ptr; + SERVER_START_REQ( replace_completion_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->chandle = wine_server_obj_handle( info->CompletionPort ); + req->ckey = info->CompletionKey; + status = wine_server_call( req ); + } + SERVER_END_REQ; + } + else status = STATUS_INVALID_PARAMETER_3; + break; + default: FIXME("Unsupported class (%d)\n", class); status = STATUS_NOT_IMPLEMENTED; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 868add58abf..f098f6af0c0 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5066,6 +5066,23 @@ struct set_completion_info_reply
+ +struct replace_completion_info_request +{ + struct request_header __header; + obj_handle_t handle; + apc_param_t ckey; + obj_handle_t chandle; + char __pad_28[4]; +}; +struct replace_completion_info_reply +{ + struct reply_header __header; +}; + + + + struct add_fd_completion_request { struct request_header __header; @@ -5697,6 +5714,7 @@ enum request REQ_remove_completion, REQ_query_completion, REQ_set_completion_info, + REQ_replace_completion_info, REQ_add_fd_completion, REQ_set_fd_completion_mode, REQ_set_fd_disp_info, @@ -5979,6 +5997,7 @@ union generic_request struct remove_completion_request remove_completion_request; struct query_completion_request query_completion_request; struct set_completion_info_request set_completion_info_request; + struct replace_completion_info_request replace_completion_info_request; struct add_fd_completion_request add_fd_completion_request; struct set_fd_completion_mode_request set_fd_completion_mode_request; struct set_fd_disp_info_request set_fd_disp_info_request; @@ -6259,6 +6278,7 @@ union generic_reply struct remove_completion_reply remove_completion_reply; struct query_completion_reply query_completion_reply; struct set_completion_info_reply set_completion_info_reply; + struct replace_completion_info_reply replace_completion_info_reply; struct add_fd_completion_reply add_fd_completion_reply; struct set_fd_completion_mode_reply set_fd_completion_mode_reply; struct set_fd_disp_info_reply set_fd_disp_info_reply; @@ -6288,7 +6308,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 751 +#define SERVER_PROTOCOL_VERSION 752
/* ### protocol_version end ### */
diff --git a/server/fd.c b/server/fd.c index 1b4b98b0e76..00314589825 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2912,6 +2912,28 @@ DECL_HANDLER(set_completion_info) } }
+/* replace completion object for a fd */ +DECL_HANDLER(replace_completion_info) +{ + struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 ); + struct completion *old_completion; + + if (fd) + { + if (is_fd_overlapped( fd ) && fd->completion) + { + old_completion = fd->completion; + if (req->chandle) + fd->completion = get_completion_obj( current->process, req->chandle, IO_COMPLETION_MODIFY_STATE ); + else + fd->completion = 0; + fd->comp_key = req->ckey; + release_object( old_completion ); + } + release_object( fd ); + } +} + /* push new completion msg into a completion queue attached to the fd */ DECL_HANDLER(add_fd_completion) { diff --git a/server/protocol.def b/server/protocol.def index 2be1658fca2..912da634797 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3530,6 +3530,16 @@ struct handle_info @END
+ +/* associate object with completion port */ +@REQ(replace_completion_info) + obj_handle_t handle; /* object handle */ + apc_param_t ckey; /* completion key */ + obj_handle_t chandle; /* port handle */ +@END + + + /* check for associated completion and push msg */ @REQ(add_fd_completion) obj_handle_t handle; /* async' object */ diff --git a/server/request.h b/server/request.h index 7fd63905e0e..64da1c319b1 100644 --- a/server/request.h +++ b/server/request.h @@ -369,6 +369,7 @@ DECL_HANDLER(add_completion); DECL_HANDLER(remove_completion); DECL_HANDLER(query_completion); DECL_HANDLER(set_completion_info); +DECL_HANDLER(replace_completion_info); DECL_HANDLER(add_fd_completion); DECL_HANDLER(set_fd_completion_mode); DECL_HANDLER(set_fd_disp_info); @@ -650,6 +651,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_remove_completion, (req_handler)req_query_completion, (req_handler)req_set_completion_info, + (req_handler)req_replace_completion_info, (req_handler)req_add_fd_completion, (req_handler)req_set_fd_completion_mode, (req_handler)req_set_fd_disp_info, @@ -2146,6 +2148,10 @@ C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, ckey) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_completion_info_request, chandle) == 24 ); C_ASSERT( sizeof(struct set_completion_info_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, ckey) == 16 ); +C_ASSERT( FIELD_OFFSET(struct replace_completion_info_request, chandle) == 24 ); +C_ASSERT( sizeof(struct replace_completion_info_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, cvalue) == 16 ); C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, information) == 24 ); diff --git a/server/trace.c b/server/trace.c index 15ca4e7d71e..15afd7e3217 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4263,6 +4263,13 @@ static void dump_set_completion_info_request( const struct set_completion_info_r fprintf( stderr, ", chandle=%04x", req->chandle ); }
+static void dump_replace_completion_info_request( const struct replace_completion_info_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + dump_uint64( ", ckey=", &req->ckey ); + fprintf( stderr, ", chandle=%04x", req->chandle ); +} + static void dump_add_fd_completion_request( const struct add_fd_completion_request *req ) { fprintf( stderr, " handle=%04x", req->handle ); @@ -4737,6 +4744,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_remove_completion_request, (dump_func)dump_query_completion_request, (dump_func)dump_set_completion_info_request, + (dump_func)dump_replace_completion_info_request, (dump_func)dump_add_fd_completion_request, (dump_func)dump_set_fd_completion_mode_request, (dump_func)dump_set_fd_disp_info_request, @@ -5020,6 +5028,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, NULL, + NULL, (dump_func)dump_get_window_layered_info_reply, NULL, (dump_func)dump_alloc_user_handle_reply, @@ -5293,6 +5302,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "remove_completion", "query_completion", "set_completion_info", + "replace_completion_info", "add_fd_completion", "set_fd_completion_mode", "set_fd_disp_info",
Hello Andrew, thanks for the patch!
Could you please add tests for this functionality, to prove its correctness and protect against future regressions?
I also have one suggestion on the implementation: instead of adding a new server request, perhaps it would make more sense to add a "replace" parameter to the existing set_completion_info request, and thus have a smaller diff to the server interface.