server: Handle the entire IOCTL_AFD_POLL ioctl on the server side.

Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2021-12-10 11:27:39 -06:00 committed by Alexandre Julliard
parent 06fdbd6eda
commit 4b00dd097d
7 changed files with 168 additions and 318 deletions

View file

@ -770,151 +770,6 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
}
struct async_poll_ioctl
{
struct async_fileio io;
unsigned int count;
struct afd_poll_params *input, *output;
struct poll_socket_output sockets[1];
};
static ULONG_PTR fill_poll_output( struct async_poll_ioctl *async, NTSTATUS status )
{
struct afd_poll_params *input = async->input, *output = async->output;
unsigned int i, count = 0;
memcpy( output, input, offsetof( struct afd_poll_params, sockets[0] ) );
if (!status)
{
for (i = 0; i < async->count; ++i)
{
if (async->sockets[i].flags)
{
output->sockets[count].socket = input->sockets[i].socket;
output->sockets[count].flags = async->sockets[i].flags;
output->sockets[count].status = async->sockets[i].status;
++count;
}
}
}
output->count = count;
return offsetof( struct afd_poll_params, sockets[count] );
}
static BOOL async_poll_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
{
struct async_poll_ioctl *async = user;
if (*status == STATUS_ALERTED)
{
SERVER_START_REQ( get_async_result )
{
req->user_arg = wine_server_client_ptr( async );
wine_server_set_reply( req, async->sockets, async->count * sizeof(async->sockets[0]) );
*status = wine_server_call( req );
}
SERVER_END_REQ;
*info = fill_poll_output( async, *status );
}
free( async->input );
release_fileio( &async->io );
return TRUE;
}
/* we could handle this ioctl entirely on the server side, but the differing
* structure size makes it painful */
static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
{
const struct afd_poll_params *params = in_buffer;
struct poll_socket_input *input;
struct async_poll_ioctl *async;
HANDLE wait_handle;
DWORD async_size;
NTSTATUS status;
unsigned int i;
ULONG options;
if (in_size < sizeof(*params) || out_size < in_size || !params->count
|| in_size < offsetof( struct afd_poll_params, sockets[params->count] ))
return STATUS_INVALID_PARAMETER;
TRACE( "timeout %s, count %u, exclusive %#x, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n",
wine_dbgstr_longlong(params->timeout), params->count, params->exclusive,
params->padding[0], params->padding[1], params->padding[2],
params->sockets[0].socket, params->sockets[0].flags );
if (params->padding[0]) FIXME( "padding[0] is %#x\n", params->padding[0] );
if (params->padding[1]) FIXME( "padding[1] is %#x\n", params->padding[1] );
if (params->padding[2]) FIXME( "padding[2] is %#x\n", params->padding[2] );
for (i = 0; i < params->count; ++i)
{
if (params->sockets[i].flags & ~0x1ff)
FIXME( "unknown socket flags %#x\n", params->sockets[i].flags );
}
if (!(input = malloc( params->count * sizeof(*input) )))
return STATUS_NO_MEMORY;
async_size = offsetof( struct async_poll_ioctl, sockets[params->count] );
if (!(async = (struct async_poll_ioctl *)alloc_fileio( async_size, async_poll_proc, handle )))
{
free( input );
return STATUS_NO_MEMORY;
}
if (!(async->input = malloc( in_size )))
{
release_fileio( &async->io );
free( input );
return STATUS_NO_MEMORY;
}
memcpy( async->input, in_buffer, in_size );
async->count = params->count;
async->output = out_buffer;
for (i = 0; i < params->count; ++i)
{
input[i].socket = params->sockets[i].socket;
input[i].flags = params->sockets[i].flags;
}
SERVER_START_REQ( poll_socket )
{
req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) );
req->exclusive = !!params->exclusive;
req->timeout = params->timeout;
wine_server_add_data( req, input, params->count * sizeof(*input) );
wine_server_set_reply( req, async->sockets, params->count * sizeof(async->sockets[0]) );
status = wine_server_call( req );
wait_handle = wine_server_ptr_handle( reply->wait );
options = reply->options;
if (wait_handle && status != STATUS_PENDING)
{
io->Status = status;
io->Information = fill_poll_output( async, status );
}
}
SERVER_END_REQ;
free( input );
if (status != STATUS_PENDING)
{
free( async->input );
release_fileio( &async->io );
}
if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
return status;
}
static NTSTATUS try_send( int fd, struct async_send_ioctl *async )
{
union unix_sockaddr unix_addr;
@ -1374,6 +1229,10 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
status = STATUS_BAD_DEVICE_TYPE;
break;
case IOCTL_AFD_POLL:
status = STATUS_BAD_DEVICE_TYPE;
break;
case IOCTL_AFD_RECV:
{
struct afd_recv_params params;
@ -1516,10 +1375,6 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
break;
}
case IOCTL_AFD_POLL:
status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size );
break;
case IOCTL_AFD_WINE_FIONREAD:
{
int value, ret;

View file

@ -122,13 +122,41 @@ struct afd_poll_params
unsigned int count;
BOOLEAN exclusive;
BOOLEAN padding[3];
struct
struct afd_poll_socket
{
SOCKET socket;
int flags;
NTSTATUS status;
} sockets[1];
};
struct afd_poll_params_64
{
LONGLONG timeout;
unsigned int count;
BOOLEAN exclusive;
BOOLEAN padding[3];
struct afd_poll_socket_64
{
ULONGLONG socket;
int flags;
NTSTATUS status;
} sockets[1];
};
struct afd_poll_params_32
{
LONGLONG timeout;
unsigned int count;
BOOLEAN exclusive;
BOOLEAN padding[3];
struct afd_poll_socket_32
{
ULONG socket;
int flags;
NTSTATUS status;
} sockets[1];
};
#include <poppack.h>
struct afd_event_select_params

