diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index 93d72a6..3ad1284 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -142,6 +142,56 @@ static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs ) return EXCEPTION_CONTINUE_SEARCH; } +/* process an open request for a given device */ +static NTSTATUS process_open( DEVICE_OBJECT *device, ULONG access, ULONG sharing, + ULONG options ) +{ + IRP irp; + IO_STACK_LOCATION irpsp; + IO_SECURITY_CONTEXT security; + PDRIVER_DISPATCH dispatch = device->DriverObject->MajorFunction[IRP_MJ_CREATE]; + NTSTATUS status; + LARGE_INTEGER count; + + TRACE( "open device %p access %x sharing %x options %x\n", device, access, + access, sharing, options ); + + /* so we can spot things that we should initialize */ + memset( &irp, 0x55, sizeof(irp) ); + memset( &irpsp, 0x66, sizeof(irpsp) ); + memset( &security, 0x77, sizeof(security) ); + + irp.RequestorMode = UserMode; + irp.Tail.Overlay.s.u.CurrentStackLocation = &irpsp; + irp.UserIosb = NULL; + + irpsp.MajorFunction = IRP_MJ_CREATE; + irpsp.Create.SecurityContext = &security; + security.DesiredAccess = access; + irpsp.Create.Options = options; + irpsp.Create.ShareAccess = sharing; + irpsp.DeviceObject = device; + irpsp.CompletionRoutine = NULL; + + device->CurrentIrp = &irp; + + KeQueryTickCount( &count ); /* update the global KeTickCount */ + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Call driver dispatch %p (device=%p,irp=%p)\n", + GetCurrentThreadId(), dispatch, device, &irp ); + + status = dispatch( device, &irp ); + + if (TRACE_ON(relay)) + DPRINTF( "%04x:Ret driver dispatch %p (device=%p,irp=%p) retval=%08x\n", + GetCurrentThreadId(), dispatch, device, &irp, status ); + + *out_size = (irp.IoStatus.u.Status >= 0) ? irp.IoStatus.Information : 0; + return irp.IoStatus.u.Status; + +} + /* process an ioctl request for a given device */ static NTSTATUS process_ioctl( DEVICE_OBJECT *device, ULONG code, void *in_buff, ULONG in_size, void *out_buff, ULONG *out_size ) @@ -206,12 +256,14 @@ static NTSTATUS process_ioctl( DEVICE_OBJECT *device, ULONG code, void *in_buff, NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) { HANDLE manager = get_device_manager(); - obj_handle_t ioctl = 0; + obj_handle_t call = 0; + enum server_call_type type = CALL_OPEN; NTSTATUS status = STATUS_SUCCESS; ULONG code = 0; void *in_buff, *out_buff = NULL; DEVICE_OBJECT *device = NULL; ULONG in_size = 4096, out_size = 0; + ULONG access, sharing, options; HANDLE handles[2]; if (!(in_buff = HeapAlloc( GetProcessHeap(), 0, in_size ))) @@ -228,21 +280,35 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) SERVER_START_REQ( get_next_device_request ) { req->manager = wine_server_obj_handle( manager ); - req->prev = ioctl; + req->prev = call; req->status = status; - wine_server_add_data( req, out_buff, out_size ); - wine_server_set_reply( req, in_buff, in_size ); + if (type == CALL_IOCTL) + { + wine_server_add_data( req, out_buff, out_size ); + wine_server_set_reply( req, in_buff, in_size ); + } if (!(status = wine_server_call( req ))) { - code = reply->code; - ioctl = reply->next; + call = reply->next; + type = reply->type; device = wine_server_get_ptr( reply->user_ptr ); - in_size = reply->in_size; - out_size = reply->out_size; + switch(type) + { + case CALL_OPEN: + access = reply->data.open.access; + sharing = reply->data.open.sharing; + options = reply->data.open.options; + break; + case CALL_IOCTL: + code = reply->data.ioctl.code; + in_size = reply->data.ioctl.in_size; + out_size = reply->data.ioctl.out_size; + break; + } } else { - ioctl = 0; /* no previous ioctl */ + call = 0; /* no previous call */ out_size = 0; in_size = reply->in_size; } @@ -255,7 +321,15 @@ NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event ) HeapFree( GetProcessHeap(), 0, out_buff ); if (out_size) out_buff = HeapAlloc( GetProcessHeap(), 0, out_size ); else out_buff = NULL; - status = process_ioctl( device, code, in_buff, in_size, out_buff, &out_size ); + switch(type) + { + case CALL_OPEN: + status = process_open( device, access, sharing, options ); + break; + case CALL_IOCTL: + status = process_ioctl( device, code, in_buff, in_size, out_buff, &out_size ); + break; + } break; case STATUS_BUFFER_OVERFLOW: HeapFree( GetProcessHeap(), 0, in_buff ); diff --git a/server/device.c b/server/device.c index 4d134a3..cc772c9 100644 --- a/server/device.c +++ b/server/device.c @@ -33,17 +33,61 @@ #include "handle.h" #include "request.h" -struct ioctl_call +int suspend_thread( struct thread *thread ); +int resume_thread( struct thread *thread ); + +struct call_object { struct object obj; /* object header */ + enum server_call_type type; /* type of driver call */ struct list dev_entry; /* entry in device queue */ struct list mgr_entry; /* entry in manager queue */ - struct device *device; /* device containing this ioctl */ - struct thread *thread; /* thread that queued the ioctl */ + struct device *device; /* device being opened */ + struct thread *thread; /* thread that opened the device */ client_ptr_t user_arg; /* user arg used to identify the request */ + unsigned int status; /* resulting status (or STATUS_PENDING) */ +}; + +static int call_object_signaled( struct object *obj, struct thread *thread ); +static void call_object_destroy( struct object *obj ); + +struct open_call +{ + struct call_object call; /* object header */ + unsigned int access; /* desired access */ + unsigned int sharing; /* desired sharing */ + unsigned int options; /* create options */ +}; + +static void open_call_dump( struct object *obj, int verbose ); +static void open_call_destroy( struct object *obj ); + +static const struct object_ops open_call_ops = +{ + sizeof(struct open_call), /* size */ + open_call_dump, /* dump */ + no_get_type, /* get_type */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + call_object_signaled, /* signaled */ + no_satisfied, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + no_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_lookup_name, /* lookup_name */ + no_open_file, /* open_file */ + no_close_handle, /* close_handle */ + open_call_destroy /* destroy */ +}; + + +struct ioctl_call +{ + struct call_object obj; /* object header */ struct async *async; /* pending async op */ ioctl_code_t code; /* ioctl code */ - unsigned int status; /* resulting status (or STATUS_PENDING) */ data_size_t in_size; /* size of input data */ void *in_data; /* input data */ data_size_t out_size; /* size of output data */ @@ -51,7 +95,6 @@ struct ioctl_call }; static void ioctl_call_dump( struct object *obj, int verbose ); -static int ioctl_call_signaled( struct object *obj, struct thread *thread ); static void ioctl_call_destroy( struct object *obj ); static const struct object_ops ioctl_call_ops = @@ -61,7 +104,7 @@ static const struct object_ops ioctl_call_ops = no_get_type, /* get_type */ add_queue, /* add_queue */ remove_queue, /* remove_queue */ - ioctl_call_signaled, /* signaled */ + call_object_signaled, /* signaled */ no_satisfied, /* satisfied */ no_signal, /* signal */ no_get_fd, /* get_fd */ @@ -160,17 +203,68 @@ static const struct fd_ops device_fd_ops = }; -static void ioctl_call_dump( struct object *obj, int verbose ) +static int call_object_signaled( struct object *obj, struct thread *thread ) { - struct ioctl_call *ioctl = (struct ioctl_call *)obj; - fprintf( stderr, "Ioctl call code=%08x device=%p\n", ioctl->code, ioctl->device ); + struct call_object *call = (struct call_object *)obj; + + return !call->device; /* device is cleared once the ioctl has completed */ } -static int ioctl_call_signaled( struct object *obj, struct thread *thread ) +static void call_object_destroy( struct call_object *obj ) { - struct ioctl_call *ioctl = (struct ioctl_call *)obj; + if (obj->device) release_object( obj->device ); + release_object( obj->thread ); +} + + +static void open_call_dump( struct object *obj, int verbose ) +{ + struct open_call *open = (struct open_call *)obj; + fprintf( stderr, "Open call device=%p\n", open->call.device ); +} + +static void open_call_destroy( struct object *obj ) +{ + struct open_call *open = (struct open_call *)obj; - return !ioctl->device; /* device is cleared once the ioctl has completed */ + call_object_destroy( &open->call ); +} + +static struct open_call *create_open( struct device *device, unsigned int access, + unsigned int sharing, unsigned int options ) +{ + struct open_call *open; + + if ((open = alloc_object( &open_call_ops ))) + { + open->call.type = CALL_OPEN; + open->call.device = (struct device *)grab_object( device ); + open->access = access; + open->sharing = sharing; + open->options = options; + } + return ioctl; +} + +static void complete_open( struct open_call *open, unsigned int status ) +{ + struct device *device = open->device; + + if (!device) return; /* already finished */ + + open->call.status = status; + resume_thread( current ); + + /* remove it from the device queue */ + list_remove( &open->call.dev_entry ); + release_object( open ); /* no longer on the device queue */ +} + + +static void ioctl_call_dump( struct object *obj, int verbose ) +{ + struct ioctl_call *ioctl = (struct ioctl_call *)obj; + fprintf( stderr, "Ioctl call code=%08x device=%p\n", ioctl->code, ioctl->cal.device ); } static void ioctl_call_destroy( struct object *obj ) @@ -184,8 +278,7 @@ static void ioctl_call_destroy( struct object *obj ) async_terminate( ioctl->async, STATUS_CANCELLED ); release_object( ioctl->async ); } - if (ioctl->device) release_object( ioctl->device ); - release_object( ioctl->thread ); + call_object_destroy( &ioctl->call ); } static struct ioctl_call *create_ioctl( struct device *device, ioctl_code_t code, @@ -196,14 +289,15 @@ static struct ioctl_call *create_ioctl( struct device *device, ioctl_code_t code if ((ioctl = alloc_object( &ioctl_call_ops ))) { - ioctl->device = (struct device *)grab_object( device ); - ioctl->code = code; - ioctl->async = NULL; - ioctl->status = STATUS_PENDING; - ioctl->in_size = in_size; - ioctl->in_data = NULL; - ioctl->out_size = out_size; - ioctl->out_data = NULL; + ioctl->call.type = CALL_IOCTL; + ioctl->call.device = (struct device *)grab_object( device ); + ioctl->code = code; + ioctl->async = NULL; + ioctl->call.status = STATUS_PENDING; + ioctl->in_size = in_size; + ioctl->in_data = NULL; + ioctl->out_size = out_size; + ioctl->out_data = NULL; if (ioctl->in_size && !(ioctl->in_data = memdup( in_data, in_size ))) { @@ -222,7 +316,7 @@ static void set_ioctl_result( struct ioctl_call *ioctl, unsigned int status, if (!device) return; /* already finished */ /* FIXME: handle the STATUS_PENDING case */ - ioctl->status = status; + ioctl->call.status = status; ioctl->out_size = min( ioctl->out_size, out_size ); if (ioctl->out_size && !(ioctl->out_data = memdup( out_data, ioctl->out_size ))) ioctl->out_size = 0; @@ -273,12 +367,12 @@ static struct fd *device_get_fd( struct object *obj ) static void device_destroy( struct object *obj ) { struct device *device = (struct device *)obj; - struct ioctl_call *ioctl, *next; + struct call_object *obj, *next; - LIST_FOR_EACH_ENTRY_SAFE( ioctl, next, &device->requests, struct ioctl_call, dev_entry ) + LIST_FOR_EACH_ENTRY_SAFE( obj, next, &device->requests, struct call_object, dev_entry ) { - list_remove( &ioctl->dev_entry ); - release_object( ioctl ); /* no longer on the device queue */ + list_remove( &obj->dev_entry ); + release_object( obj ); /* no longer on the device queue */ } if (device->fd) release_object( device->fd ); if (device->manager) list_remove( &device->entry ); @@ -287,6 +381,26 @@ static void device_destroy( struct object *obj ) static struct object *device_open_file( struct object *obj, unsigned int access, unsigned int sharing, unsigned int options ) { + struct device *device = (struct device *)obj; + struct open_call *open; + + if (!device->manager) /* it has been deleted */ + { + set_error( STATUS_FILE_DELETED ); + return 0; + } + + /* create an open request */ + if (!(open = create_open( device, access, sharing, options ))) + return NULL; + open->call.thread = current; + suspend_thread( current ); + + list_add_tail( &device->requests, &open->call.dev_entry ); + list_add_tail( &device->manager->requests, &open->call.mgr_entry ); + if (list_head( &device->manager->requests ) == &open->call.mgr_entry) /* first one */ + wake_up( &device->manager->obj, 0 ); + return grab_object( obj ); } @@ -298,10 +412,10 @@ static enum server_fd_type device_get_fd_type( struct fd *fd ) static struct ioctl_call *find_ioctl_call( struct device *device, struct thread *thread, client_ptr_t user_arg ) { - struct ioctl_call *ioctl; + struct call_object *ioctl; - LIST_FOR_EACH_ENTRY( ioctl, &device->requests, struct ioctl_call, dev_entry ) - if (ioctl->thread == thread && ioctl->user_arg == user_arg) return ioctl; + LIST_FOR_EACH_ENTRY( ioctl, &device->requests, struct call_object, dev_entry ) + if (ioctl->thread == thread && ioctl->user_arg == user_arg && ioctl->type == CALL_IOCTL) return (struct ioctl_call *)ioctl; set_error( STATUS_INVALID_PARAMETER ); return NULL; @@ -323,8 +437,8 @@ static obj_handle_t device_ioctl( struct fd *fd, ioctl_code_t code, const async_ if (!(ioctl = create_ioctl( device, code, data, size, get_reply_max_size() ))) return 0; - ioctl->thread = (struct thread *)grab_object( current ); - ioctl->user_arg = async_data->arg; + ioctl->call.thread = (struct thread *)grab_object( current ); + ioctl->call.user_arg = async_data->arg; if (!(handle = alloc_handle( current->process, ioctl, SYNCHRONIZE, 0 ))) { @@ -482,9 +596,11 @@ DECL_HANDLER(delete_device) } -/* retrieve the next pending device ioctl request */ +/* retrieve the next pending device request */ DECL_HANDLER(get_next_device_request) { + struct call_object *call; + struct open_call *open; struct ioctl_call *ioctl; struct device_manager *manager; struct list *ptr; @@ -502,24 +618,46 @@ DECL_HANDLER(get_next_device_request) close_handle( current->process, req->prev ); /* avoid an extra round-trip for close */ release_object( ioctl ); } + if ((open = (struct open_call *)get_handle_obj( current->process, req->prev, + 0, &open_call_ops ))) + { + complete_open( open, req->status ); + close_handle( current->process, req->prev ); /* avoid an extra round-trip for close */ + release_object( open ); + } clear_error(); } if ((ptr = list_head( &manager->requests ))) { - ioctl = LIST_ENTRY( ptr, struct ioctl_call, mgr_entry ); - reply->code = ioctl->code; - reply->user_ptr = ioctl->device->user_ptr; - reply->in_size = ioctl->in_size; - reply->out_size = ioctl->out_size; - if (ioctl->in_size > get_reply_max_size()) set_error( STATUS_BUFFER_OVERFLOW ); - else if ((reply->next = alloc_handle( current->process, ioctl, 0, 0 ))) + call = LIST_ENTRY( ptr, struct call_object, mgr_entry ); + reply->user_ptr = call->device->user_ptr; + switch (call->type) { - set_reply_data_ptr( ioctl->in_data, ioctl->in_size ); - ioctl->in_data = NULL; - ioctl->in_size = 0; - list_remove( &ioctl->mgr_entry ); - list_init( &ioctl->mgr_entry ); + case CALL_OPEN: + open = (struct ioctl_call *)call; + reply->data.open.access = open->access; + reply->data.open.sharing = open->sharing; + reply->data.open.options = open->options; + if ((reply->next = alloc_handle( current->process, open, 0, 0 ))) + { + list_remove( &call->mgr_entry ); + list_init( &call->mgr_entry ); + } + case CALL_IOCTL: + ioctl = (struct ioctl_call *)call; + reply->data.ioctl.code = ioctl->code; + reply->data.ioctl.in_size = ioctl->in_size; + reply->data.ioctl.out_size = ioctl->out_size; + if (ioctl->in_size > get_reply_max_size()) set_error( STATUS_BUFFER_OVERFLOW ); + else if ((reply->next = alloc_handle( current->process, ioctl, 0, 0 ))) + { + set_reply_data_ptr( ioctl->in_data, ioctl->in_size ); + ioctl->in_data = NULL; + ioctl->in_size = 0; + list_remove( &call->mgr_entry ); + list_init( &call->mgr_entry ); + } } } else set_error( STATUS_PENDING ); diff --git a/server/protocol.def b/server/protocol.def index a0e1702..e554112 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3102,18 +3102,39 @@ enum message_type @END -/* Retrieve the next pending device ioctl request */ +enum server_call_type +{ + CALL_OPEN, + CALL_IOCTL +}; + +union next_request +{ + struct + { + ioctl_code_t code; /* ioctl code */ + data_size_t in_size; /* total needed input size */ + data_size_t out_size; /* needed output size */ + } ioctl; + struct + { + unsigned int access; + unsigned int sharing; + unsigned int options; + } open; +}; + +/* Retrieve the next pending device request */ @REQ(get_next_device_request) obj_handle_t manager; /* handle to the device manager */ - obj_handle_t prev; /* handle to the previous ioctl */ - unsigned int status; /* status of the previous ioctl */ + obj_handle_t prev; /* handle to the previous request */ + unsigned int status; /* status of the previous request */ VARARG(prev_data,bytes); /* output data of the previous ioctl */ @REPLY - obj_handle_t next; /* handle to the next ioctl */ - ioctl_code_t code; /* ioctl code */ + obj_handle_t next; /* handle to the next request */ + enum server_call_type type; /* type of this request */ client_ptr_t user_ptr; /* opaque ptr for the device */ - data_size_t in_size; /* total needed input size */ - data_size_t out_size; /* needed output size */ + union next_request data; /* data associated with the next request */ VARARG(next_data,bytes); /* input data of the next ioctl */ @END diff --git a/server/thread.c b/server/thread.c index f45be24..a1b4120 100644 --- a/server/thread.c +++ b/server/thread.c @@ -474,7 +474,7 @@ void stop_thread( struct thread *thread ) } /* suspend a thread */ -static int suspend_thread( struct thread *thread ) +int suspend_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend < MAXIMUM_SUSPEND_COUNT) @@ -486,7 +486,7 @@ static int suspend_thread( struct thread *thread ) } /* resume a thread */ -static int resume_thread( struct thread *thread ) +int resume_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend > 0)