From 635714397fc25b92d0f8bda1ca9af65bd060ff00 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Mon, 16 Apr 2007 14:45:03 +0200
Subject: [PATCH] server: Add infrastructure for ioctl server request.

---
 dlls/ntdll/file.c              | 68 +++++++++++++++++++++++++++-------
 include/wine/server_protocol.h | 21 ++++++++++-
 server/change.c                |  2 +
 server/fd.c                    | 20 ++++++++++
 server/file.c                  |  1 +
 server/file.h                  |  5 +++
 server/mailslot.c              |  3 ++
 server/named_pipe.c            |  3 ++
 server/process.c               |  1 +
 server/protocol.def            | 11 ++++++
 server/queue.c                 |  1 +
 server/request.c               |  1 +
 server/request.h               |  2 +
 server/serial.c                |  1 +
 server/signal.c                |  1 +
 server/sock.c                  |  1 +
 server/thread.c                |  1 +
 server/trace.c                 | 21 +++++++++++
 18 files changed, 150 insertions(+), 14 deletions(-)

diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index b218b8323ee..b29bb1370ad 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -913,6 +913,48 @@ done:
     return status;
 }
 
+
+/* callback for ioctl async I/O completion */
+static NTSTATUS ioctl_completion( void *arg, IO_STATUS_BLOCK *io, NTSTATUS status )
+{
+    io->u.Status = status;
+    return status;
+}
+
+/* do a ioctl call through the server */
+static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
+                                   PIO_APC_ROUTINE apc, PVOID apc_context,
+                                   IO_STATUS_BLOCK *io, ULONG code,
+                                   PVOID in_buffer, ULONG in_size,
+                                   PVOID out_buffer, ULONG out_size )
+{
+    NTSTATUS status;
+
+    SERVER_START_REQ( ioctl )
+    {
+        req->handle         = handle;
+        req->code           = code;
+        req->async.callback = ioctl_completion;
+        req->async.iosb     = io;
+        req->async.arg      = NULL;
+        req->async.apc      = apc;
+        req->async.apc_arg  = apc_context;
+        req->async.event    = event;
+        wine_server_add_data( req, in_buffer, in_size );
+        wine_server_set_reply( req, out_buffer, out_size );
+        if (!(status = wine_server_call( req )))
+            io->Information = wine_server_reply_size( reply );
+    }
+    SERVER_END_REQ;
+
+    if (status == STATUS_NOT_SUPPORTED)
+        FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
+              code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
+
+    return status;
+}
+
+
 /**************************************************************************
  *		NtDeviceIoControlFile			[NTDLL.@]
  *		ZwDeviceIoControlFile			[NTDLL.@]
@@ -942,6 +984,7 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
                                       PVOID out_buffer, ULONG out_size)
 {
     ULONG device = (code >> 16);
+    NTSTATUS status;
 
     TRACE("(%p,%p,%p,%p,%p,0x%08x,%p,0x%08x,%p,0x%08x)\n",
           handle, event, apc, apc_context, io, code,
@@ -954,24 +997,24 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
     case FILE_DEVICE_DVD:
     case FILE_DEVICE_CONTROLLER:
     case FILE_DEVICE_MASS_STORAGE:
-        io->u.Status = CDROM_DeviceIoControl(handle, event, apc, apc_context, io, code,
-                                             in_buffer, in_size, out_buffer, out_size);
+        status = CDROM_DeviceIoControl(handle, event, apc, apc_context, io, code,
+                                       in_buffer, in_size, out_buffer, out_size);
         break;
     case FILE_DEVICE_SERIAL_PORT:
-        io->u.Status = COMM_DeviceIoControl(handle, event, apc, apc_context, io, code,
-                                            in_buffer, in_size, out_buffer, out_size);
+        status = COMM_DeviceIoControl(handle, event, apc, apc_context, io, code,
+                                      in_buffer, in_size, out_buffer, out_size);
         break;
     case FILE_DEVICE_TAPE:
-        io->u.Status = TAPE_DeviceIoControl(handle, event, apc, apc_context, io, code,
-                                            in_buffer, in_size, out_buffer, out_size);
+        status = TAPE_DeviceIoControl(handle, event, apc, apc_context, io, code,
+                                      in_buffer, in_size, out_buffer, out_size);
         break;
     default:
-        FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
-              code, device, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
-        io->u.Status = STATUS_NOT_SUPPORTED;
+        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
+                                    in_buffer, in_size, out_buffer, out_size );
         break;
     }
-    return io->u.Status;
+    if (status != STATUS_PENDING) io->u.Status = status;
+    return status;
 }
 
 /***********************************************************************
@@ -1170,9 +1213,8 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
         break;
 
     default:
-        FIXME("Unsupported fsctl %x (device=%x access=%x func=%x method=%x)\n",
-              code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
-        status = STATUS_NOT_SUPPORTED;
+        status = server_ioctl_file( handle, event, apc, apc_context, io, code,
+                                    in_buffer, in_size, out_buffer, out_size );
         break;
     }
 
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index fe0f7da8154..a4ee09188dc 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2679,6 +2679,22 @@ struct cancel_async_reply
 
 
 
+struct ioctl_request
+{
+    struct request_header __header;
+    obj_handle_t   handle;
+    unsigned int   code;
+    async_data_t   async;
+    /* VARARG(in_data,bytes); */
+};
+struct ioctl_reply
+{
+    struct reply_header __header;
+    /* VARARG(out_data,bytes); */
+};
+
+
+
 struct create_named_pipe_request
 {
     struct request_header __header;
@@ -4141,6 +4157,7 @@ enum request
     REQ_set_serial_info,
     REQ_register_async,
     REQ_cancel_async,
+    REQ_ioctl,
     REQ_create_named_pipe,
     REQ_connect_named_pipe,
     REQ_wait_named_pipe,
@@ -4365,6 +4382,7 @@ union generic_request
     struct set_serial_info_request set_serial_info_request;
     struct register_async_request register_async_request;
     struct cancel_async_request cancel_async_request;
+    struct ioctl_request ioctl_request;
     struct create_named_pipe_request create_named_pipe_request;
     struct connect_named_pipe_request connect_named_pipe_request;
     struct wait_named_pipe_request wait_named_pipe_request;
@@ -4587,6 +4605,7 @@ union generic_reply
     struct set_serial_info_reply set_serial_info_reply;
     struct register_async_reply register_async_reply;
     struct cancel_async_reply cancel_async_reply;
+    struct ioctl_reply ioctl_reply;
     struct create_named_pipe_reply create_named_pipe_reply;
     struct connect_named_pipe_reply connect_named_pipe_reply;
     struct wait_named_pipe_reply wait_named_pipe_reply;
@@ -4671,6 +4690,6 @@ union generic_reply
     struct allocate_locally_unique_id_reply allocate_locally_unique_id_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 292
+#define SERVER_PROTOCOL_VERSION 293
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/change.c b/server/change.c
index c7d5823b7a9..e996d4999e4 100644
--- a/server/change.c
+++ b/server/change.c
@@ -187,6 +187,7 @@ static const struct fd_ops dir_fd_ops =
     default_poll_event,          /* poll_event */
     no_flush,                    /* flush */
     dir_get_fd_type,             /* get_fd_type */
+    default_fd_ioctl,            /* ioctl */
     default_fd_queue_async,      /* queue_async */
     default_fd_reselect_async,   /* reselect_async */
     default_fd_cancel_async      /* cancel_async */
@@ -520,6 +521,7 @@ static const struct fd_ops inotify_fd_ops =
     inotify_poll_event,          /* poll_event */
     NULL,                        /* flush */
     NULL,                        /* get_fd_type */
+    NULL,                        /* ioctl */
     NULL,                        /* queue_async */
     NULL,                        /* reselect_async */
     NULL,                        /* cancel_async */
diff --git a/server/fd.c b/server/fd.c
index 88fa6a44642..37ddefb7819 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1686,6 +1686,13 @@ void default_poll_event( struct fd *fd, int event )
     else if (!fd->inode) set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
 }
 