View file

@ -1759,36 +1759,6 @@ struct recv_socket_reply
};
struct poll_socket_input
{
obj_handle_t socket;
int flags;
};
struct poll_socket_output
{
int flags;
unsigned int status;
};
struct poll_socket_request
{
struct request_header __header;
int exclusive;
async_data_t async;
timeout_t timeout;
/* VARARG(sockets,poll_socket_input); */
};
struct poll_socket_reply
{
struct reply_header __header;
obj_handle_t wait;
unsigned int options;
/* VARARG(sockets,poll_socket_output); */
};
struct send_socket_request
{
@ -5493,7 +5463,6 @@ enum request
REQ_lock_file,
REQ_unlock_file,
REQ_recv_socket,
REQ_poll_socket,
REQ_send_socket,
REQ_get_next_console_request,
REQ_read_directory_changes,
@ -5775,7 +5744,6 @@ union generic_request
struct lock_file_request lock_file_request;
struct unlock_file_request unlock_file_request;
struct recv_socket_request recv_socket_request;
struct poll_socket_request poll_socket_request;
struct send_socket_request send_socket_request;
struct get_next_console_request_request get_next_console_request_request;
struct read_directory_changes_request read_directory_changes_request;
@ -6055,7 +6023,6 @@ union generic_reply
struct lock_file_reply lock_file_reply;
struct unlock_file_reply unlock_file_reply;
struct recv_socket_reply recv_socket_reply;
struct poll_socket_reply poll_socket_reply;
struct send_socket_reply send_socket_reply;
struct get_next_console_request_reply get_next_console_request_reply;
struct read_directory_changes_reply read_directory_changes_reply;
@ -6279,7 +6246,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 738
#define SERVER_PROTOCOL_VERSION 739
/* ### protocol_version end ### */

View file

@ -1443,31 +1443,6 @@ enum server_fd_type
@END
struct poll_socket_input
{
obj_handle_t socket; /* socket handle */
int flags; /* events to poll for */
};
struct poll_socket_output
{
int flags; /* events signaled */
unsigned int status; /* socket status */
};
/* Perform an async poll on a socket */
@REQ(poll_socket)
int exclusive; /* is the poll exclusive? */
async_data_t async; /* async I/O parameters */
timeout_t timeout; /* timeout */
VARARG(sockets,poll_socket_input); /* list of sockets to poll */
@REPLY
obj_handle_t wait; /* handle to wait on for blocking poll */
unsigned int options; /* file open options */
VARARG(sockets,poll_socket_output); /* data returned */
@END
/* Perform a send on a socket */
@REQ(send_socket)
async_data_t async; /* async I/O parameters */

View file

@ -174,7 +174,6 @@ DECL_HANDLER(get_volume_info);
DECL_HANDLER(lock_file);
DECL_HANDLER(unlock_file);
DECL_HANDLER(recv_socket);
DECL_HANDLER(poll_socket);
DECL_HANDLER(send_socket);
DECL_HANDLER(get_next_console_request);
DECL_HANDLER(read_directory_changes);
@ -455,7 +454,6 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_lock_file,
(req_handler)req_unlock_file,
(req_handler)req_recv_socket,
(req_handler)req_poll_socket,
(req_handler)req_send_socket,
(req_handler)req_get_next_console_request,
(req_handler)req_read_directory_changes,
@ -1050,13 +1048,6 @@ C_ASSERT( sizeof(struct recv_socket_request) == 64 );
C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 );
C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 );
C_ASSERT( sizeof(struct recv_socket_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct poll_socket_request, exclusive) == 12 );
C_ASSERT( FIELD_OFFSET(struct poll_socket_request, async) == 16 );
C_ASSERT( FIELD_OFFSET(struct poll_socket_request, timeout) == 56 );
C_ASSERT( sizeof(struct poll_socket_request) == 64 );
C_ASSERT( FIELD_OFFSET(struct poll_socket_reply, wait) == 8 );
C_ASSERT( FIELD_OFFSET(struct poll_socket_reply, options) == 12 );
C_ASSERT( sizeof(struct poll_socket_reply) == 16 );
C_ASSERT( FIELD_OFFSET(struct send_socket_request, async) == 16 );
C_ASSERT( FIELD_OFFSET(struct send_socket_request, status) == 56 );
C_ASSERT( FIELD_OFFSET(struct send_socket_request, total) == 60 );

