X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=win32ss%2Fuser%2Fconsrv%2Fhandle.c;h=339dbb3646eec9d4a670237bed4e8a665ad7310b;hp=e30338196fab0a1500bccfa2f3fd1df8d989763d;hb=e2d2cb40e26869e432760765aee58eb4946ef640;hpb=f77f05cd9df0b7d4844d5879e5b1193c6b9e647c;ds=sidebyside diff --git a/win32ss/user/consrv/handle.c b/win32ss/user/consrv/handle.c index e30338196fa..339dbb3646e 100644 --- a/win32ss/user/consrv/handle.c +++ b/win32ss/user/consrv/handle.c @@ -2,7 +2,7 @@ * LICENSE: GPL - See COPYING in the top level directory * PROJECT: ReactOS Console Server DLL * FILE: win32ss/user/consrv/handle.c - * PURPOSE: Handle functions + * PURPOSE: Console I/O Handles functions * PROGRAMMERS: */ @@ -11,304 +11,778 @@ #include "consrv.h" #include "conio.h" -#define NDEBUG +//#define NDEBUG #include -/* FUNCTIONS *****************************************************************/ - -static -BOOL -CsrIsConsoleHandle(HANDLE Handle) -{ - return ((ULONG_PTR)Handle & 0x10000003) == 0x3; -} +/* PRIVATE FUNCTIONS *********************************************************/ static INT -AdjustHandleCounts(PCSRSS_HANDLE Entry, INT Change) +AdjustHandleCounts(PCONSOLE_IO_HANDLE Entry, INT Change) { Object_t *Object = Entry->Object; + + DPRINT1("AdjustHandleCounts(0x%p, %d), Object = 0x%p, Object->HandleCount = %d, Object->Type = %lu\n", Entry, Change, Object, Object->HandleCount, Object->Type); + if (Entry->Access & GENERIC_READ) Object->AccessRead += Change; if (Entry->Access & GENERIC_WRITE) Object->AccessWrite += Change; if (!(Entry->ShareMode & FILE_SHARE_READ)) Object->ExclusiveRead += Change; if (!(Entry->ShareMode & FILE_SHARE_WRITE)) Object->ExclusiveWrite += Change; + Object->HandleCount += Change; + return Object->HandleCount; } static VOID -Win32CsrCreateHandleEntry(PCSRSS_HANDLE Entry) +ConSrvCreateHandleEntry(PCONSOLE_IO_HANDLE Entry) { - Object_t *Object = Entry->Object; - EnterCriticalSection(&Object->Console->Lock); + /// LOCK /// Object_t *Object = Entry->Object; + /// LOCK /// EnterCriticalSection(&Object->Console->Lock); AdjustHandleCounts(Entry, +1); - LeaveCriticalSection(&Object->Console->Lock); + /// LOCK /// LeaveCriticalSection(&Object->Console->Lock); } static VOID -Win32CsrCloseHandleEntry(PCSRSS_HANDLE Entry) +ConSrvCloseHandleEntry(PCONSOLE_IO_HANDLE Entry) { Object_t *Object = Entry->Object; if (Object != NULL) { - PCSRSS_CONSOLE Console = Object->Console; - EnterCriticalSection(&Console->Lock); - /* If the last handle to a screen buffer is closed, delete it */ - if (AdjustHandleCounts(Entry, -1) == 0 - && Object->Type == CONIO_SCREEN_BUFFER_MAGIC) + /// LOCK /// PCONSOLE Console = Object->Console; + /// LOCK /// EnterCriticalSection(&Console->Lock); + + /* + * If this is a input handle, notify and dereference + * all the waits related to this handle. + */ + if (Object->Type == CONIO_INPUT_BUFFER_MAGIC) { - PCSRSS_SCREEN_BUFFER Buffer = (PCSRSS_SCREEN_BUFFER)Object; - /* ...unless it's the only buffer left. Windows allows deletion - * even of the last buffer, but having to deal with a lack of - * any active buffer might be error-prone. */ - if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink) - ConioDeleteScreenBuffer(Buffer); + PCONSOLE_INPUT_BUFFER InputBuffer = (PCONSOLE_INPUT_BUFFER)Object; + + /* + * Wake up all the writing waiters related to this handle for this + * input buffer, if any, then dereference them and purge them all + * from the list. + * To select them amongst all the waiters for this input buffer, + * pass the handle pointer to the waiters, then they will check + * whether or not they are related to this handle and if so, they + * return. + */ + CsrNotifyWait(&InputBuffer->ReadWaitQueue, + WaitAll, + NULL, + (PVOID)Entry); + if (!IsListEmpty(&InputBuffer->ReadWaitQueue)) + { + CsrDereferenceWait(&InputBuffer->ReadWaitQueue); + } } - LeaveCriticalSection(&Console->Lock); + + /* If the last handle to a screen buffer is closed, delete it... */ + if (AdjustHandleCounts(Entry, -1) == 0) + { + if (Object->Type == CONIO_SCREEN_BUFFER_MAGIC) + { + PCONSOLE_SCREEN_BUFFER Buffer = (PCONSOLE_SCREEN_BUFFER)Object; + /* ...unless it's the only buffer left. Windows allows deletion + * even of the last buffer, but having to deal with a lack of + * any active buffer might be error-prone. */ + if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink) + ConioDeleteScreenBuffer(Buffer); + } + else if (Object->Type == CONIO_INPUT_BUFFER_MAGIC) + { + DPRINT1("Closing the input buffer\n"); + } + } + + /// LOCK /// LeaveCriticalSection(&Console->Lock); Entry->Object = NULL; } } -NTSTATUS + +/* FUNCTIONS *****************************************************************/ + +/* static */ NTSTATUS FASTCALL -Win32CsrReleaseObject(PCONSOLE_PROCESS_DATA ProcessData, - HANDLE Handle) +ConSrvInitHandlesTable(IN OUT PCONSOLE_PROCESS_DATA ProcessData, + OUT PHANDLE pInputHandle, + OUT PHANDLE pOutputHandle, + OUT PHANDLE pErrorHandle) { - ULONG_PTR h = (ULONG_PTR)Handle >> 2; - Object_t *Object; + NTSTATUS Status; + HANDLE InputHandle = INVALID_HANDLE_VALUE, + OutputHandle = INVALID_HANDLE_VALUE, + ErrorHandle = INVALID_HANDLE_VALUE; + + /* + * Initialize the handles table. Use temporary variables to store + * the handles values in such a way that, if we fail, we don't + * return to the caller invalid handle values. + * + * Insert the IO handles. + */ RtlEnterCriticalSection(&ProcessData->HandleTableLock); - if (h >= ProcessData->HandleTableSize - || (Object = ProcessData->HandleTable[h].Object) == NULL) + + /* Insert the Input handle */ + Status = ConSrvInsertObject(ProcessData, + &InputHandle, + &ProcessData->Console->InputBuffer.Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) { + DPRINT1("Failed to insert the input handle\n"); RtlLeaveCriticalSection(&ProcessData->HandleTableLock); - return STATUS_INVALID_HANDLE; + ConSrvFreeHandlesTable(ProcessData); + return Status; + } + + /* Insert the Output handle */ + Status = ConSrvInsertObject(ProcessData, + &OutputHandle, + &ProcessData->Console->ActiveBuffer->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert the output handle\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + ConSrvFreeHandlesTable(ProcessData); + return Status; + } + + /* Insert the Error handle */ + Status = ConSrvInsertObject(ProcessData, + &ErrorHandle, + &ProcessData->Console->ActiveBuffer->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert the error handle\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + ConSrvFreeHandlesTable(ProcessData); + return Status; } - Win32CsrCloseHandleEntry(&ProcessData->HandleTable[h]); + + /* Return the newly created handles */ + *pInputHandle = InputHandle; + *pOutputHandle = OutputHandle; + *pErrorHandle = ErrorHandle; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return STATUS_SUCCESS; } NTSTATUS FASTCALL -Win32CsrLockObject(PCONSOLE_PROCESS_DATA ProcessData, - HANDLE Handle, - Object_t **Object, - DWORD Access, - LONG Type) +ConSrvInheritHandlesTable(IN PCONSOLE_PROCESS_DATA SourceProcessData, + IN PCONSOLE_PROCESS_DATA TargetProcessData) { - ULONG_PTR h = (ULONG_PTR)Handle >> 2; + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; - DPRINT("Win32CsrLockObject, Object: %x, %x, %x\n", - Object, Handle, ProcessData ? ProcessData->HandleTableSize : 0); + RtlEnterCriticalSection(&SourceProcessData->HandleTableLock); - RtlEnterCriticalSection(&ProcessData->HandleTableLock); - if (!CsrIsConsoleHandle(Handle) || h >= ProcessData->HandleTableSize - || (*Object = ProcessData->HandleTable[h].Object) == NULL - || ~ProcessData->HandleTable[h].Access & Access - || (Type != 0 && (*Object)->Type != Type)) + /* Inherit a handles table only if there is no already */ + if (TargetProcessData->HandleTable != NULL /* || TargetProcessData->HandleTableSize != 0 */) { - DPRINT1("CsrGetObject returning invalid handle (%x)\n", Handle); - RtlLeaveCriticalSection(&ProcessData->HandleTableLock); - return STATUS_INVALID_HANDLE; + Status = STATUS_UNSUCCESSFUL; /* STATUS_INVALID_PARAMETER */ + goto Quit; } - _InterlockedIncrement(&(*Object)->Console->ReferenceCount); - RtlLeaveCriticalSection(&ProcessData->HandleTableLock); - EnterCriticalSection(&((*Object)->Console->Lock)); - return STATUS_SUCCESS; + /* Allocate a new handle table for the child process */ + TargetProcessData->HandleTable = RtlAllocateHeap(ConSrvHeap, + HEAP_ZERO_MEMORY, + SourceProcessData->HandleTableSize + * sizeof(CONSOLE_IO_HANDLE)); + if (TargetProcessData->HandleTable == NULL) + { + Status = STATUS_NO_MEMORY; + goto Quit; + } + + TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize; + + /* + * Parse the parent process' handles table and, for each handle, + * do a copy of it and reference it, if the handle is inheritable. + */ + for (i = 0; i < SourceProcessData->HandleTableSize; i++) + { + if (SourceProcessData->HandleTable[i].Object != NULL && + SourceProcessData->HandleTable[i].Inheritable) + { + /* + * Copy the handle data and increment the reference count of the + * pointed object (via the call to ConSrvCreateHandleEntry). + */ + TargetProcessData->HandleTable[i] = SourceProcessData->HandleTable[i]; + ConSrvCreateHandleEntry(&TargetProcessData->HandleTable[i]); + } + } + +Quit: + RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); + return Status; } VOID FASTCALL -Win32CsrUnlockObject(Object_t *Object) +ConSrvFreeHandlesTable(PCONSOLE_PROCESS_DATA ProcessData) { - PCSRSS_CONSOLE Console = Object->Console; - LeaveCriticalSection(&Console->Lock); - /* dec ref count */ - if (_InterlockedDecrement(&Console->ReferenceCount) == 0) - ConioDeleteConsole(&Console->Header); + DPRINT1("ConSrvFreeHandlesTable\n"); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if (ProcessData->HandleTable != NULL) + { + ULONG i; + + /* Close all console handles and free the handle table memory */ + for (i = 0; i < ProcessData->HandleTableSize; i++) + { + ConSrvCloseHandleEntry(&ProcessData->HandleTable[i]); + } + RtlFreeHeap(ConSrvHeap, 0, ProcessData->HandleTable); + ProcessData->HandleTable = NULL; + } + + ProcessData->HandleTableSize = 0; + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); } NTSTATUS -NTAPI -ConsoleNewProcess(PCSR_PROCESS SourceProcess, - PCSR_PROCESS TargetProcess) +FASTCALL +ConSrvInsertObject(PCONSOLE_PROCESS_DATA ProcessData, + PHANDLE Handle, + Object_t *Object, + DWORD Access, + BOOL Inheritable, + DWORD ShareMode) { - PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData; +#define IO_HANDLES_INCREMENT 2*3 + ULONG i; + PCONSOLE_IO_HANDLE Block; - DPRINT1("ConsoleNewProcess inside\n"); - DPRINT1("SourceProcess = 0x%p ; TargetProcess = 0x%p\n", SourceProcess, TargetProcess); + RtlEnterCriticalSection(&ProcessData->HandleTableLock); - /* An empty target process is invalid */ - if (!TargetProcess) - return STATUS_INVALID_PARAMETER; + for (i = 0; i < ProcessData->HandleTableSize; i++) + { + if (ProcessData->HandleTable[i].Object == NULL) + { + break; + } + } + if (i >= ProcessData->HandleTableSize) + { + Block = RtlAllocateHeap(ConSrvHeap, + HEAP_ZERO_MEMORY, + (ProcessData->HandleTableSize + + IO_HANDLES_INCREMENT) * sizeof(CONSOLE_IO_HANDLE)); + if (Block == NULL) + { + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_UNSUCCESSFUL; + } + RtlCopyMemory(Block, + ProcessData->HandleTable, + ProcessData->HandleTableSize * sizeof(CONSOLE_IO_HANDLE)); + RtlFreeHeap(ConSrvHeap, 0, ProcessData->HandleTable); + ProcessData->HandleTable = Block; + ProcessData->HandleTableSize += IO_HANDLES_INCREMENT; + } - DPRINT1("ConsoleNewProcess - OK\n"); + ProcessData->HandleTable[i].Object = Object; + ProcessData->HandleTable[i].Access = Access; + ProcessData->HandleTable[i].Inheritable = Inheritable; + ProcessData->HandleTable[i].ShareMode = ShareMode; + ConSrvCreateHandleEntry(&ProcessData->HandleTable[i]); + *Handle = ULongToHandle((i << 2) | 0x3); - TargetProcessData = ConsoleGetPerProcessData(TargetProcess); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); - /* Initialize the new (target) process */ - TargetProcessData->Process = TargetProcess; - RtlInitializeCriticalSection(&TargetProcessData->HandleTableLock); + return STATUS_SUCCESS; +} - /* Do nothing if the source process is NULL */ - if (!SourceProcess) - return STATUS_SUCCESS; +NTSTATUS +FASTCALL +ConSrvRemoveObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle) +{ + ULONG_PTR h = (ULONG_PTR)Handle >> 2; + Object_t *Object; - SourceProcessData = ConsoleGetPerProcessData(SourceProcess); + RtlEnterCriticalSection(&ProcessData->HandleTableLock); - // TODO: Check if one of the processes is really a CONSOLE. - /* - if (!(CreateProcessRequest->CreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS))) + if (h >= ProcessData->HandleTableSize || + (Object = ProcessData->HandleTable[h].Object) == NULL) { - // NewProcess == TargetProcess. - NewProcess->ParentConsole = Process->Console; - NewProcess->bInheritHandles = CreateProcessRequest->bInheritHandles; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; } - */ - /* Only inherit if the if the flag was set */ - if (!TargetProcessData->bInheritHandles) return STATUS_SUCCESS; + DPRINT1("ConSrvRemoveObject - Process 0x%p, Release 0x%p\n", ProcessData->Process, &ProcessData->HandleTable[h]); + ConSrvCloseHandleEntry(&ProcessData->HandleTable[h]); + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +ConSrvGetObject(PCONSOLE_PROCESS_DATA ProcessData, + HANDLE Handle, + Object_t** Object, + PCONSOLE_IO_HANDLE* Entry OPTIONAL, + DWORD Access, + BOOL LockConsole, + ULONG Type) +{ + ULONG_PTR h = (ULONG_PTR)Handle >> 2; + PCONSOLE_IO_HANDLE HandleEntry = NULL; + Object_t* ObjectEntry = NULL; + + ASSERT(Object); + if (Entry) *Entry = NULL; - if (TargetProcessData->HandleTableSize) + // DPRINT("ConSrvGetObject, Object: %x, %x, %x\n", + // Object, Handle, ProcessData ? ProcessData->HandleTableSize : 0); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + if ( IsConsoleHandle(Handle) && + h < ProcessData->HandleTableSize ) { - return STATUS_INVALID_PARAMETER; + HandleEntry = &ProcessData->HandleTable[h]; + ObjectEntry = HandleEntry->Object; } - RtlEnterCriticalSection(&SourceProcessData->HandleTableLock); + if ( HandleEntry == NULL || + ObjectEntry == NULL || + (HandleEntry->Access & Access) == 0 || + (Type != 0 && ObjectEntry->Type != Type) ) + { + DPRINT1("ConSrvGetObject returning invalid handle (%x) of type %lu with access %lu\n", Handle, Type, Access); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } - TargetProcessData->HandleTable = RtlAllocateHeap(ConSrvHeap, - HEAP_ZERO_MEMORY, - SourceProcessData->HandleTableSize - * sizeof(CSRSS_HANDLE)); - if (TargetProcessData->HandleTable == NULL) + _InterlockedIncrement(&ObjectEntry->Console->ReferenceCount); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + if (LockConsole) EnterCriticalSection(&ObjectEntry->Console->Lock); + + /* Return the objects to the caller */ + *Object = ObjectEntry; + if (Entry) *Entry = HandleEntry; + + return STATUS_SUCCESS; +} + +VOID +FASTCALL +ConSrvReleaseObject(Object_t *Object, + BOOL IsConsoleLocked) +{ + ConSrvReleaseConsole(Object->Console, IsConsoleLocked); +} + +NTSTATUS +FASTCALL +ConSrvAllocateConsole(PCONSOLE_PROCESS_DATA ProcessData, + LPCWSTR AppPath, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle, + PCONSOLE_START_INFO ConsoleStartInfo) +{ + NTSTATUS Status = STATUS_SUCCESS; + + /* Initialize a new Console owned by this process */ + Status = ConSrvInitConsole(&ProcessData->Console, AppPath, ConsoleStartInfo, ProcessData->Process); + if (!NT_SUCCESS(Status)) { - RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); - return STATUS_UNSUCCESSFUL; + DPRINT1("Console initialization failed\n"); + return Status; } - TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize; + /* Initialize the handles table */ + Status = ConSrvInitHandlesTable(ProcessData, + pInputHandle, + pOutputHandle, + pErrorHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to initialize the handles table\n"); + ConSrvDeleteConsole(ProcessData->Console); + ProcessData->Console = NULL; + return Status; + } - for (i = 0; i < SourceProcessData->HandleTableSize; i++) + /* Duplicate the Input Event */ + Status = NtDuplicateObject(NtCurrentProcess(), + ProcessData->Console->InputBuffer.ActiveEvent, + ProcessData->Process->ProcessHandle, + &ProcessData->ConsoleEvent, + EVENT_ALL_ACCESS, 0, 0); + if (!NT_SUCCESS(Status)) { - if (SourceProcessData->HandleTable[i].Object != NULL && - SourceProcessData->HandleTable[i].Inheritable) + DPRINT1("NtDuplicateObject() failed: %lu\n", Status); + ConSrvFreeHandlesTable(ProcessData); + ConSrvDeleteConsole(ProcessData->Console); + ProcessData->Console = NULL; + return Status; + } + + /* Insert the process into the processes list of the console */ + InsertHeadList(&ProcessData->Console->ProcessList, &ProcessData->ConsoleLink); + + /* Add a reference count because the process is tied to the console */ + _InterlockedIncrement(&ProcessData->Console->ReferenceCount); + + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +ConSrvInheritConsole(PCONSOLE_PROCESS_DATA ProcessData, + PCONSOLE Console, + BOOL CreateNewHandlesTable, + PHANDLE pInputHandle, + PHANDLE pOutputHandle, + PHANDLE pErrorHandle) +{ + NTSTATUS Status = STATUS_SUCCESS; + + /* Inherit the console */ + ProcessData->Console = Console; + + if (CreateNewHandlesTable) + { + /* Initialize the handles table */ + Status = ConSrvInitHandlesTable(ProcessData, + pInputHandle, + pOutputHandle, + pErrorHandle); + if (!NT_SUCCESS(Status)) { - TargetProcessData->HandleTable[i] = SourceProcessData->HandleTable[i]; - Win32CsrCreateHandleEntry(&TargetProcessData->HandleTable[i]); + DPRINT1("Failed to initialize the handles table\n"); + ProcessData->Console = NULL; + return Status; } } - RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); + /* Duplicate the Input Event */ + Status = NtDuplicateObject(NtCurrentProcess(), + ProcessData->Console->InputBuffer.ActiveEvent, + ProcessData->Process->ProcessHandle, + &ProcessData->ConsoleEvent, + EVENT_ALL_ACCESS, 0, 0); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtDuplicateObject() failed: %lu\n", Status); + ConSrvFreeHandlesTable(ProcessData); // NOTE: Always free the handles table. + ProcessData->Console = NULL; + return Status; + } + + /* Insert the process into the processes list of the console */ + InsertHeadList(&ProcessData->Console->ProcessList, &ProcessData->ConsoleLink); + + /* Add a reference count because the process is tied to the console */ + _InterlockedIncrement(&ProcessData->Console->ReferenceCount); return STATUS_SUCCESS; } VOID -WINAPI -Win32CsrReleaseConsole(PCSR_PROCESS Process) +FASTCALL +ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData) { - PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(Process); - PCSRSS_CONSOLE Console; - ULONG i; + PCONSOLE Console; - /* Close all console handles and detach process from console */ - RtlEnterCriticalSection(&ProcessData->HandleTableLock); + DPRINT1("ConSrvRemoveConsole\n"); - for (i = 0; i < ProcessData->HandleTableSize; i++) - Win32CsrCloseHandleEntry(&ProcessData->HandleTable[i]); - ProcessData->HandleTableSize = 0; - RtlFreeHeap(ConSrvHeap, 0, ProcessData->HandleTable); - ProcessData->HandleTable = NULL; + /* Close all console handles and free the handle table memory */ + ConSrvFreeHandlesTable(ProcessData); + /* Detach process from console */ Console = ProcessData->Console; if (Console != NULL) { + DPRINT1("ConSrvRemoveConsole - Console->ReferenceCount = %lu - We are going to decrement it !\n", Console->ReferenceCount); ProcessData->Console = NULL; + EnterCriticalSection(&Console->Lock); + DPRINT1("ConSrvRemoveConsole - Locking OK\n"); + + /* Remove ourselves from the console's list of processes */ RemoveEntryList(&ProcessData->ConsoleLink); - LeaveCriticalSection(&Console->Lock); - if (_InterlockedDecrement(&Console->ReferenceCount) == 0) - ConioDeleteConsole(&Console->Header); + + /* Update the console leader process */ + // SetConsoleWndConsoleLeaderCID(Console); + + /* Release the console */ + ConSrvReleaseConsole(Console, TRUE); //CloseHandle(ProcessData->ConsoleEvent); //ProcessData->ConsoleEvent = NULL; } - RtlLeaveCriticalSection(&ProcessData->HandleTableLock); } NTSTATUS FASTCALL -Win32CsrInsertObject(PCONSOLE_PROCESS_DATA ProcessData, - PHANDLE Handle, - Object_t *Object, - DWORD Access, - BOOL Inheritable, - DWORD ShareMode) +ConSrvGetConsole(PCONSOLE_PROCESS_DATA ProcessData, + PCONSOLE* Console, + BOOL LockConsole) { - ULONG i; - PCSRSS_HANDLE Block; + PCONSOLE ProcessConsole; RtlEnterCriticalSection(&ProcessData->HandleTableLock); + ProcessConsole = ProcessData->Console; - for (i = 0; i < ProcessData->HandleTableSize; i++) + if (!ProcessConsole) { - if (ProcessData->HandleTable[i].Object == NULL) + *Console = NULL; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + + InterlockedIncrement(&ProcessConsole->ReferenceCount); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + if (LockConsole) EnterCriticalSection(&ProcessConsole->Lock); + + *Console = ProcessConsole; + + return STATUS_SUCCESS; +} + +VOID FASTCALL +ConSrvReleaseConsole(PCONSOLE Console, + BOOL IsConsoleLocked) +{ + if (IsConsoleLocked) LeaveCriticalSection(&Console->Lock); + + /* Decrement reference count */ + if (_InterlockedDecrement(&Console->ReferenceCount) == 0) + ConSrvDeleteConsole(Console); +} + +NTSTATUS +NTAPI +ConSrvNewProcess(PCSR_PROCESS SourceProcess, + PCSR_PROCESS TargetProcess) +{ + /************************************************************************** + * This function is called whenever a new process (GUI or CUI) is created. + * + * Copy the parent's handles table here if both the parent and the child + * processes are CUI. If we must actually create our proper console (and + * thus do not inherit from the console handles of the parent's), then we + * will clean this table in the next ConSrvConnect call. Why we are doing + * this? It's because here, we still don't know whether or not we must create + * a new console instead of inherit it from the parent, and, because in + * ConSrvConnect we don't have any reference to the parent process anymore. + **************************************************************************/ + + PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData; + + /* An empty target process is invalid */ + if (!TargetProcess) return STATUS_INVALID_PARAMETER; + + TargetProcessData = ConsoleGetPerProcessData(TargetProcess); + + /**** HACK !!!! ****/ RtlZeroMemory(TargetProcessData, sizeof(*TargetProcessData)); + + /* Initialize the new (target) process */ + TargetProcessData->Process = TargetProcess; + TargetProcessData->ConsoleEvent = NULL; + TargetProcessData->Console = TargetProcessData->ParentConsole = NULL; + TargetProcessData->ConsoleApp = ((TargetProcess->Flags & CsrProcessIsConsoleApp) ? TRUE : FALSE); + + // Testing + TargetProcessData->HandleTableSize = 0; + TargetProcessData->HandleTable = NULL; + + RtlInitializeCriticalSection(&TargetProcessData->HandleTableLock); + + /* Do nothing if the source process is NULL */ + if (!SourceProcess) return STATUS_SUCCESS; + + SourceProcessData = ConsoleGetPerProcessData(SourceProcess); + + /* + * If both of the processes (parent and new child) are console applications, + * then try to inherit handles from the parent process. + */ + if ( SourceProcessData->Console != NULL && /* SourceProcessData->ConsoleApp */ + TargetProcessData->ConsoleApp ) + { + NTSTATUS Status; + + Status = ConSrvInheritHandlesTable(SourceProcessData, TargetProcessData); + if (!NT_SUCCESS(Status)) return Status; + + /* Temporary save the parent's console */ + TargetProcessData->ParentConsole = SourceProcessData->Console; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +ConSrvConnect(IN PCSR_PROCESS CsrProcess, + IN OUT PVOID ConnectionInfo, + IN OUT PULONG ConnectionInfoLength) +{ + /************************************************************************** + * This function is called whenever a CUI new process is created. + **************************************************************************/ + + NTSTATUS Status = STATUS_SUCCESS; + PCONSOLE_CONNECTION_INFO ConnectInfo = (PCONSOLE_CONNECTION_INFO)ConnectionInfo; + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess); + + if ( ConnectionInfo == NULL || + ConnectionInfoLength == NULL || + *ConnectionInfoLength != sizeof(CONSOLE_CONNECTION_INFO) ) + { + DPRINT1("CONSRV: Connection failed\n"); + return STATUS_UNSUCCESSFUL; + } + + /* If we don't need a console, then get out of here */ + if (!ConnectInfo->ConsoleNeeded || !ProcessData->ConsoleApp) // In fact, it is for GUI apps. + { + return STATUS_SUCCESS; + } + + /* If we don't have a console, then create a new one... */ + if (!ConnectInfo->Console || + ConnectInfo->Console != ProcessData->ParentConsole) + { + DPRINT1("ConSrvConnect - Allocate a new console\n"); + + /* + * We are about to create a new console. However when ConSrvNewProcess + * was called, we didn't know that we wanted to create a new console and + * therefore, we by default inherited the handles table from our parent + * process. It's only now that we notice that in fact we do not need + * them, because we've created a new console and thus we must use it. + * + * Therefore, free the console we can have and our handles table, + * and recreate a new one later on. + */ + ConSrvRemoveConsole(ProcessData); + + /* Initialize a new Console owned by the Console Leader Process */ + Status = ConSrvAllocateConsole(ProcessData, + ConnectInfo->AppPath, + &ConnectInfo->InputHandle, + &ConnectInfo->OutputHandle, + &ConnectInfo->ErrorHandle, + &ConnectInfo->ConsoleStartInfo); + if (!NT_SUCCESS(Status)) { - break; + DPRINT1("Console allocation failed\n"); + return Status; } } - if (i >= ProcessData->HandleTableSize) + else /* We inherit it from the parent */ { - Block = RtlAllocateHeap(ConSrvHeap, - HEAP_ZERO_MEMORY, - (ProcessData->HandleTableSize + 64) * sizeof(CSRSS_HANDLE)); - if (Block == NULL) + DPRINT1("ConSrvConnect - Reuse current (parent's) console\n"); + + /* Reuse our current console */ + Status = ConSrvInheritConsole(ProcessData, + ConnectInfo->Console, + FALSE, + NULL, // &ConnectInfo->InputHandle, + NULL, // &ConnectInfo->OutputHandle, + NULL); // &ConnectInfo->ErrorHandle); + if (!NT_SUCCESS(Status)) { - RtlLeaveCriticalSection(&ProcessData->HandleTableLock); - return(STATUS_UNSUCCESSFUL); + DPRINT1("Console inheritance failed\n"); + return Status; } - RtlCopyMemory(Block, - ProcessData->HandleTable, - ProcessData->HandleTableSize * sizeof(CSRSS_HANDLE)); - RtlFreeHeap(ConSrvHeap, 0, ProcessData->HandleTable); - ProcessData->HandleTable = Block; - ProcessData->HandleTableSize += 64; } - ProcessData->HandleTable[i].Object = Object; - ProcessData->HandleTable[i].Access = Access; - ProcessData->HandleTable[i].Inheritable = Inheritable; - ProcessData->HandleTable[i].ShareMode = ShareMode; - Win32CsrCreateHandleEntry(&ProcessData->HandleTable[i]); - *Handle = UlongToHandle((i << 2) | 0x3); - RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + /* Return it to the caller */ + ConnectInfo->Console = ProcessData->Console; + + /* Input Wait Handle */ + ConnectInfo->InputWaitHandle = ProcessData->ConsoleEvent; + + /* Set the Property Dialog Handler */ + ProcessData->PropDispatcher = ConnectInfo->PropDispatcher; + + /* Set the Ctrl Dispatcher */ + ProcessData->CtrlDispatcher = ConnectInfo->CtrlDispatcher; + return STATUS_SUCCESS; } +VOID +NTAPI +ConSrvDisconnect(PCSR_PROCESS Process) +{ + PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(Process); + + /************************************************************************** + * This function is called whenever a new process (GUI or CUI) is destroyed. + **************************************************************************/ + + DPRINT1("ConSrvDisconnect\n"); + + if ( ProcessData->Console != NULL || + ProcessData->HandleTable != NULL ) + { + DPRINT1("ConSrvDisconnect - calling ConSrvRemoveConsole\n"); + ConSrvRemoveConsole(ProcessData); + } + + RtlDeleteCriticalSection(&ProcessData->HandleTableLock); +} + + + CSR_API(SrvCloseHandle) { - PCSRSS_CLOSE_HANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest; + PCONSOLE_CLOSEHANDLE CloseHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.CloseHandleRequest; - return Win32CsrReleaseObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process), - CloseHandleRequest->Handle); + return ConSrvRemoveObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + CloseHandleRequest->ConsoleHandle); } CSR_API(SrvVerifyConsoleIoHandle) { NTSTATUS Status = STATUS_SUCCESS; - PCSRSS_VERIFY_HANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest; + PCONSOLE_VERIFYHANDLE VerifyHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.VerifyHandleRequest; PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); - ULONG_PTR Index; + HANDLE ConsoleHandle = VerifyHandleRequest->ConsoleHandle; + ULONG_PTR Index = (ULONG_PTR)ConsoleHandle >> 2; - Index = (ULONG_PTR)VerifyHandleRequest->Handle >> 2; RtlEnterCriticalSection(&ProcessData->HandleTableLock); - if (Index >= ProcessData->HandleTableSize || + + if (!IsConsoleHandle(ConsoleHandle) || + Index >= ProcessData->HandleTableSize || ProcessData->HandleTable[Index].Object == NULL) { - DPRINT("CsrVerifyObject failed\n"); + DPRINT("SrvVerifyConsoleIoHandle failed\n"); Status = STATUS_INVALID_HANDLE; } + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return Status; @@ -316,18 +790,20 @@ CSR_API(SrvVerifyConsoleIoHandle) CSR_API(SrvDuplicateHandle) { - ULONG_PTR Index; - PCSRSS_HANDLE Entry; + PCONSOLE_IO_HANDLE Entry; DWORD DesiredAccess; - PCSRSS_DUPLICATE_HANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest; + PCONSOLE_DUPLICATEHANDLE DuplicateHandleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.DuplicateHandleRequest; PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process); + HANDLE ConsoleHandle = DuplicateHandleRequest->ConsoleHandle; + ULONG_PTR Index = (ULONG_PTR)ConsoleHandle >> 2; - Index = (ULONG_PTR)DuplicateHandleRequest->Handle >> 2; RtlEnterCriticalSection(&ProcessData->HandleTableLock); - if (Index >= ProcessData->HandleTableSize - || (Entry = &ProcessData->HandleTable[Index])->Object == NULL) + + if ( /** !IsConsoleHandle(ConsoleHandle) || **/ + Index >= ProcessData->HandleTableSize || + (Entry = &ProcessData->HandleTable[Index])->Object == NULL) { - DPRINT1("Couldn't dup invalid handle %p\n", DuplicateHandleRequest->Handle); + DPRINT1("Couldn't duplicate invalid handle %p\n", ConsoleHandle); RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return STATUS_INVALID_HANDLE; } @@ -340,39 +816,29 @@ CSR_API(SrvDuplicateHandle) { DesiredAccess = DuplicateHandleRequest->Access; /* Make sure the source handle has all the desired flags */ - if (~Entry->Access & DesiredAccess) + if ((Entry->Access & DesiredAccess) == 0) { DPRINT1("Handle %p only has access %X; requested %X\n", - DuplicateHandleRequest->Handle, Entry->Access, DesiredAccess); + ConsoleHandle, Entry->Access, DesiredAccess); RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return STATUS_INVALID_PARAMETER; } } - ApiMessage->Status = Win32CsrInsertObject(ProcessData, - &DuplicateHandleRequest->Handle, - Entry->Object, - DesiredAccess, - DuplicateHandleRequest->Inheritable, - Entry->ShareMode); - if (NT_SUCCESS(ApiMessage->Status) - && DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE) + ApiMessage->Status = ConSrvInsertObject(ProcessData, + &DuplicateHandleRequest->ConsoleHandle, // Use the new handle value! + Entry->Object, + DesiredAccess, + DuplicateHandleRequest->Inheritable, + Entry->ShareMode); + if (NT_SUCCESS(ApiMessage->Status) && + DuplicateHandleRequest->Options & DUPLICATE_CLOSE_SOURCE) { - Win32CsrCloseHandleEntry(Entry); + ConSrvCloseHandleEntry(Entry); } RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return ApiMessage->Status; } -CSR_API(CsrGetInputWaitHandle) -{ - PCSRSS_GET_INPUT_WAIT_HANDLE GetConsoleInputWaitHandle = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleInputWaitHandle; - - GetConsoleInputWaitHandle->InputWaitHandle = - ConsoleGetPerProcessData(CsrGetClientThread()->Process)->ConsoleEvent; - - return STATUS_SUCCESS; -} - /* EOF */