+/* default ioctl() routine */
+void default_fd_ioctl( struct fd *fd, unsigned int code, const async_data_t *async,
+                       const void *data, data_size_t size )
+{
+    set_error( STATUS_NOT_SUPPORTED );
+}
+
 struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
 {
     struct async_queue *queue;
@@ -1918,6 +1925,19 @@ DECL_HANDLER(unmount_device)
     }
 }
 
+/* perform an ioctl on a file */
+DECL_HANDLER(ioctl)
+{
+    unsigned int access = (req->code >> 14) & (FILE_READ_DATA|FILE_WRITE_DATA);
+    struct fd *fd = get_handle_fd_obj( current->process, req->handle, access );
+
+    if (fd)
+    {
+        fd->fd_ops->ioctl( fd, req->code, &req->async, get_req_data(), get_req_data_size() );
+        release_object( fd );
+    }
+}
+
 /* create / reschedule an async I/O */
 DECL_HANDLER(register_async)
 {
diff --git a/server/file.c b/server/file.c
index 8e10847550f..a45c340d362 100644
--- a/server/file.c
+++ b/server/file.c
@@ -94,6 +94,7 @@ static const struct fd_ops file_fd_ops =
     default_poll_event,           /* poll_event */
     file_flush,                   /* flush */
     file_get_fd_type,             /* get_fd_type */
+    default_fd_ioctl,             /* ioctl */
     default_fd_queue_async,       /* queue_async */
     default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
diff --git a/server/file.h b/server/file.h
index 5e2e5f01d15..2c54e4d7985 100644
--- a/server/file.h
+++ b/server/file.h
@@ -39,6 +39,9 @@ struct fd_ops
     void (*flush)(struct fd *, struct event **);
     /* get file information */
     enum server_fd_type (*get_fd_type)(struct fd *fd);
+    /* perform an ioctl on the file */
+    void (*ioctl)(struct fd *fd, unsigned int code, const async_data_t *async,
+                  const void *data, data_size_t size);
     /* queue an async operation */
     void (*queue_async)(struct fd *, const async_data_t *data, int type, int count);
     /* selected events for async i/o need an update */
@@ -74,6 +77,8 @@ extern void default_poll_event( struct fd *fd, int event );
 extern struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
 extern void fd_async_wake_up( struct fd *fd, int type, unsigned int status );
 extern void fd_reselect_async( struct fd *fd, struct async_queue *queue );
+extern void default_fd_ioctl( struct fd *fd, unsigned int code, const async_data_t *async,
+                              const void *data, data_size_t size );
 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 );