View file

@ -122,13 +122,16 @@ struct poll_req
struct async *async;
struct iosb *iosb;
struct timeout_user *timeout;
timeout_t orig_timeout;
int exclusive;
unsigned int count;
struct poll_socket_output *output;
struct
{
struct sock *sock;
int mask;
obj_handle_t handle;
int flags;
unsigned int status;
} sockets[1];
};
@ -235,6 +238,8 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock );
static struct sock *accept_socket( struct sock *sock );
static int sock_get_ntstatus( int err );
static unsigned int sock_get_error( int err );
static void poll_socket( struct sock *poll_sock, struct async *async, int exclusive, timeout_t timeout,
unsigned int count, const struct afd_poll_socket_64 *sockets );
static const struct object_ops sock_ops =
{
@ -789,7 +794,6 @@ static void free_poll_req( void *private )
release_object( req->async );
release_object( req->iosb );
list_remove( &req->entry );
free( req->output );
free( req );
}
@ -832,8 +836,7 @@ static int get_poll_flags( struct sock *sock, int event )
static void complete_async_poll( struct poll_req *req, unsigned int status )
{
struct poll_socket_output *output = req->output;
unsigned int i;
unsigned int i, signaled_count = 0;
for (i = 0; i < req->count; ++i)
{
@ -843,9 +846,65 @@ static void complete_async_poll( struct poll_req *req, unsigned int status )
sock->main_poll = NULL;
}
/* pass 0 as result; client will set actual result size */
req->output = NULL;
async_request_complete( req->async, status, 0, req->count * sizeof(*output), output );
if (!status)
{
for (i = 0; i < req->count; ++i)
{
if (req->sockets[i].flags)
++signaled_count;
}
}
if (is_machine_64bit( async_get_thread( req->async )->process->machine ))
{
size_t output_size = offsetof( struct afd_poll_params_64, sockets[signaled_count] );
struct afd_poll_params_64 *output;
if (!(output = mem_alloc( output_size )))
{
async_terminate( req->async, get_error() );
return;
}
memset( output, 0, output_size );
output->timeout = req->orig_timeout;
output->exclusive = req->exclusive;
for (i = 0; i < req->count; ++i)
{
if (!req->sockets[i].flags) continue;
output->sockets[output->count].socket = req->sockets[i].handle;
output->sockets[output->count].flags = req->sockets[i].flags;
output->sockets[output->count].status = req->sockets[i].status;
++output->count;
}
assert( output->count == signaled_count );
async_request_complete( req->async, status, output_size, output_size, output );
}
else
{
size_t output_size = offsetof( struct afd_poll_params_32, sockets[signaled_count] );
struct afd_poll_params_32 *output;
if (!(output = mem_alloc( output_size )))
{
async_terminate( req->async, get_error() );
return;
}
memset( output, 0, output_size );
output->timeout = req->orig_timeout;
output->exclusive = req->exclusive;
for (i = 0; i < req->count; ++i)
{
if (!req->sockets[i].flags) continue;
output->sockets[output->count].socket = req->sockets[i].handle;
output->sockets[output->count].flags = req->sockets[i].flags;
output->sockets[output->count].status = req->sockets[i].status;
++output->count;
}
assert( output->count == signaled_count );
async_request_complete( req->async, status, output_size, output_size, output );
}
}
static void complete_async_polls( struct sock *sock, int event, int error )
@ -868,8 +927,8 @@ static void complete_async_polls( struct sock *sock, int event, int error )
fprintf( stderr, "completing poll for socket %p, wanted %#x got %#x\n",
sock, req->sockets[i].mask, flags );
req->output[i].flags = req->sockets[i].mask & flags;
req->output[i].status = sock_get_ntstatus( error );
req->sockets[i].flags = req->sockets[i].mask & flags;
req->sockets[i].status = sock_get_ntstatus( error );
complete_async_poll( req, STATUS_SUCCESS );
break;
@ -1353,8 +1412,8 @@ static int sock_close_handle( struct object *obj, struct process *process, obj_h
if (poll_req->sockets[i].sock == sock)
{
signaled = TRUE;
poll_req->output[i].flags = AFD_POLL_CLOSE;
poll_req->output[i].status = 0;
poll_req->sockets[i].flags = AFD_POLL_CLOSE;
poll_req->sockets[i].status = 0;
}
}
@ -2849,6 +2908,55 @@ static void sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
return;
}
case IOCTL_AFD_POLL:
{
if (get_reply_max_size() < get_req_data_size())
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (is_machine_64bit( current->process->machine ))
{
const struct afd_poll_params_64 *params = get_req_data();
if (get_req_data_size() < sizeof(struct afd_poll_params_64) ||
get_req_data_size() < offsetof( struct afd_poll_params_64, sockets[params->count] ))
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
poll_socket( sock, async, params->exclusive, params->timeout, params->count, params->sockets );
}
else
{
const struct afd_poll_params_32 *params = get_req_data();
struct afd_poll_socket_64 *sockets;
unsigned int i;
if (get_req_data_size() < sizeof(struct afd_poll_params_32) ||
get_req_data_size() < offsetof( struct afd_poll_params_32, sockets[params->count] ))
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (!(sockets = mem_alloc( params->count * sizeof(*sockets) ))) return;
for (i = 0; i < params->count; ++i)
{
sockets[i].socket = params->sockets[i].socket;
sockets[i].flags = params->sockets[i].flags;
sockets[i].status = params->sockets[i].status;
}
poll_socket( sock, async, params->exclusive, params->timeout, params->count, sockets );
free( sockets );
}
return;
}
default:
set_error( STATUS_NOT_SUPPORTED );
return;
@ -2899,51 +3007,49 @@ static void handle_exclusive_poll(struct poll_req *req)
}
static void poll_socket( struct sock *poll_sock, struct async *async, int exclusive, timeout_t timeout,
unsigned int count, const struct poll_socket_input *input )
unsigned int count, const struct afd_poll_socket_64 *sockets )
{
struct poll_socket_output *output;
BOOL signaled = FALSE;
struct poll_req *req;
unsigned int i, j;
if (!(output = mem_alloc( count * sizeof(*output) )))
return;
memset( output, 0, count * sizeof(*output) );
if (!(req = mem_alloc( offsetof( struct poll_req, sockets[count] ) )))
if (!count)
{
free( output );
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (!(req = mem_alloc( offsetof( struct poll_req, sockets[count] ) )))
return;
req->timeout = NULL;
if (timeout && timeout != TIMEOUT_INFINITE &&
!(req->timeout = add_timeout_user( timeout, async_poll_timeout, req )))
{
free( req );
free( output );
return;
}
req->orig_timeout = timeout;
for (i = 0; i < count; ++i)
{
req->sockets[i].sock = (struct sock *)get_handle_obj( current->process, input[i].socket, 0, &sock_ops );
req->sockets[i].sock = (struct sock *)get_handle_obj( current->process, sockets[i].socket, 0, &sock_ops );
if (!req->sockets[i].sock)
{
for (j = 0; j < i; ++j) release_object( req->sockets[i].sock );
if (req->timeout) remove_timeout_user( req->timeout );
free( req );
free( output );
return;
}
req->sockets[i].mask = input[i].flags;
req->sockets[i].handle = sockets[i].socket;
req->sockets[i].mask = sockets[i].flags;
req->sockets[i].flags = 0;
}
req->exclusive = exclusive;
req->count = count;
req->async = (struct async *)grab_object( async );
req->iosb = async_get_iosb( async );
req->output = output;
handle_exclusive_poll(req);
@ -2960,16 +3066,16 @@ static void poll_socket( struct sock *poll_sock, struct async *async, int exclus
if (flags)
{
signaled = TRUE;
output[i].flags = flags;
output[i].status = sock_get_ntstatus( sock_error( sock->fd ) );
req->sockets[i].flags = flags;
req->sockets[i].status = sock_get_ntstatus( sock_error( sock->fd ) );
}
/* FIXME: do other error conditions deserve a similar treatment? */
if (sock->state != SOCK_CONNECTING && sock->errors[AFD_POLL_BIT_CONNECT_ERR] && (mask & AFD_POLL_CONNECT_ERR))
{
signaled = TRUE;
output[i].flags |= AFD_POLL_CONNECT_ERR;
output[i].status = sock_get_ntstatus( sock->errors[AFD_POLL_BIT_CONNECT_ERR] );
req->sockets[i].flags |= AFD_POLL_CONNECT_ERR;
req->sockets[i].status = sock_get_ntstatus( sock->errors[AFD_POLL_BIT_CONNECT_ERR] );
}
}
@ -3335,28 +3441,6 @@ DECL_HANDLER(recv_socket)
release_object( sock );
}
DECL_HANDLER(poll_socket)
{
struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
const struct poll_socket_input *input = get_req_data();
struct async *async;
unsigned int count;
if (!sock) return;
count = get_req_data_size() / sizeof(*input);
if ((async = create_request_async( sock->fd, get_fd_comp_flags( sock->fd ), &req->async )))
{
poll_socket( sock, async, req->exclusive, req->timeout, count, input );
reply->wait = async_handoff( async, NULL, 0 );
reply->options = get_fd_options( sock->fd );
release_object( async );
}
release_object( sock );
}
DECL_HANDLER(send_socket)
{
struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );

View file

@ -1394,38 +1394,6 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size )
fputc( '}', stderr );
}
static void dump_varargs_poll_socket_input( const char *prefix, data_size_t size )
{
const struct poll_socket_input *input;
fprintf( stderr, "%s{", prefix );
while (size >= sizeof(*input))
{
input = cur_data;
fprintf( stderr, "{socket=%04x,flags=%08x}", input->socket, input->flags );
size -= sizeof(*input);
remove_data( sizeof(*input) );
if (size) fputc( ',', stderr );
}
fputc( '}', stderr );
}
static void dump_varargs_poll_socket_output( const char *prefix, data_size_t size )
{
const struct poll_socket_output *output;
fprintf( stderr, "%s{", prefix );
while (size >= sizeof(*output))
{
output = cur_data;
fprintf( stderr, "{flags=%08x,status=%s}", output->flags, get_status_name( output->status ) );
size -= sizeof(*output);
remove_data( sizeof(*output) );
if (size) fputc( ',', stderr );
}
fputc( '}', stderr );
}
typedef void (*dump_func)( const void *req );
/* Everything below this line is generated automatically by tools/make_requests */
@ -2119,21 +2087,6 @@ static void dump_recv_socket_reply( const struct recv_socket_reply *req )
fprintf( stderr, ", options=%08x", req->options );
}
static void dump_poll_socket_request( const struct poll_socket_request *req )
{
fprintf( stderr, " exclusive=%d", req->exclusive );
dump_async_data( ", async=", &req->async );
dump_timeout( ", timeout=", &req->timeout );
dump_varargs_poll_socket_input( ", sockets=", cur_size );
}
static void dump_poll_socket_reply( const struct poll_socket_reply *req )
{
fprintf( stderr, " wait=%04x", req->wait );
fprintf( stderr, ", options=%08x", req->options );
dump_varargs_poll_socket_output( ", sockets=", cur_size );
}
static void dump_send_socket_request( const struct send_socket_request *req )
{
dump_async_data( " async=", &req->async );
@ -4618,7 +4571,6 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_lock_file_request,
(dump_func)dump_unlock_file_request,
(dump_func)dump_recv_socket_request,
(dump_func)dump_poll_socket_request,
(dump_func)dump_send_socket_request,
(dump_func)dump_get_next_console_request_request,
(dump_func)dump_read_directory_changes_request,
@ -4896,7 +4848,6 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_lock_file_reply,
NULL,
(dump_func)dump_recv_socket_reply,
(dump_func)dump_poll_socket_reply,
(dump_func)dump_send_socket_reply,
(dump_func)dump_get_next_console_request_reply,
NULL,
@ -5174,7 +5125,6 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"lock_file",
"unlock_file",
"recv_socket",
"poll_socket",
"send_socket",
"get_next_console_request",
"read_directory_changes",