/* * Process synchronisation * * Copyright 1996, 1997, 1998 Marcus Meissner * Copyright 1997, 1999 Alexandre Julliard * Copyright 1999, 2000 Juergen Schmied * Copyright 2003 Eric Pouech * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include <assert.h> #include <errno.h> #include <signal.h> #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #endif #ifdef HAVE_POLL_H #include <poll.h> #endif #ifdef HAVE_SYS_POLL_H # include <sys/poll.h> #endif #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #ifdef HAVE_SCHED_H # include <sched.h> #endif #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" #include "wine/server.h" #include "wine/debug.h" #include "ntdll_misc.h" WINE_DEFAULT_DEBUG_CHANNEL(ntdll); /* creates a struct security_descriptor and contained information in one contiguous piece of memory */ NTSTATUS NTDLL_create_struct_sd(PSECURITY_DESCRIPTOR nt_sd, struct security_descriptor **server_sd, data_size_t *server_sd_len) { unsigned int len; PSID owner, group; ACL *dacl, *sacl; BOOLEAN owner_present, group_present, dacl_present, sacl_present; BOOLEAN defaulted; NTSTATUS status; unsigned char *ptr; if (!nt_sd) { *server_sd = NULL; *server_sd_len = 0; return STATUS_SUCCESS; } len = sizeof(struct security_descriptor); status = RtlGetOwnerSecurityDescriptor(nt_sd, &owner, &owner_present); if (status != STATUS_SUCCESS) return status; status = RtlGetGroupSecurityDescriptor(nt_sd, &group, &group_present); if (status != STATUS_SUCCESS) return status; status = RtlGetSaclSecurityDescriptor(nt_sd, &sacl_present, &sacl, &defaulted); if (status != STATUS_SUCCESS) return status; status = RtlGetDaclSecurityDescriptor(nt_sd, &dacl_present, &dacl, &defaulted); if (status != STATUS_SUCCESS) return status; if (owner_present) len += RtlLengthSid(owner); if (group_present) len += RtlLengthSid(group); if (sacl_present && sacl) len += sacl->AclSize; if (dacl_present && dacl) len += dacl->AclSize; /* fix alignment for the Unicode name that follows the structure */ len = (len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1); *server_sd = RtlAllocateHeap(GetProcessHeap(), 0, len); if (!*server_sd) return STATUS_NO_MEMORY; (*server_sd)->control = ((SECURITY_DESCRIPTOR *)nt_sd)->Control & ~SE_SELF_RELATIVE; (*server_sd)->owner_len = owner_present ? RtlLengthSid(owner) : 0; (*server_sd)->group_len = group_present ? RtlLengthSid(group) : 0; (*server_sd)->sacl_len = (sacl_present && sacl) ? sacl->AclSize : 0; (*server_sd)->dacl_len = (dacl_present && dacl) ? dacl->AclSize : 0; ptr = (unsigned char *)(*server_sd + 1); memcpy(ptr, owner, (*server_sd)->owner_len); ptr += (*server_sd)->owner_len; memcpy(ptr, group, (*server_sd)->group_len); ptr += (*server_sd)->group_len; memcpy(ptr, sacl, (*server_sd)->sacl_len); ptr += (*server_sd)->sacl_len; memcpy(ptr, dacl, (*server_sd)->dacl_len); *server_sd_len = len; return STATUS_SUCCESS; } /* frees a struct security_descriptor allocated by NTDLL_create_struct_sd */ void NTDLL_free_struct_sd(struct security_descriptor *server_sd) { RtlFreeHeap(GetProcessHeap(), 0, server_sd); } /* * Semaphores */ /****************************************************************************** * NtCreateSemaphore (NTDLL.@) */ NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES *attr OPTIONAL, IN LONG InitialCount, IN LONG MaximumCount ) { DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; NTSTATUS ret; struct object_attributes objattr; struct security_descriptor *sd = NULL; if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount) return STATUS_INVALID_PARAMETER; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); objattr.sd_len = 0; objattr.name_len = len; if (attr) { ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); if (ret != STATUS_SUCCESS) return ret; } SERVER_START_REQ( create_semaphore ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->initial = InitialCount; req->max = MaximumCount; wine_server_add_data( req, &objattr, sizeof(objattr) ); if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); ret = wine_server_call( req ); *SemaphoreHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; NTDLL_free_struct_sd( sd ); return ret; } /****************************************************************************** * NtOpenSemaphore (NTDLL.@) */ NTSTATUS WINAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES *attr ) { DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; NTSTATUS ret; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; SERVER_START_REQ( open_semaphore ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); ret = wine_server_call( req ); *SemaphoreHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtQuerySemaphore (NTDLL.@) */ NTSTATUS WINAPI NtQuerySemaphore( HANDLE SemaphoreHandle, SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, PVOID SemaphoreInformation, ULONG Length, PULONG ReturnLength) { FIXME("(%p,%d,%p,0x%08x,%p) stub!\n", SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength); return STATUS_SUCCESS; } /****************************************************************************** * NtReleaseSemaphore (NTDLL.@) */ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous ) { NTSTATUS ret; SERVER_START_REQ( release_semaphore ) { req->handle = wine_server_obj_handle( handle ); req->count = count; if (!(ret = wine_server_call( req ))) { if (previous) *previous = reply->prev_count; } } SERVER_END_REQ; return ret; } /* * Events */ /************************************************************************** * NtCreateEvent (NTDLL.@) * ZwCreateEvent (NTDLL.@) */ NTSTATUS WINAPI NtCreateEvent( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN const OBJECT_ATTRIBUTES *attr, IN BOOLEAN ManualReset, IN BOOLEAN InitialState) { DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; NTSTATUS ret; struct security_descriptor *sd = NULL; struct object_attributes objattr; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); objattr.sd_len = 0; objattr.name_len = len; if (attr) { ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); if (ret != STATUS_SUCCESS) return ret; } SERVER_START_REQ( create_event ) { req->access = DesiredAccess; req->attributes = (attr) ? attr->Attributes : 0; req->manual_reset = ManualReset; req->initial_state = InitialState; wine_server_add_data( req, &objattr, sizeof(objattr) ); if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); ret = wine_server_call( req ); *EventHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; NTDLL_free_struct_sd( sd ); return ret; } /****************************************************************************** * NtOpenEvent (NTDLL.@) * ZwOpenEvent (NTDLL.@) */ NTSTATUS WINAPI NtOpenEvent( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN const OBJECT_ATTRIBUTES *attr ) { DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; NTSTATUS ret; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; SERVER_START_REQ( open_event ) { req->access = DesiredAccess; req->attributes = (attr) ? attr->Attributes : 0; req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); ret = wine_server_call( req ); *EventHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtSetEvent (NTDLL.@) * ZwSetEvent (NTDLL.@) */ NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased ) { NTSTATUS ret; /* FIXME: set NumberOfThreadsReleased */ SERVER_START_REQ( event_op ) { req->handle = wine_server_obj_handle( handle ); req->op = SET_EVENT; ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtResetEvent (NTDLL.@) */ NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased ) { NTSTATUS ret; /* resetting an event can't release any thread... */ if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0; SERVER_START_REQ( event_op ) { req->handle = wine_server_obj_handle( handle ); req->op = RESET_EVENT; ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtClearEvent (NTDLL.@) * * FIXME * same as NtResetEvent ??? */ NTSTATUS WINAPI NtClearEvent ( HANDLE handle ) { return NtResetEvent( handle, NULL ); } /****************************************************************************** * NtPulseEvent (NTDLL.@) * * FIXME * PulseCount */ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, PULONG PulseCount ) { NTSTATUS ret; if (PulseCount) FIXME("(%p,%d)\n", handle, *PulseCount); SERVER_START_REQ( event_op ) { req->handle = wine_server_obj_handle( handle ); req->op = PULSE_EVENT; ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtQueryEvent (NTDLL.@) */ NTSTATUS WINAPI NtQueryEvent ( IN HANDLE EventHandle, IN EVENT_INFORMATION_CLASS EventInformationClass, OUT PVOID EventInformation, IN ULONG EventInformationLength, OUT PULONG ReturnLength) { FIXME("(%p)\n", EventHandle); return STATUS_NOT_IMPLEMENTED; } /* * Mutants (known as Mutexes in Kernel32) */ /****************************************************************************** * NtCreateMutant [NTDLL.@] * ZwCreateMutant [NTDLL.@] */ NTSTATUS WINAPI NtCreateMutant(OUT HANDLE* MutantHandle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES* attr OPTIONAL, IN BOOLEAN InitialOwner) { NTSTATUS status; DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; struct security_descriptor *sd = NULL; struct object_attributes objattr; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); objattr.sd_len = 0; objattr.name_len = len; if (attr) { status = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); if (status != STATUS_SUCCESS) return status; } SERVER_START_REQ( create_mutex ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->owned = InitialOwner; wine_server_add_data( req, &objattr, sizeof(objattr) ); if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); status = wine_server_call( req ); *MutantHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; NTDLL_free_struct_sd( sd ); return status; } /************************************************************************** * NtOpenMutant [NTDLL.@] * ZwOpenMutant [NTDLL.@] */ NTSTATUS WINAPI NtOpenMutant(OUT HANDLE* MutantHandle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES* attr ) { NTSTATUS status; DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; SERVER_START_REQ( open_mutex ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); status = wine_server_call( req ); *MutantHandle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /************************************************************************** * NtReleaseMutant [NTDLL.@] * ZwReleaseMutant [NTDLL.@] */ NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL) { NTSTATUS status; SERVER_START_REQ( release_mutex ) { req->handle = wine_server_obj_handle( handle ); status = wine_server_call( req ); if (prev_count) *prev_count = reply->prev_count; } SERVER_END_REQ; return status; } /****************************************************************** * NtQueryMutant [NTDLL.@] * ZwQueryMutant [NTDLL.@] */ NTSTATUS WINAPI NtQueryMutant(IN HANDLE handle, IN MUTANT_INFORMATION_CLASS MutantInformationClass, OUT PVOID MutantInformation, IN ULONG MutantInformationLength, OUT PULONG ResultLength OPTIONAL ) { FIXME("(%p %u %p %u %p): stub!\n", handle, MutantInformationClass, MutantInformation, MutantInformationLength, ResultLength); return STATUS_NOT_IMPLEMENTED; } /* * Jobs */ /****************************************************************************** * NtCreateJobObject [NTDLL.@] * ZwCreateJobObject [NTDLL.@] */ NTSTATUS WINAPI NtCreateJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) { FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" ); *handle = (HANDLE)0xdead; return STATUS_SUCCESS; } /****************************************************************************** * NtOpenJobObject [NTDLL.@] * ZwOpenJobObject [NTDLL.@] */ NTSTATUS WINAPI NtOpenJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) { FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" ); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtTerminateJobObject [NTDLL.@] * ZwTerminateJobObject [NTDLL.@] */ NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status ) { FIXME( "stub: %p %x\n", handle, status ); return STATUS_SUCCESS; } /****************************************************************************** * NtQueryInformationJobObject [NTDLL.@] * ZwQueryInformationJobObject [NTDLL.@] */ NTSTATUS WINAPI NtQueryInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len, PULONG ret_len ) { FIXME( "stub: %p %u %p %u %p\n", handle, class, info, len, ret_len ); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtSetInformationJobObject [NTDLL.@] * ZwSetInformationJobObject [NTDLL.@] */ NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len ) { FIXME( "stub: %p %u %p %u\n", handle, class, info, len ); return STATUS_SUCCESS; } /****************************************************************************** * NtIsProcessInJob [NTDLL.@] * ZwIsProcessInJob [NTDLL.@] */ NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job ) { FIXME( "stub: %p %p\n", process, job ); return STATUS_PROCESS_NOT_IN_JOB; } /****************************************************************************** * NtAssignProcessToJobObject [NTDLL.@] * ZwAssignProcessToJobObject [NTDLL.@] */ NTSTATUS WINAPI NtAssignProcessToJobObject( HANDLE job, HANDLE process ) { FIXME( "stub: %p %p\n", job, process ); return STATUS_SUCCESS; } /* * Timers */ /************************************************************************** * NtCreateTimer [NTDLL.@] * ZwCreateTimer [NTDLL.@] */ NTSTATUS WINAPI NtCreateTimer(OUT HANDLE *handle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES *attr OPTIONAL, IN TIMER_TYPE timer_type) { DWORD len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0; NTSTATUS status; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; if (timer_type != NotificationTimer && timer_type != SynchronizationTimer) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( create_timer ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); req->manual = (timer_type == NotificationTimer) ? TRUE : FALSE; if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); status = wine_server_call( req ); *handle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /************************************************************************** * NtOpenTimer [NTDLL.@] * ZwOpenTimer [NTDLL.@] */ NTSTATUS WINAPI NtOpenTimer(OUT PHANDLE handle, IN ACCESS_MASK access, IN const OBJECT_ATTRIBUTES* attr ) { DWORD len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0; NTSTATUS status; if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; SERVER_START_REQ( open_timer ) { req->access = access; req->attributes = (attr) ? attr->Attributes : 0; req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); status = wine_server_call( req ); *handle = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /************************************************************************** * NtSetTimer [NTDLL.@] * ZwSetTimer [NTDLL.@] */ NTSTATUS WINAPI NtSetTimer(IN HANDLE handle, IN const LARGE_INTEGER* when, IN PTIMER_APC_ROUTINE callback, IN PVOID callback_arg, IN BOOLEAN resume, IN ULONG period OPTIONAL, OUT PBOOLEAN state OPTIONAL) { NTSTATUS status = STATUS_SUCCESS; TRACE("(%p,%p,%p,%p,%08x,0x%08x,%p) stub\n", handle, when, callback, callback_arg, resume, period, state); SERVER_START_REQ( set_timer ) { req->handle = wine_server_obj_handle( handle ); req->period = period; req->expire = when->QuadPart; req->callback = wine_server_client_ptr( callback ); req->arg = wine_server_client_ptr( callback_arg ); status = wine_server_call( req ); if (state) *state = reply->signaled; } SERVER_END_REQ; /* set error but can still succeed */ if (resume && status == STATUS_SUCCESS) return STATUS_TIMER_RESUME_IGNORED; return status; } /************************************************************************** * NtCancelTimer [NTDLL.@] * ZwCancelTimer [NTDLL.@] */ NTSTATUS WINAPI NtCancelTimer(IN HANDLE handle, OUT BOOLEAN* state) { NTSTATUS status; SERVER_START_REQ( cancel_timer ) { req->handle = wine_server_obj_handle( handle ); status = wine_server_call( req ); if (state) *state = reply->signaled; } SERVER_END_REQ; return status; } /****************************************************************************** * NtQueryTimer (NTDLL.@) * * Retrieves information about a timer. * * PARAMS * TimerHandle [I] The timer to retrieve information about. * TimerInformationClass [I] The type of information to retrieve. * TimerInformation [O] Pointer to buffer to store information in. * Length [I] The length of the buffer pointed to by TimerInformation. * ReturnLength [O] Optional. The size of buffer actually used. * * RETURNS * Success: STATUS_SUCCESS * Failure: STATUS_INFO_LENGTH_MISMATCH, if Length doesn't match the required data * size for the class specified. * STATUS_INVALID_INFO_CLASS, if an invalid TimerInformationClass was specified. * STATUS_ACCESS_DENIED, if TimerHandle does not have TIMER_QUERY_STATE access * to the timer. */ NTSTATUS WINAPI NtQueryTimer( HANDLE TimerHandle, TIMER_INFORMATION_CLASS TimerInformationClass, PVOID TimerInformation, ULONG Length, PULONG ReturnLength) { TIMER_BASIC_INFORMATION * basic_info = TimerInformation; NTSTATUS status; LARGE_INTEGER now; TRACE("(%p,%d,%p,0x%08x,%p)\n", TimerHandle, TimerInformationClass, TimerInformation, Length, ReturnLength); switch (TimerInformationClass) { case TimerBasicInformation: if (Length < sizeof(TIMER_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH; SERVER_START_REQ(get_timer_info) { req->handle = wine_server_obj_handle( TimerHandle ); status = wine_server_call(req); /* convert server time to absolute NTDLL time */ basic_info->RemainingTime.QuadPart = reply->when; basic_info->TimerState = reply->signaled; } SERVER_END_REQ; /* convert from absolute into relative time */ NtQuerySystemTime(&now); if (now.QuadPart > basic_info->RemainingTime.QuadPart) basic_info->RemainingTime.QuadPart = 0; else basic_info->RemainingTime.QuadPart -= now.QuadPart; if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); return status; } FIXME("Unhandled class %d\n", TimerInformationClass); return STATUS_INVALID_INFO_CLASS; } /****************************************************************************** * NtQueryTimerResolution [NTDLL.@] */ NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution, OUT ULONG* max_resolution, OUT ULONG* current_resolution) { FIXME("(%p,%p,%p), stub!\n", min_resolution, max_resolution, current_resolution); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtSetTimerResolution [NTDLL.@] */ NTSTATUS WINAPI NtSetTimerResolution(IN ULONG resolution, IN BOOLEAN set_resolution, OUT ULONG* current_resolution ) { FIXME("(%u,%u,%p), stub!\n", resolution, set_resolution, current_resolution); return STATUS_NOT_IMPLEMENTED; } /*********************************************************************** * wait_reply * * Wait for a reply on the waiting pipe of the current thread. */ static int wait_reply( void *cookie ) { int signaled; struct wake_up_reply reply; for (;;) { int ret; ret = read( ntdll_get_thread_data()->wait_fd[0], &reply, sizeof(reply) ); if (ret == sizeof(reply)) { if (!reply.cookie) break; /* thread got killed */ if (wine_server_get_ptr(reply.cookie) == cookie) return reply.signaled; /* we stole another reply, wait for the real one */ signaled = wait_reply( cookie ); /* and now put the wrong one back in the pipe */ for (;;) { ret = write( ntdll_get_thread_data()->wait_fd[1], &reply, sizeof(reply) ); if (ret == sizeof(reply)) break; if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret ); if (errno == EINTR) continue; server_protocol_perror("wakeup write"); } return signaled; } if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret ); if (errno == EINTR) continue; server_protocol_perror("wakeup read"); } /* the server closed the connection; time to die... */ abort_thread(0); } /*********************************************************************** * invoke_apc * * Invoke a single APC. Return TRUE if a user APC has been run. */ static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result ) { BOOL user_apc = FALSE; SIZE_T size; void *addr; memset( result, 0, sizeof(*result) ); switch (call->type) { case APC_NONE: break; case APC_USER: { void (WINAPI *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR) = wine_server_get_ptr( call->user.func ); func( call->user.args[0], call->user.args[1], call->user.args[2] ); user_apc = TRUE; break; } case APC_TIMER: { void (WINAPI *func)(void*, unsigned int, unsigned int) = wine_server_get_ptr( call->timer.func ); func( wine_server_get_ptr( call->timer.arg ), (DWORD)call->timer.time, (DWORD)(call->timer.time >> 32) ); user_apc = TRUE; break; } case APC_ASYNC_IO: { void *apc = NULL; IO_STATUS_BLOCK *iosb = wine_server_get_ptr( call->async_io.sb ); NTSTATUS (*func)(void *, IO_STATUS_BLOCK *, NTSTATUS, void **) = wine_server_get_ptr( call->async_io.func ); result->type = call->type; result->async_io.status = func( wine_server_get_ptr( call->async_io.user ), iosb, call->async_io.status, &apc ); if (result->async_io.status != STATUS_PENDING) { result->async_io.total = iosb->Information; result->async_io.apc = wine_server_client_ptr( apc ); } break; } case APC_VIRTUAL_ALLOC: result->type = call->type; addr = wine_server_get_ptr( call->virtual_alloc.addr ); size = call->virtual_alloc.size; if ((ULONG_PTR)addr == call->virtual_alloc.addr && size == call->virtual_alloc.size) { result->virtual_alloc.status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, call->virtual_alloc.zero_bits, &size, call->virtual_alloc.op_type, call->virtual_alloc.prot ); result->virtual_alloc.addr = wine_server_client_ptr( addr ); result->virtual_alloc.size = size; } else result->virtual_alloc.status = STATUS_WORKING_SET_LIMIT_RANGE; break; case APC_VIRTUAL_FREE: result->type = call->type; addr = wine_server_get_ptr( call->virtual_free.addr ); size = call->virtual_free.size; if ((ULONG_PTR)addr == call->virtual_free.addr && size == call->virtual_free.size) { result->virtual_free.status = NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, call->virtual_free.op_type ); result->virtual_free.addr = wine_server_client_ptr( addr ); result->virtual_free.size = size; } else result->virtual_free.status = STATUS_INVALID_PARAMETER; break; case APC_VIRTUAL_QUERY: { MEMORY_BASIC_INFORMATION info; result->type = call->type; addr = wine_server_get_ptr( call->virtual_query.addr ); if ((ULONG_PTR)addr == call->virtual_query.addr) result->virtual_query.status = NtQueryVirtualMemory( NtCurrentProcess(), addr, MemoryBasicInformation, &info, sizeof(info), NULL ); else result->virtual_query.status = STATUS_WORKING_SET_LIMIT_RANGE; if (result->virtual_query.status == STATUS_SUCCESS) { result->virtual_query.base = wine_server_client_ptr( info.BaseAddress ); result->virtual_query.alloc_base = wine_server_client_ptr( info.AllocationBase ); result->virtual_query.size = info.RegionSize; result->virtual_query.prot = info.Protect; result->virtual_query.alloc_prot = info.AllocationProtect; result->virtual_query.state = info.State >> 12; result->virtual_query.alloc_type = info.Type >> 16; } break; } case APC_VIRTUAL_PROTECT: result->type = call->type; addr = wine_server_get_ptr( call->virtual_protect.addr ); size = call->virtual_protect.size; if ((ULONG_PTR)addr == call->virtual_protect.addr && size == call->virtual_protect.size) { result->virtual_protect.status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, call->virtual_protect.prot, &result->virtual_protect.prot ); result->virtual_protect.addr = wine_server_client_ptr( addr ); result->virtual_protect.size = size; } else result->virtual_protect.status = STATUS_INVALID_PARAMETER; break; case APC_VIRTUAL_FLUSH: result->type = call->type; addr = wine_server_get_ptr( call->virtual_flush.addr ); size = call->virtual_flush.size; if ((ULONG_PTR)addr == call->virtual_flush.addr && size == call->virtual_flush.size) { result->virtual_flush.status = NtFlushVirtualMemory( NtCurrentProcess(), (const void **)&addr, &size, 0 ); result->virtual_flush.addr = wine_server_client_ptr( addr ); result->virtual_flush.size = size; } else result->virtual_flush.status = STATUS_INVALID_PARAMETER; break; case APC_VIRTUAL_LOCK: result->type = call->type; addr = wine_server_get_ptr( call->virtual_lock.addr ); size = call->virtual_lock.size; if ((ULONG_PTR)addr == call->virtual_lock.addr && size == call->virtual_lock.size) { result->virtual_lock.status = NtLockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 ); result->virtual_lock.addr = wine_server_client_ptr( addr ); result->virtual_lock.size = size; } else result->virtual_lock.status = STATUS_INVALID_PARAMETER; break; case APC_VIRTUAL_UNLOCK: result->type = call->type; addr = wine_server_get_ptr( call->virtual_unlock.addr ); size = call->virtual_unlock.size; if ((ULONG_PTR)addr == call->virtual_unlock.addr && size == call->virtual_unlock.size) { result->virtual_unlock.status = NtUnlockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 ); result->virtual_unlock.addr = wine_server_client_ptr( addr ); result->virtual_unlock.size = size; } else result->virtual_unlock.status = STATUS_INVALID_PARAMETER; break; case APC_MAP_VIEW: result->type = call->type; addr = wine_server_get_ptr( call->map_view.addr ); size = call->map_view.size; if ((ULONG_PTR)addr == call->map_view.addr && size == call->map_view.size) { LARGE_INTEGER offset; offset.QuadPart = call->map_view.offset; result->map_view.status = NtMapViewOfSection( wine_server_ptr_handle(call->map_view.handle), NtCurrentProcess(), &addr, call->map_view.zero_bits, 0, &offset, &size, ViewShare, call->map_view.alloc_type, call->map_view.prot ); result->map_view.addr = wine_server_client_ptr( addr ); result->map_view.size = size; } else result->map_view.status = STATUS_INVALID_PARAMETER; NtClose( wine_server_ptr_handle(call->map_view.handle) ); break; case APC_UNMAP_VIEW: result->type = call->type; addr = wine_server_get_ptr( call->unmap_view.addr ); if ((ULONG_PTR)addr == call->unmap_view.addr) result->unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(), addr ); else result->unmap_view.status = STATUS_INVALID_PARAMETER; break; case APC_CREATE_THREAD: { CLIENT_ID id; HANDLE handle; SIZE_T reserve = call->create_thread.reserve; SIZE_T commit = call->create_thread.commit; void *func = wine_server_get_ptr( call->create_thread.func ); void *arg = wine_server_get_ptr( call->create_thread.arg ); result->type = call->type; if (reserve == call->create_thread.reserve && commit == call->create_thread.commit && (ULONG_PTR)func == call->create_thread.func && (ULONG_PTR)arg == call->create_thread.arg) { result->create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL, call->create_thread.suspend, NULL, reserve, commit, func, arg, &handle, &id ); result->create_thread.handle = wine_server_obj_handle( handle ); result->create_thread.tid = HandleToULong(id.UniqueThread); } else result->create_thread.status = STATUS_INVALID_PARAMETER; break; } default: server_protocol_error( "get_apc_request: bad type %d\n", call->type ); break; } return user_apc; } /*********************************************************************** * NTDLL_queue_process_apc */ NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result ) { for (;;) { NTSTATUS ret; HANDLE handle = 0; BOOL self = FALSE; SERVER_START_REQ( queue_apc ) { req->handle = wine_server_obj_handle( process ); req->call = *call; if (!(ret = wine_server_call( req ))) { handle = wine_server_ptr_handle( reply->handle ); self = reply->self; } } SERVER_END_REQ; if (ret != STATUS_SUCCESS) return ret; if (self) { invoke_apc( call, result ); } else { NtWaitForSingleObject( handle, FALSE, NULL ); SERVER_START_REQ( get_apc_result ) { req->handle = wine_server_obj_handle( handle ); if (!(ret = wine_server_call( req ))) *result = reply->result; } SERVER_END_REQ; if (!ret && result->type == APC_NONE) continue; /* APC didn't run, try again */ if (ret) NtClose( handle ); } return ret; } } /*********************************************************************** * NTDLL_wait_for_multiple_objects * * Implementation of NtWaitForMultipleObjects */ NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags, const LARGE_INTEGER *timeout, HANDLE signal_object ) { NTSTATUS ret; int i, cookie; BOOL user_apc = FALSE; obj_handle_t obj_handles[MAXIMUM_WAIT_OBJECTS]; obj_handle_t apc_handle = 0; apc_call_t call; apc_result_t result; timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; memset( &result, 0, sizeof(result) ); for (i = 0; i < count; i++) obj_handles[i] = wine_server_obj_handle( handles[i] ); for (;;) { SERVER_START_REQ( select ) { req->flags = flags; req->cookie = wine_server_client_ptr( &cookie ); req->signal = wine_server_obj_handle( signal_object ); req->prev_apc = apc_handle; req->timeout = abs_timeout; wine_server_add_data( req, &result, sizeof(result) ); wine_server_add_data( req, obj_handles, count * sizeof(*obj_handles) ); ret = wine_server_call( req ); abs_timeout = reply->timeout; apc_handle = reply->apc_handle; call = reply->call; } SERVER_END_REQ; if (ret == STATUS_PENDING) ret = wait_reply( &cookie ); if (ret != STATUS_USER_APC) break; if (invoke_apc( &call, &result )) { /* if we ran a user apc we have to check once more if an object got signaled, * but we don't want to wait */ abs_timeout = 0; user_apc = TRUE; } signal_object = 0; /* don't signal it multiple times */ } if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC; /* A test on Windows 2000 shows that Windows always yields during a wait, but a wait that is hit by an event gets a priority boost as well. This seems to model that behavior the closest. */ if (ret == STATUS_TIMEOUT) NtYieldExecution(); return ret; } /* wait operations */ /****************************************************************** * NtWaitForMultipleObjects (NTDLL.@) */ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BOOLEAN wait_all, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { UINT flags = SELECT_INTERRUPTIBLE; if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; if (wait_all) flags |= SELECT_ALL; if (alertable) flags |= SELECT_ALERTABLE; return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout, 0 ); } /****************************************************************** * NtWaitForSingleObject (NTDLL.@) */ NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout ); } /****************************************************************** * NtSignalAndWaitForSingleObject (NTDLL.@) */ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { UINT flags = SELECT_INTERRUPTIBLE; if (!hSignalObject) return STATUS_INVALID_HANDLE; if (alertable) flags |= SELECT_ALERTABLE; return NTDLL_wait_for_multiple_objects( 1, &hWaitObject, flags, timeout, hSignalObject ); } /****************************************************************** * NtYieldExecution (NTDLL.@) */ NTSTATUS WINAPI NtYieldExecution(void) { #ifdef HAVE_SCHED_YIELD sched_yield(); return STATUS_SUCCESS; #else return STATUS_NO_YIELD_PERFORMED; #endif } /****************************************************************** * NtDelayExecution (NTDLL.@) */ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout ) { /* if alertable, we need to query the server */ if (alertable) return NTDLL_wait_for_multiple_objects( 0, NULL, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout, 0 ); if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ { for (;;) select( 0, NULL, NULL, NULL, NULL ); } else { LARGE_INTEGER now; timeout_t when, diff; if ((when = timeout->QuadPart) < 0) { NtQuerySystemTime( &now ); when = now.QuadPart - when; } /* Note that we yield after establishing the desired timeout */ NtYieldExecution(); if (!when) return STATUS_SUCCESS; for (;;) { struct timeval tv; NtQuerySystemTime( &now ); diff = (when - now.QuadPart + 9) / 10; if (diff <= 0) break; tv.tv_sec = diff / 1000000; tv.tv_usec = diff % 1000000; if (select( 0, NULL, NULL, NULL, &tv ) != -1) break; } } return STATUS_SUCCESS; } /****************************************************************** * NtCreateIoCompletion (NTDLL.@) * ZwCreateIoCompletion (NTDLL.@) * * Creates I/O completion object. * * PARAMS * CompletionPort [O] created completion object handle will be placed there * DesiredAccess [I] desired access to a handle (combination of IO_COMPLETION_*) * ObjectAttributes [I] completion object attributes * NumberOfConcurrentThreads [I] desired number of concurrent active worker threads * */ NTSTATUS WINAPI NtCreateIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads ) { NTSTATUS status; TRACE("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess, ObjectAttributes, NumberOfConcurrentThreads); if (!CompletionPort) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( create_completion ) { req->access = DesiredAccess; req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0; req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 ); req->concurrent = NumberOfConcurrentThreads; if (ObjectAttributes && ObjectAttributes->ObjectName) wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length ); if (!(status = wine_server_call( req ))) *CompletionPort = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /****************************************************************** * NtSetIoCompletion (NTDLL.@) * ZwSetIoCompletion (NTDLL.@) * * Inserts completion message into queue * * PARAMS * CompletionPort [I] HANDLE to completion object * CompletionKey [I] completion key * CompletionValue [I] completion value (usually pointer to OVERLAPPED) * Status [I] operation status * NumberOfBytesTransferred [I] number of bytes transferred */ NTSTATUS WINAPI NtSetIoCompletion( HANDLE CompletionPort, ULONG_PTR CompletionKey, ULONG_PTR CompletionValue, NTSTATUS Status, ULONG NumberOfBytesTransferred ) { NTSTATUS status; TRACE("(%p, %lx, %lx, %x, %d)\n", CompletionPort, CompletionKey, CompletionValue, Status, NumberOfBytesTransferred); SERVER_START_REQ( add_completion ) { req->handle = wine_server_obj_handle( CompletionPort ); req->ckey = CompletionKey; req->cvalue = CompletionValue; req->status = Status; req->information = NumberOfBytesTransferred; status = wine_server_call( req ); } SERVER_END_REQ; return status; } /****************************************************************** * NtRemoveIoCompletion (NTDLL.@) * ZwRemoveIoCompletion (NTDLL.@) * * (Wait for and) retrieve first completion message from completion object's queue * * PARAMS * CompletionPort [I] HANDLE to I/O completion object * CompletionKey [O] completion key * CompletionValue [O] Completion value given in NtSetIoCompletion or in async operation * iosb [O] IO_STATUS_BLOCK of completed asynchronous operation * WaitTime [I] optional wait time in NTDLL format * */ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE CompletionPort, PULONG_PTR CompletionKey, PULONG_PTR CompletionValue, PIO_STATUS_BLOCK iosb, PLARGE_INTEGER WaitTime ) { NTSTATUS status; TRACE("(%p, %p, %p, %p, %p)\n", CompletionPort, CompletionKey, CompletionValue, iosb, WaitTime); for(;;) { SERVER_START_REQ( remove_completion ) { req->handle = wine_server_obj_handle( CompletionPort ); if (!(status = wine_server_call( req ))) { *CompletionKey = reply->ckey; *CompletionValue = reply->cvalue; iosb->Information = reply->information; iosb->u.Status = reply->status; } } SERVER_END_REQ; if (status != STATUS_PENDING) break; status = NtWaitForSingleObject( CompletionPort, FALSE, WaitTime ); if (status != WAIT_OBJECT_0) break; } return status; } /****************************************************************** * NtOpenIoCompletion (NTDLL.@) * ZwOpenIoCompletion (NTDLL.@) * * Opens I/O completion object * * PARAMS * CompletionPort [O] completion object handle will be placed there * DesiredAccess [I] desired access to a handle (combination of IO_COMPLETION_*) * ObjectAttributes [I] completion object name * */ NTSTATUS WINAPI NtOpenIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes ) { NTSTATUS status; TRACE("(%p, 0x%x, %p)\n", CompletionPort, DesiredAccess, ObjectAttributes); if (!CompletionPort || !ObjectAttributes || !ObjectAttributes->ObjectName) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( open_completion ) { req->access = DesiredAccess; req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory ); wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length ); if (!(status = wine_server_call( req ))) *CompletionPort = wine_server_ptr_handle( reply->handle ); } SERVER_END_REQ; return status; } /****************************************************************** * NtQueryIoCompletion (NTDLL.@) * ZwQueryIoCompletion (NTDLL.@) * * Requests information about given I/O completion object * * PARAMS * CompletionPort [I] HANDLE to completion port to request * InformationClass [I] information class * CompletionInformation [O] user-provided buffer for data * BufferLength [I] buffer length * RequiredLength [O] required buffer length * */ NTSTATUS WINAPI NtQueryIoCompletion( HANDLE CompletionPort, IO_COMPLETION_INFORMATION_CLASS InformationClass, PVOID CompletionInformation, ULONG BufferLength, PULONG RequiredLength ) { NTSTATUS status; TRACE("(%p, %d, %p, 0x%x, %p)\n", CompletionPort, InformationClass, CompletionInformation, BufferLength, RequiredLength); if (!CompletionInformation) return STATUS_INVALID_PARAMETER; switch( InformationClass ) { case IoCompletionBasicInformation: { ULONG *info = CompletionInformation; if (RequiredLength) *RequiredLength = sizeof(*info); if (BufferLength != sizeof(*info)) status = STATUS_INFO_LENGTH_MISMATCH; else { SERVER_START_REQ( query_completion ) { req->handle = wine_server_obj_handle( CompletionPort ); if (!(status = wine_server_call( req ))) *info = reply->depth; } SERVER_END_REQ; } } break; default: status = STATUS_INVALID_PARAMETER; break; } return status; } NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue, NTSTATUS CompletionStatus, ULONG Information ) { NTSTATUS status; SERVER_START_REQ( add_fd_completion ) { req->handle = wine_server_obj_handle( hFile ); req->cvalue = CompletionValue; req->status = CompletionStatus; req->information = Information; status = wine_server_call( req ); } SERVER_END_REQ; return status; }