diff --git a/server/mailslot.c b/server/mailslot.c
index 8872742f2b3..94f7f340a07 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -96,6 +96,7 @@ static const struct fd_ops mailslot_fd_ops =
     default_poll_event,         /* poll_event */
     no_flush,                   /* flush */
     mailslot_get_fd_type,       /* get_fd_type */
+    default_fd_ioctl,           /* ioctl */
     mailslot_queue_async,       /* queue_async */
     default_fd_reselect_async,  /* reselect_async */
     default_fd_cancel_async     /* cancel_async */
@@ -142,6 +143,7 @@ static const struct fd_ops mail_writer_fd_ops =
     default_poll_event,          /* poll_event */
     no_flush,                    /* flush */
     mail_writer_get_fd_type,     /* get_fd_type */
+    default_fd_ioctl,            /* ioctl */
     default_fd_queue_async,      /* queue_async */
     default_fd_reselect_async,   /* reselect_async */
     default_fd_cancel_async      /* cancel_async */
@@ -187,6 +189,7 @@ static const struct fd_ops mailslot_device_fd_ops =
     default_poll_event,             /* poll_event */
     no_flush,                       /* flush */
     mailslot_device_get_fd_type,    /* get_fd_type */
+    default_fd_ioctl,               /* ioctl */
     default_fd_queue_async,         /* queue_async */
     default_fd_reselect_async,      /* reselect_async */
     default_fd_cancel_async         /* cancel_async */
diff --git a/server/named_pipe.c b/server/named_pipe.c
index d5b88a2710e..62c30c4aaba 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -162,6 +162,7 @@ static const struct fd_ops pipe_server_fd_ops =
     default_poll_event,           /* poll_event */
     pipe_server_flush,            /* flush */
     pipe_server_get_fd_type,      /* get_fd_type */
+    default_fd_ioctl,             /* ioctl */
     default_fd_queue_async,       /* queue_async */
     default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async,      /* cancel_async */
@@ -197,6 +198,7 @@ static const struct fd_ops pipe_client_fd_ops =
     default_poll_event,           /* poll_event */
     pipe_client_flush,            /* flush */
     pipe_client_get_fd_type,      /* get_fd_type */
+    default_fd_ioctl,             /* ioctl */
     default_fd_queue_async,       /* queue_async */
     default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
@@ -234,6 +236,7 @@ static const struct fd_ops named_pipe_device_fd_ops =
     default_poll_event,               /* poll_event */
     no_flush,                         /* flush */
     named_pipe_device_get_fd_type,    /* get_fd_type */
+    default_fd_ioctl,                 /* ioctl */
     default_fd_queue_async,           /* queue_async */
     default_fd_reselect_async,        /* reselect_async */
     default_fd_cancel_async           /* cancel_async */
diff --git a/server/process.c b/server/process.c
index 2e9fd594894..fffae5171bc 100644
--- a/server/process.c
+++ b/server/process.c
@@ -86,6 +86,7 @@ static const struct fd_ops process_fd_ops =
     process_poll_event,          /* poll_event */
     NULL,                        /* flush */
     NULL,                        /* get_fd_type */
+    NULL,                        /* ioctl */
     NULL,                        /* queue_async */
     NULL,                        /* reselect_async */
     NULL                         /* cancel async */
diff --git a/server/protocol.def b/server/protocol.def
index 3396d4a0cd2..3ca72412708 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1972,6 +1972,17 @@ enum message_type
 @END
 
 
+/* Perform an ioctl on a file */
+@REQ(ioctl)
+    obj_handle_t   handle;        /* handle to the device */
+    unsigned int   code;          /* ioctl code */
+    async_data_t   async;         /* async I/O parameters */
+    VARARG(in_data,bytes);        /* ioctl input data */
+@REPLY
+    VARARG(out_data,bytes);       /* ioctl output data */
+@END
+
+
 /* Create a named pipe */
 @REQ(create_named_pipe)
     unsigned int   access;
diff --git a/server/queue.c b/server/queue.c
index fa80d62903c..85e08568e32 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -168,6 +168,7 @@ static const struct fd_ops msg_queue_fd_ops =
     msg_queue_poll_event,        /* poll_event */
     NULL,                        /* flush */
     NULL,                        /* get_fd_type */
+    NULL,                        /* ioctl */
     NULL,                        /* queue_async */
     NULL,                        /* reselect_async */
     NULL                         /* cancel async */
diff --git a/server/request.c b/server/request.c
index 404dda04770..4c4036a0a17 100644
--- a/server/request.c
+++ b/server/request.c
@@ -108,6 +108,7 @@ static const struct fd_ops master_socket_fd_ops =
     master_socket_poll_event,      /* poll_event */
     NULL,                          /* flush */
     NULL,                          /* get_fd_type */
+    NULL,                          /* ioctl */
     NULL,                          /* queue_async */
     NULL,                          /* reselect_async */
     NULL                           /* cancel_async */
diff --git a/server/request.h b/server/request.h
index 5b28a7f0772..3424fb3fb1a 100644
--- a/server/request.h
+++ b/server/request.h
@@ -245,6 +245,7 @@ DECL_HANDLER(get_serial_info);
 DECL_HANDLER(set_serial_info);
 DECL_HANDLER(register_async);
 DECL_HANDLER(cancel_async);
+DECL_HANDLER(ioctl);
 DECL_HANDLER(create_named_pipe);
 DECL_HANDLER(connect_named_pipe);
 DECL_HANDLER(wait_named_pipe);
@@ -468,6 +469,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_set_serial_info,
     (req_handler)req_register_async,
     (req_handler)req_cancel_async,
+    (req_handler)req_ioctl,
     (req_handler)req_create_named_pipe,
     (req_handler)req_connect_named_pipe,
     (req_handler)req_wait_named_pipe,
diff --git a/server/serial.c b/server/serial.c
index 4d1853cd36b..d677d171e86 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -107,6 +107,7 @@ static const struct fd_ops serial_fd_ops =
     default_poll_event,           /* poll_event */
     serial_flush,                 /* flush */
     serial_get_fd_type,           /* get_file_info */
+    default_fd_ioctl,             /* ioctl */
     serial_queue_async,           /* queue_async */
     default_fd_reselect_async,    /* reselect_async */
     default_fd_cancel_async       /* cancel_async */
diff --git a/server/signal.c b/server/signal.c
index 11a01799761..fb16c2fbddd 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -84,6 +84,7 @@ static const struct fd_ops handler_fd_ops =
     handler_poll_event,       /* poll_event */
     NULL,                     /* flush */
     NULL,                     /* get_fd_type */
+    NULL,                     /* ioctl */
     NULL,                     /* queue_async */
     NULL,                     /* reselect_async */
     NULL                      /* cancel_async */
diff --git a/server/sock.c b/server/sock.c
index 817c6bd2c3b..29b685e32e1 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -126,6 +126,7 @@ static const struct fd_ops sock_fd_ops =
     sock_poll_event,              /* poll_event */
     no_flush,                     /* flush */
     sock_get_fd_type,             /* get_file_info */
+    default_fd_ioctl,             /* ioctl */
     sock_queue_async,             /* queue_async */
     sock_reselect_async,          /* reselect_async */
     sock_cancel_async             /* cancel_async */
diff --git a/server/thread.c b/server/thread.c
index 2bf031e54e5..8e63e8b2388 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -131,6 +131,7 @@ static const struct fd_ops thread_fd_ops =
     thread_poll_event,          /* poll_event */
     NULL,                       /* flush */
     NULL,                       /* get_fd_type */
+    NULL,                       /* ioctl */
     NULL,                       /* queue_async */
     NULL,                       /* reselect_async */
     NULL                        /* cancel_async */
diff --git a/server/trace.c b/server/trace.c
index 2b42a90808d..84c409e7bde 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2401,6 +2401,23 @@ static void dump_cancel_async_request( const struct cancel_async_request *req )
     fprintf( stderr, " handle=%p", req->handle );
 }
 
+static void dump_ioctl_request( const struct ioctl_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " code=%08x,", req->code );
+    fprintf( stderr, " async=" );
+    dump_async_data( &req->async );
+    fprintf( stderr, "," );
+    fprintf( stderr, " in_data=" );
+    dump_varargs_bytes( cur_size );
+}
+
+static void dump_ioctl_reply( const struct ioctl_reply *req )
+{
+    fprintf( stderr, " out_data=" );
+    dump_varargs_bytes( cur_size );
+}
+
 static void dump_create_named_pipe_request( const struct create_named_pipe_request *req )
 {
     fprintf( stderr, " access=%08x,", req->access );
@@ -3590,6 +3607,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_set_serial_info_request,
     (dump_func)dump_register_async_request,
     (dump_func)dump_cancel_async_request,
+    (dump_func)dump_ioctl_request,
     (dump_func)dump_create_named_pipe_request,
     (dump_func)dump_connect_named_pipe_request,
     (dump_func)dump_wait_named_pipe_request,
@@ -3810,6 +3828,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)0,
     (dump_func)0,
     (dump_func)0,
+    (dump_func)dump_ioctl_reply,
     (dump_func)dump_create_named_pipe_reply,
     (dump_func)0,
     (dump_func)0,
@@ -4030,6 +4049,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "set_serial_info",
     "register_async",
     "cancel_async",
+    "ioctl",
     "create_named_pipe",
     "connect_named_pipe",
     "wait_named_pipe",
@@ -4166,6 +4186,7 @@ static const struct
     { "NOT_A_DIRECTORY",             STATUS_NOT_A_DIRECTORY },
     { "NOT_IMPLEMENTED",             STATUS_NOT_IMPLEMENTED },
     { "NOT_REGISTRY_FILE",           STATUS_NOT_REGISTRY_FILE },
+    { "NOT_SUPPORTED",               STATUS_NOT_SUPPORTED },
     { "NO_DATA_DETECTED",            STATUS_NO_DATA_DETECTED },
     { "NO_IMPERSONATION_TOKEN",      STATUS_NO_IMPERSONATION_TOKEN },
     { "NO_MEMORY",                   STATUS_NO_MEMORY },
-- 
GitLab