You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1820 lines
47 KiB
1820 lines
47 KiB
/*++
|
|
|
|
Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
handle.c
|
|
|
|
Abstract:
|
|
|
|
This file manages console and io handles.
|
|
|
|
Author:
|
|
|
|
Therese Stowell (thereses) 16-Nov-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// array of pointers to consoles
|
|
//
|
|
|
|
PCONSOLE_INFORMATION InitialConsoleHandles[CONSOLE_INITIAL_CONSOLES];
|
|
PCONSOLE_INFORMATION *ConsoleHandles;
|
|
ULONG NumberOfConsoleHandles;
|
|
|
|
CRITICAL_SECTION ConsoleHandleLock; // serializes console handle table access
|
|
|
|
ULONG ConsoleId = 47; // unique number identifying console
|
|
|
|
//
|
|
// Macros to manipulate console handles
|
|
//
|
|
|
|
#define HandleFromIndex(i) (LongToHandle(((i & 0xFFFF) | (ConsoleId++ << 16))))
|
|
#define IndexFromHandle(h) ((USHORT)((ULONG_PTR)h & 0xFFFF))
|
|
#define ConsoleHandleTableLocked() (ConsoleHandleLock.OwningThread == NtCurrentTeb()->ClientId.UniqueThread)
|
|
|
|
VOID
|
|
AddProcessToList(
|
|
IN OUT PCONSOLE_INFORMATION Console,
|
|
IN OUT PCONSOLE_PROCESS_HANDLE ProcessHandleRecord,
|
|
IN HANDLE ProcessHandle
|
|
);
|
|
|
|
VOID
|
|
FreeInputHandle(
|
|
IN PHANDLE_DATA HandleData
|
|
);
|
|
|
|
|
|
#if DBG
|
|
VOID RefConsole(
|
|
PCONSOLE_INFORMATION Console)
|
|
{
|
|
PCONSOLE_REF_NODE pNode;
|
|
|
|
UserAssert(Console->RefCount < 0xFFFFFFFF);
|
|
Console->RefCount += 1;
|
|
|
|
pNode = ConsoleHeapAlloc(TMP_TAG, sizeof(CONSOLE_REF_NODE));
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(pNode, sizeof(CONSOLE_REF_NODE));
|
|
RtlWalkFrameChain(pNode->pStackTrace, ARRAY_SIZE(pNode->pStackTrace), 0);
|
|
pNode->bRef = TRUE;
|
|
pNode->pNext = Console->pRefNodes;
|
|
Console->pRefNodes = pNode;
|
|
}
|
|
|
|
VOID DerefConsole(
|
|
PCONSOLE_INFORMATION Console)
|
|
{
|
|
PCONSOLE_REF_NODE pNode;
|
|
|
|
UserAssert(Console->RefCount > 0);
|
|
Console->RefCount -= 1;
|
|
|
|
pNode = ConsoleHeapAlloc(TMP_TAG, sizeof(CONSOLE_REF_NODE));
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(pNode, sizeof(CONSOLE_REF_NODE));
|
|
RtlWalkFrameChain(pNode->pStackTrace, ARRAY_SIZE(pNode->pStackTrace), 0);
|
|
pNode->bRef = FALSE;
|
|
pNode->pNext = Console->pRefNodes;
|
|
Console->pRefNodes = pNode;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
InitializeConsoleHandleTable( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the global console handle table.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RtlInitializeCriticalSectionAndSpinCount(&ConsoleHandleLock,
|
|
0x80000000);
|
|
|
|
RtlZeroMemory(InitialConsoleHandles, sizeof(InitialConsoleHandles));
|
|
ConsoleHandles = InitialConsoleHandles;
|
|
NumberOfConsoleHandles = NELEM(InitialConsoleHandles);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
LockConsoleHandleTable( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locks the global console handle table. It also verifies
|
|
that we're not in the USER critical section. This is necessary to
|
|
prevent potential deadlocks. This routine is only defined in debug
|
|
builds.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
RtlEnterCriticalSection(&ConsoleHandleLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
UnlockConsoleHandleTable( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unlocks the global console handle table. This routine
|
|
is only defined in debug builds.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
RtlLeaveCriticalSection(&ConsoleHandleLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
LockConsole(
|
|
IN PCONSOLE_INFORMATION Console)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locks the console. This routine is only defined
|
|
in debug builds.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(!ConsoleHandleTableLocked());
|
|
RtlEnterCriticalSection(&(Console->ConsoleLock));
|
|
ASSERT(ConsoleLocked(Console));
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
NTSTATUS
|
|
DereferenceConsoleHandle(
|
|
IN HANDLE ConsoleHandle,
|
|
OUT PCONSOLE_INFORMATION *Console)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a console handle value into a pointer to the
|
|
console data structure.
|
|
|
|
Arguments:
|
|
|
|
ConsoleHandle - console handle to convert.
|
|
|
|
Console - On output, contains pointer to the console data structure.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Note:
|
|
|
|
The console handle table lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
ASSERT(ConsoleHandleTableLocked());
|
|
|
|
i = IndexFromHandle(ConsoleHandle);
|
|
if ((i >= NumberOfConsoleHandles) ||
|
|
((*Console = ConsoleHandles[i]) == NULL) ||
|
|
((*Console)->ConsoleHandle != ConsoleHandle)) {
|
|
*Console = NULL;
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
if ((*Console)->Flags & CONSOLE_TERMINATING) {
|
|
*Console = NULL;
|
|
return STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
GrowConsoleHandleTable( VOID )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine grows the console handle table.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONSOLE_INFORMATION *NewTable;
|
|
PCONSOLE_INFORMATION *OldTable;
|
|
ULONG i;
|
|
ULONG MaxConsoleHandles;
|
|
|
|
ASSERT(ConsoleHandleTableLocked());
|
|
|
|
MaxConsoleHandles = NumberOfConsoleHandles + CONSOLE_CONSOLE_HANDLE_INCREMENT;
|
|
ASSERT(MaxConsoleHandles <= 0xFFFF);
|
|
NewTable = ConsoleHeapAlloc(HANDLE_TAG, MaxConsoleHandles * sizeof(PCONSOLE_INFORMATION));
|
|
if (NewTable == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
RtlCopyMemory(NewTable, ConsoleHandles,
|
|
NumberOfConsoleHandles * sizeof(PCONSOLE_INFORMATION));
|
|
for (i=NumberOfConsoleHandles;i<MaxConsoleHandles;i++) {
|
|
NewTable[i] = NULL;
|
|
}
|
|
OldTable = ConsoleHandles;
|
|
ConsoleHandles = NewTable;
|
|
NumberOfConsoleHandles = MaxConsoleHandles;
|
|
if (OldTable != InitialConsoleHandles) {
|
|
ConsoleHeapFree(OldTable);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AllocateConsoleHandle(
|
|
OUT PHANDLE Handle)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a console handle from the global table.
|
|
|
|
Arguments:
|
|
|
|
Handle - Pointer to store handle in.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console handle table lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(ConsoleHandleTableLocked());
|
|
|
|
//
|
|
// have to start allocation at 1 because 0 indicates no console handle
|
|
// in ConDllInitialize.
|
|
//
|
|
|
|
for (i=1;i<NumberOfConsoleHandles;i++) {
|
|
if (ConsoleHandles[i] == NULL) {
|
|
ConsoleHandles[i] = (PCONSOLE_INFORMATION) CONSOLE_HANDLE_ALLOCATED;
|
|
*Handle = HandleFromIndex(i);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// grow console handle table
|
|
//
|
|
|
|
Status = GrowConsoleHandleTable();
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
for ( ;i<NumberOfConsoleHandles;i++) {
|
|
if (ConsoleHandles[i] == NULL) {
|
|
ConsoleHandles[i] = (PCONSOLE_INFORMATION) CONSOLE_HANDLE_ALLOCATED;
|
|
*Handle = HandleFromIndex(i);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
ASSERT (FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FreeConsoleHandle(
|
|
IN HANDLE Handle)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a console handle from the global table.
|
|
|
|
Arguments:
|
|
|
|
Handle - Handle to free.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console handle table lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
ASSERT(ConsoleHandleTableLocked());
|
|
|
|
ASSERT (Handle != NULL);
|
|
i = IndexFromHandle(Handle);
|
|
if ((i >= NumberOfConsoleHandles) || (ConsoleHandles[i] == NULL)) {
|
|
ASSERT (FALSE);
|
|
} else {
|
|
ConsoleHandles[i] = NULL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ValidateConsole(
|
|
IN PCONSOLE_INFORMATION Console)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine ensures that the given console pointer is valid.
|
|
|
|
Arguments:
|
|
|
|
Console - Console pointer to validate.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
if (Console != NULL) {
|
|
for (i = 0; i < NumberOfConsoleHandles; i++) {
|
|
if (ConsoleHandles[i] == Console) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeIoHandleTable(
|
|
IN OUT PCONSOLE_INFORMATION Console,
|
|
OUT PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
OUT PHANDLE StdIn,
|
|
OUT PHANDLE StdOut,
|
|
OUT PHANDLE StdErr)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a process's handle table for the first
|
|
time (there is no parent process). It also sets up stdin, stdout,
|
|
and stderr.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
Stdin - Pointer in which to return StdIn handle.
|
|
|
|
StdOut - Pointer in which to return StdOut handle.
|
|
|
|
StdErr - Pointer in which to return StdErr handle.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
PHANDLE_DATA HandleData, InputHandleData;
|
|
|
|
//
|
|
// HandleTablePtr gets set up by ConsoleAddProcessRoutine.
|
|
// it will be != to HandleTable if the new process was created
|
|
// using "start xxx" at the command line and cmd.exe has >
|
|
// CONSOLE_INITIAL_IO_HANDLES.
|
|
//
|
|
|
|
if (ProcessData->HandleTablePtr != ProcessData->HandleTable) {
|
|
UserAssert(ProcessData->HandleTableSize != CONSOLE_INITIAL_IO_HANDLES);
|
|
ConsoleHeapFree(ProcessData->HandleTablePtr);
|
|
ProcessData->HandleTablePtr = ProcessData->HandleTable;
|
|
}
|
|
|
|
for (i = 0;i < CONSOLE_INITIAL_IO_HANDLES; i++) {
|
|
ProcessData->HandleTable[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
}
|
|
|
|
ProcessData->HandleTableSize = CONSOLE_INITIAL_IO_HANDLES;
|
|
|
|
//
|
|
// Set up stdin, stdout, and stderr.
|
|
//
|
|
// stdin
|
|
//
|
|
|
|
Status = AllocateIoHandle(ProcessData, CONSOLE_INPUT_HANDLE, &Handle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = DereferenceIoHandleNoCheck(ProcessData, Handle, &InputHandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (!InitializeInputHandle(InputHandleData, &Console->InputBuffer)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
InputHandleData->HandleType |= CONSOLE_INHERITABLE;
|
|
|
|
Status = ConsoleAddShare(GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
&Console->InputBuffer.ShareAccess,
|
|
InputHandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
*StdIn = INDEX_TO_HANDLE(Handle);
|
|
|
|
//
|
|
// stdout
|
|
//
|
|
|
|
Status = AllocateIoHandle(ProcessData, CONSOLE_OUTPUT_HANDLE, &Handle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = DereferenceIoHandleNoCheck(ProcessData, Handle, &HandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeOutputHandle(HandleData,Console->CurrentScreenBuffer);
|
|
HandleData->HandleType |= CONSOLE_INHERITABLE;
|
|
Status = ConsoleAddShare(GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
&Console->ScreenBuffers->ShareAccess,
|
|
HandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
*StdOut = INDEX_TO_HANDLE(Handle);
|
|
|
|
//
|
|
// stderr
|
|
//
|
|
|
|
Status = AllocateIoHandle(ProcessData, CONSOLE_OUTPUT_HANDLE, &Handle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = DereferenceIoHandleNoCheck(ProcessData, Handle, &HandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeOutputHandle(HandleData,Console->CurrentScreenBuffer);
|
|
HandleData->HandleType |= CONSOLE_INHERITABLE;
|
|
Status = ConsoleAddShare(GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
&Console->ScreenBuffers->ShareAccess,
|
|
HandleData);
|
|
UserAssert(NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto Cleanup;
|
|
}
|
|
*StdErr = INDEX_TO_HANDLE(Handle);
|
|
return STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
FreeInputHandle(InputHandleData);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
InheritIoHandleTable(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN PCONSOLE_PER_PROCESS_DATA ParentProcessData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a process's handle table from the parent
|
|
process's handle table. ProcessData contains the process data
|
|
copied directly from the parent to the child process by CSR.
|
|
This routine allocates a new handle table, if necessary, then
|
|
invalidates non-inherited handles and increments the sharing
|
|
and reference counts for inherited handles.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Copy handles from parent process. If the table size
|
|
// is CONSOLE_INITIAL_IO_HANDLES, CSR has done the copy
|
|
// for us.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER(Console);
|
|
|
|
ASSERT(ParentProcessData->HandleTableSize != 0);
|
|
ASSERT(ParentProcessData->HandleTableSize <= 0x0000FFFF);
|
|
|
|
if (ParentProcessData->HandleTableSize != CONSOLE_INITIAL_IO_HANDLES) {
|
|
ProcessData->HandleTableSize = ParentProcessData->HandleTableSize;
|
|
ProcessData->HandleTablePtr = ConsoleHeapAlloc(HANDLE_TAG, ProcessData->HandleTableSize * sizeof(HANDLE_DATA));
|
|
|
|
if (ProcessData->HandleTablePtr == NULL) {
|
|
ProcessData->HandleTablePtr = ProcessData->HandleTable;
|
|
ProcessData->HandleTableSize = CONSOLE_INITIAL_IO_HANDLES;
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
RtlCopyMemory(ProcessData->HandleTablePtr,
|
|
ParentProcessData->HandleTablePtr,
|
|
ProcessData->HandleTableSize * sizeof(HANDLE_DATA));
|
|
}
|
|
|
|
ASSERT(!(Console->Flags & CONSOLE_SHUTTING_DOWN));
|
|
|
|
//
|
|
// Allocate any memory associated with each handle.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
for (i = 0;i < ProcessData->HandleTableSize; i++) {
|
|
|
|
if (NT_SUCCESS(Status) && ProcessData->HandleTablePtr[i].HandleType & CONSOLE_INHERITABLE) {
|
|
|
|
if (ProcessData->HandleTablePtr[i].HandleType & CONSOLE_INPUT_HANDLE) {
|
|
ProcessData->HandleTablePtr[i].InputReadData = ConsoleHeapAlloc(HANDLE_TAG, sizeof(INPUT_READ_HANDLE_DATA));
|
|
if (!ProcessData->HandleTablePtr[i].InputReadData) {
|
|
ProcessData->HandleTablePtr[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
Status = STATUS_NO_MEMORY;
|
|
continue;
|
|
}
|
|
ProcessData->HandleTablePtr[i].InputReadData->InputHandleFlags = 0;
|
|
ProcessData->HandleTablePtr[i].InputReadData->ReadCount = 0;
|
|
Status = RtlInitializeCriticalSection(&ProcessData->HandleTablePtr[i].InputReadData->ReadCountLock);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ConsoleHeapFree(ProcessData->HandleTablePtr[i].InputReadData);
|
|
ProcessData->HandleTablePtr[i].InputReadData = NULL;
|
|
ProcessData->HandleTablePtr[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
ProcessData->HandleTablePtr[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If something failed, we need to free any input data we allocated and
|
|
// free the handle table.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
for (i=0;i<ProcessData->HandleTableSize;i++) {
|
|
if (ProcessData->HandleTablePtr[i].HandleType & CONSOLE_INPUT_HANDLE) {
|
|
FreeInputHandle(&ProcessData->HandleTablePtr[i]);
|
|
}
|
|
}
|
|
if (ProcessData->HandleTableSize != CONSOLE_INITIAL_IO_HANDLES) {
|
|
ConsoleHeapFree(ProcessData->HandleTablePtr);
|
|
ProcessData->HandleTablePtr = ProcessData->HandleTable;
|
|
ProcessData->HandleTableSize = CONSOLE_INITIAL_IO_HANDLES;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// All the memory allocations succeeded. Now go through and increment the
|
|
// object reference counts and dup the shares.
|
|
//
|
|
|
|
for (i=0;i<ProcessData->HandleTableSize;i++) {
|
|
if (ProcessData->HandleTablePtr[i].HandleType != CONSOLE_FREE_HANDLE) {
|
|
PCONSOLE_SHARE_ACCESS ShareAccess;
|
|
|
|
if (ProcessData->HandleTablePtr[i].HandleType & CONSOLE_INPUT_HANDLE) {
|
|
ProcessData->HandleTablePtr[i].Buffer.InputBuffer->RefCount++;
|
|
ShareAccess = &ProcessData->HandleTablePtr[i].Buffer.InputBuffer->ShareAccess;
|
|
} else {
|
|
ProcessData->HandleTablePtr[i].Buffer.ScreenBuffer->RefCount++;
|
|
ShareAccess = &ProcessData->HandleTablePtr[i].Buffer.ScreenBuffer->ShareAccess;
|
|
}
|
|
|
|
Status = ConsoleDupShare(ProcessData->HandleTablePtr[i].Access,
|
|
ProcessData->HandleTablePtr[i].ShareAccess,
|
|
ShareAccess,
|
|
&ProcessData->HandleTablePtr[i]
|
|
);
|
|
ASSERT (NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ConsoleAddProcessRoutine(
|
|
IN PCSR_PROCESS ParentProcess,
|
|
IN PCSR_PROCESS Process)
|
|
{
|
|
PCONSOLE_PER_PROCESS_DATA ProcessData, ParentProcessData;
|
|
PCONSOLE_INFORMATION Console;
|
|
PCONSOLE_PROCESS_HANDLE ProcessHandleRecord;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
ProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(Process);
|
|
ProcessData->HandleTablePtr = ProcessData->HandleTable;
|
|
ProcessData->HandleTableSize = CONSOLE_INITIAL_IO_HANDLES;
|
|
CONSOLE_SETCONSOLEAPPFROMPROCESSDATA(ProcessData,FALSE);
|
|
|
|
|
|
if (ParentProcess) {
|
|
ProcessData->RootProcess = FALSE;
|
|
ProcessData->ParentProcessId = HandleToUlong(ParentProcess->ClientId.UniqueProcess);
|
|
ParentProcessData = CONSOLE_FROMPROCESSPERPROCESSDATA(ParentProcess);
|
|
|
|
|
|
//
|
|
// If both the parent and new processes are console apps,
|
|
// inherit handles from the parent process.
|
|
//
|
|
|
|
if (ParentProcessData->ConsoleHandle != NULL &&
|
|
(Process->Flags & CSR_PROCESS_CONSOLEAPP)) {
|
|
if (!(NT_SUCCESS(RevalidateConsole(ParentProcessData->ConsoleHandle,
|
|
&Console)))) {
|
|
ProcessData->ConsoleHandle = NULL;
|
|
return STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
//
|
|
// Don't add the process if the console is being shutdown.
|
|
//
|
|
|
|
if (Console->Flags & CONSOLE_SHUTTING_DOWN) {
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
} else {
|
|
ProcessHandleRecord = ConsoleHeapAlloc(HANDLE_TAG, sizeof(CONSOLE_PROCESS_HANDLE));
|
|
if (ProcessHandleRecord == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
} else {
|
|
|
|
//
|
|
// duplicate parent's handle table
|
|
//
|
|
|
|
Status = InheritIoHandleTable(Console, ProcessData, ParentProcessData);
|
|
if (NT_SUCCESS(Status)) {
|
|
ProcessHandleRecord->Process = Process;
|
|
ProcessHandleRecord->CtrlRoutine = NULL;
|
|
ProcessHandleRecord->PropRoutine = NULL;
|
|
AddProcessToList(Console,ProcessHandleRecord,Process->ProcessHandle);
|
|
|
|
//
|
|
// increment console reference count
|
|
//
|
|
|
|
RefConsole(Console);
|
|
} else {
|
|
ConsoleHeapFree(ProcessHandleRecord);
|
|
}
|
|
}
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
ProcessData->ConsoleHandle = NULL;
|
|
for (i=0;i<CONSOLE_INITIAL_IO_HANDLES;i++) {
|
|
ProcessData->HandleTable[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
}
|
|
}
|
|
UnlockConsole(Console);
|
|
} else {
|
|
ProcessData->ConsoleHandle = NULL;
|
|
}
|
|
} else {
|
|
ProcessData->ConsoleHandle = NULL;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MapEventHandles(
|
|
IN HANDLE ClientProcessHandle,
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN OUT PCONSOLE_INFO ConsoleInfo)
|
|
{
|
|
if (!MapHandle(ClientProcessHandle,
|
|
Console->InitEvents[INITIALIZATION_SUCCEEDED],
|
|
&ConsoleInfo->InitEvents[INITIALIZATION_SUCCEEDED]
|
|
)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
if (!MapHandle(ClientProcessHandle,
|
|
Console->InitEvents[INITIALIZATION_FAILED],
|
|
&ConsoleInfo->InitEvents[INITIALIZATION_FAILED]
|
|
)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
if (!MapHandle(ClientProcessHandle,
|
|
Console->InputBuffer.InputWaitEvent,
|
|
&ConsoleInfo->InputWaitHandle
|
|
)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AllocateConsole(
|
|
IN HANDLE ConsoleHandle,
|
|
IN LPWSTR Title,
|
|
IN USHORT TitleLength,
|
|
IN HANDLE ClientProcessHandle,
|
|
OUT PHANDLE StdIn,
|
|
OUT PHANDLE StdOut,
|
|
OUT PHANDLE StdErr,
|
|
OUT PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN OUT PCONSOLE_INFO ConsoleInfo,
|
|
IN BOOLEAN WindowVisible,
|
|
IN DWORD dwConsoleThreadId)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates and initialized a console and its associated
|
|
data - input buffer and screen buffer.
|
|
|
|
Arguments:
|
|
|
|
ConsoleHandle - Handle of console to allocate.
|
|
|
|
dwWindowSize - Initial size of screen buffer window, in rows and columns.
|
|
|
|
nFont - Initial number of font text is displayed in.
|
|
|
|
dwScreenBufferSize - Initial size of screen buffer, in rows and columns.
|
|
|
|
nInputBufferSize - Initial size of input buffer, in events.
|
|
|
|
dwWindowFlags -
|
|
|
|
StdIn - On return, contains handle to stdin.
|
|
|
|
StdOut - On return, contains handle to stdout.
|
|
|
|
StdErr - On return, contains handle to stderr.
|
|
|
|
ProcessData - On return, contains the initialized per-process data.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console handle table lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONSOLE_INFORMATION Console;
|
|
NTSTATUS Status;
|
|
BOOL Success;
|
|
|
|
//
|
|
// allocate console data
|
|
//
|
|
|
|
Console = ConsoleHeapAlloc(CONSOLE_TAG | HEAP_ZERO_MEMORY,
|
|
sizeof(CONSOLE_INFORMATION));
|
|
if (Console == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
ConsoleHandles[IndexFromHandle(ConsoleHandle)] = Console;
|
|
|
|
Console->Flags = WindowVisible ? 0 : CONSOLE_NO_WINDOW;
|
|
Console->hIcon = ConsoleInfo->hIcon;
|
|
Console->hSmIcon = ConsoleInfo->hSmIcon;
|
|
Console->iIconId = ConsoleInfo->iIconId;
|
|
Console->dwHotKey = ConsoleInfo->dwHotKey;
|
|
#if !defined(FE_SB)
|
|
Console->CP = OEMCP;
|
|
Console->OutputCP = ConsoleOutputCP;
|
|
#endif
|
|
Console->ReserveKeys = CONSOLE_NOSHORTCUTKEY;
|
|
Console->ConsoleHandle = ConsoleHandle;
|
|
Console->bIconInit = TRUE;
|
|
Console->VerticalClientToWindow = VerticalClientToWindow;
|
|
Console->HorizontalClientToWindow = HorizontalClientToWindow;
|
|
#if defined(FE_SB)
|
|
SetConsoleCPInfo(Console,TRUE);
|
|
SetConsoleCPInfo(Console,FALSE);
|
|
#endif
|
|
|
|
//
|
|
// must wait for window to be destroyed or client impersonation won't
|
|
// work.
|
|
//
|
|
|
|
Status = NtDuplicateObject(NtCurrentProcess(),
|
|
CONSOLE_CLIENTTHREADHANDLE(CSR_SERVER_QUERYCLIENTTHREAD()),
|
|
NtCurrentProcess(),
|
|
&Console->ClientThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit5;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Make sure the handle isn't protected so we can close it later
|
|
//
|
|
UnProtectHandle(Console->ClientThreadHandle);
|
|
#endif // DBG
|
|
|
|
InitializeListHead(&Console->OutputQueue);
|
|
InitializeListHead(&Console->ProcessHandleList);
|
|
InitializeListHead(&Console->ExeAliasList);
|
|
InitializeListHead(&Console->MessageQueue);
|
|
|
|
Status = NtCreateEvent(&Console->InitEvents[INITIALIZATION_SUCCEEDED],
|
|
EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit4a;
|
|
}
|
|
Status = NtCreateEvent(&Console->InitEvents[INITIALIZATION_FAILED],
|
|
EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit4;
|
|
}
|
|
Status = RtlInitializeCriticalSection(&Console->ConsoleLock);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit3a;
|
|
}
|
|
InitializeConsoleCommandData(Console);
|
|
|
|
//
|
|
// initialize input buffer
|
|
//
|
|
|
|
#if defined(FE_SB)
|
|
Status = CreateInputBuffer(ConsoleInfo->nInputBufferSize,
|
|
&Console->InputBuffer,
|
|
Console);
|
|
#else
|
|
Status = CreateInputBuffer(ConsoleInfo->nInputBufferSize,
|
|
&Console->InputBuffer);
|
|
#endif
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit3;
|
|
}
|
|
|
|
Console->Title = ConsoleHeapAlloc(TITLE_TAG, TitleLength+sizeof(WCHAR));
|
|
if (Console->Title == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto ErrorExit2;
|
|
}
|
|
RtlCopyMemory(Console->Title,Title,TitleLength);
|
|
Console->Title[TitleLength/sizeof(WCHAR)] = (WCHAR)0; // NULL terminate
|
|
Console->TitleLength = TitleLength;
|
|
|
|
Console->OriginalTitle = TranslateConsoleTitle(Console->Title, &Console->OriginalTitleLength, TRUE, FALSE);
|
|
if (Console->OriginalTitle == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto ErrorExit1;
|
|
}
|
|
|
|
Status = NtCreateEvent(&Console->TerminationEvent,
|
|
EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit1a;
|
|
}
|
|
|
|
//
|
|
// initialize screen buffer. we don't call OpenConsole to do this
|
|
// because we need to specify the font, windowsize, etc.
|
|
//
|
|
|
|
Status = DoCreateScreenBuffer(Console,
|
|
ConsoleInfo);
|
|
if (!NT_SUCCESS(Status)){
|
|
goto ErrorExit1b;
|
|
}
|
|
|
|
|
|
Console->CurrentScreenBuffer = Console->ScreenBuffers;
|
|
#if defined(FE_SB)
|
|
#if defined(FE_IME)
|
|
SetUndetermineAttribute(Console);
|
|
#endif
|
|
Status = CreateEUDC(Console);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit1c;
|
|
}
|
|
#endif
|
|
|
|
Status = InitializeIoHandleTable(Console,
|
|
ProcessData,
|
|
StdIn,
|
|
StdOut,
|
|
StdErr);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit0;
|
|
}
|
|
|
|
//
|
|
// map event handles
|
|
//
|
|
|
|
Status = MapEventHandles(ClientProcessHandle, Console, ConsoleInfo);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ErrorExit0;
|
|
}
|
|
|
|
Success = PostThreadMessage(dwConsoleThreadId,
|
|
CM_CREATE_CONSOLE_WINDOW,
|
|
(WPARAM)ConsoleHandle,
|
|
(LPARAM)ClientProcessHandle);
|
|
if (!Success) {
|
|
RIPMSG1(RIP_WARNING, "PostThreadMessage failed 0x%x", GetLastError());
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto ErrorExit0;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
ErrorExit0: Console->ScreenBuffers->RefCount = 0;
|
|
#if defined(FE_SB)
|
|
if (Console->EudcInformation != NULL) {
|
|
ConsoleHeapFree(Console->EudcInformation);
|
|
}
|
|
ErrorExit1c:
|
|
#endif
|
|
FreeScreenBuffer(Console->ScreenBuffers);
|
|
ErrorExit1b: NtClose(Console->TerminationEvent);
|
|
ErrorExit1a: ConsoleHeapFree(Console->OriginalTitle);
|
|
ErrorExit1: ConsoleHeapFree(Console->Title);
|
|
ErrorExit2: Console->InputBuffer.RefCount = 0;
|
|
FreeInputBuffer(&Console->InputBuffer);
|
|
ErrorExit3: RtlDeleteCriticalSection(&Console->ConsoleLock);
|
|
|
|
ErrorExit3a: NtClose(Console->InitEvents[INITIALIZATION_FAILED]);
|
|
ErrorExit4: NtClose(Console->InitEvents[INITIALIZATION_SUCCEEDED]);
|
|
ErrorExit4a: NtClose(Console->ClientThreadHandle);
|
|
ErrorExit5: ConsoleHeapFree(Console);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
DestroyConsole(
|
|
IN PCONSOLE_INFORMATION Console)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a console structure if it's not being referenced.
|
|
|
|
Arguments:
|
|
|
|
Console - Console to free.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE ConsoleHandle = Console->ConsoleHandle;
|
|
|
|
//
|
|
// Make sure we have the console locked and it really is going away.
|
|
//
|
|
|
|
ASSERT(ConsoleLocked(Console));
|
|
ASSERT(Console->hWnd == NULL);
|
|
|
|
//
|
|
// Mark this console as being destroyed.
|
|
//
|
|
|
|
Console->Flags |= CONSOLE_IN_DESTRUCTION;
|
|
|
|
//
|
|
// Unlock this console.
|
|
//
|
|
|
|
RtlLeaveCriticalSection(&Console->ConsoleLock);
|
|
|
|
//
|
|
// If the console still exists and no one is waiting on it, free it.
|
|
//
|
|
|
|
LockConsoleHandleTable();
|
|
if (Console == ConsoleHandles[IndexFromHandle(ConsoleHandle)] &&
|
|
Console->ConsoleHandle == ConsoleHandle &&
|
|
Console->ConsoleLock.OwningThread == NULL &&
|
|
Console->WaitCount == 0) {
|
|
|
|
FreeConsoleHandle(ConsoleHandle);
|
|
RtlDeleteCriticalSection(&Console->ConsoleLock);
|
|
|
|
#if DBG
|
|
if (Console->pRefNodes != NULL) {
|
|
ConsoleHeapFree(Console->pRefNodes);
|
|
}
|
|
#endif
|
|
|
|
ConsoleHeapFree(Console);
|
|
}
|
|
UnlockConsoleHandleTable();
|
|
}
|
|
|
|
VOID
|
|
FreeCon(
|
|
IN PCONSOLE_INFORMATION Console)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a console and its associated
|
|
data - input buffer and screen buffer.
|
|
|
|
Arguments:
|
|
|
|
ConsoleHandle - Handle of console to free.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console handle table lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
HWND hWnd;
|
|
USERTHREAD_USEDESKTOPINFO utudi;
|
|
NTSTATUS Status;
|
|
|
|
Console->Flags |= CONSOLE_TERMINATING;
|
|
NtSetEvent(Console->TerminationEvent,NULL);
|
|
hWnd = Console->hWnd;
|
|
|
|
//
|
|
// Wait 10 seconds or until the input thread replies
|
|
// to synchronize the window destruction with
|
|
// the termination of the thread
|
|
//
|
|
|
|
if (hWnd != NULL) {
|
|
UnlockConsole(Console);
|
|
utudi.hThread = NULL;
|
|
utudi.drdRestore.pdeskRestore = NULL;
|
|
|
|
Status = NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseActiveDesktop,
|
|
&utudi, sizeof(utudi));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
SendMessageTimeout(hWnd, CM_DESTROY_WINDOW, 0, 0, SMTO_BLOCK, 10000, NULL);
|
|
Status = NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseDesktop,
|
|
&utudi,
|
|
sizeof(utudi));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
} else {
|
|
AbortCreateConsole(Console);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InsertScreenBuffer(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN PSCREEN_INFORMATION ScreenInfo)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts the screen buffer pointer into the console's
|
|
list of screen buffers.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
ScreenInfo - Pointer to screen information structure.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
ScreenInfo->Next = Console->ScreenBuffers;
|
|
Console->ScreenBuffers = ScreenInfo;
|
|
}
|
|
|
|
VOID
|
|
RemoveScreenBuffer(
|
|
IN PCONSOLE_INFORMATION Console,
|
|
IN PSCREEN_INFORMATION ScreenInfo)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the screen buffer pointer from the console's
|
|
list of screen buffers.
|
|
|
|
Arguments:
|
|
|
|
Console - Pointer to console information structure.
|
|
|
|
ScreenInfo - Pointer to screen information structure.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console lock must be held when calling this routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSCREEN_INFORMATION Prev,Cur;
|
|
|
|
if (ScreenInfo == Console->ScreenBuffers) {
|
|
Console->ScreenBuffers = ScreenInfo->Next;
|
|
return;
|
|
}
|
|
Prev = Cur = Console->ScreenBuffers;
|
|
while (Cur != NULL) {
|
|
if (ScreenInfo == Cur)
|
|
break;
|
|
Prev = Cur;
|
|
Cur = Cur->Next;
|
|
}
|
|
ASSERT (Cur != NULL);
|
|
if (Cur != NULL) {
|
|
Prev->Next = Cur->Next;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
GrowIoHandleTable(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine grows the per-process io handle table.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to the per-process data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PHANDLE_DATA NewTable;
|
|
ULONG i;
|
|
ULONG MaxFileHandles;
|
|
|
|
MaxFileHandles = ProcessData->HandleTableSize + CONSOLE_IO_HANDLE_INCREMENT;
|
|
NewTable = ConsoleHeapAlloc(HANDLE_TAG, MaxFileHandles * sizeof(HANDLE_DATA));
|
|
if (NewTable == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
RtlCopyMemory(NewTable, ProcessData->HandleTablePtr,
|
|
ProcessData->HandleTableSize * sizeof(HANDLE_DATA));
|
|
for (i=ProcessData->HandleTableSize;i<MaxFileHandles;i++) {
|
|
NewTable[i].HandleType = CONSOLE_FREE_HANDLE;
|
|
}
|
|
if (ProcessData->HandleTableSize != CONSOLE_INITIAL_IO_HANDLES) {
|
|
ConsoleHeapFree(ProcessData->HandleTablePtr);
|
|
}
|
|
ProcessData->HandleTablePtr = NewTable;
|
|
ProcessData->HandleTableSize = MaxFileHandles;
|
|
ASSERT(ProcessData->HandleTableSize != 0);
|
|
ASSERT(ProcessData->HandleTableSize <= 0x0000FFFF);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FreeProcessData(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees any per-process data allocated by the console.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to the per-process data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if (ProcessData->HandleTableSize != CONSOLE_INITIAL_IO_HANDLES) {
|
|
ConsoleHeapFree(ProcessData->HandleTablePtr);
|
|
ProcessData->HandleTablePtr = ProcessData->HandleTable;
|
|
ProcessData->HandleTableSize = CONSOLE_INITIAL_IO_HANDLES;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
InitializeOutputHandle(
|
|
PHANDLE_DATA HandleData,
|
|
PSCREEN_INFORMATION ScreenBuffer)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the output-specific fields of the handle data
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
HandleData - Pointer to handle data structure.
|
|
|
|
ScreenBuffer - Pointer to screen buffer data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
HandleData->Buffer.ScreenBuffer = ScreenBuffer;
|
|
HandleData->Buffer.ScreenBuffer->RefCount++;
|
|
}
|
|
|
|
BOOLEAN
|
|
InitializeInputHandle(
|
|
PHANDLE_DATA HandleData,
|
|
PINPUT_INFORMATION InputBuffer)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the input-specific fields of the handle data
|
|
structure.
|
|
|
|
Arguments:
|
|
|
|
HandleData - Pointer to handle data structure.
|
|
|
|
InputBuffer - Pointer to input buffer data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
HandleData->InputReadData = ConsoleHeapAlloc(HANDLE_TAG, sizeof(INPUT_READ_HANDLE_DATA));
|
|
if (!HandleData->InputReadData) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = RtlInitializeCriticalSection(&HandleData->InputReadData->ReadCountLock);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ConsoleHeapFree(HandleData->InputReadData);
|
|
HandleData->InputReadData = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
HandleData->InputReadData->ReadCount = 0;
|
|
HandleData->InputReadData->InputHandleFlags = 0;
|
|
HandleData->Buffer.InputBuffer = InputBuffer;
|
|
HandleData->Buffer.InputBuffer->RefCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FreeInputHandle(
|
|
IN PHANDLE_DATA HandleData)
|
|
{
|
|
if (HandleData->InputReadData) {
|
|
RtlDeleteCriticalSection(&HandleData->InputReadData->ReadCountLock);
|
|
ConsoleHeapFree(HandleData->InputReadData);
|
|
HandleData->InputReadData = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
AllocateIoHandle(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN ULONG HandleType,
|
|
OUT PHANDLE Handle)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates an input or output handle from the process's
|
|
handle table.
|
|
|
|
This routine initializes all non-type specific fields in the handle
|
|
data structure.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
HandleType - Flag indicating input or output handle.
|
|
|
|
Handle - On return, contains allocated handle. Handle is an index
|
|
internally. When returned to the API caller, it is translated into
|
|
a handle.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console lock must be held when calling this routine. The handle
|
|
is allocated from the per-process handle table. Holding the console
|
|
lock serializes both threads within the calling process and any other
|
|
process that shares the console.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
for (i = 0;i < ProcessData->HandleTableSize; i++) {
|
|
if (ProcessData->HandleTablePtr[i].HandleType == CONSOLE_FREE_HANDLE) {
|
|
ProcessData->HandleTablePtr[i].HandleType = HandleType;
|
|
*Handle = LongToHandle(i);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
Status = GrowIoHandleTable(ProcessData);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for ( ;i < ProcessData->HandleTableSize; i++) {
|
|
if (ProcessData->HandleTablePtr[i].HandleType == CONSOLE_FREE_HANDLE) {
|
|
ProcessData->HandleTablePtr[i].HandleType = HandleType;
|
|
*Handle = LongToHandle(i);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
ASSERT (FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FreeIoHandle(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN HANDLE Handle)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees an input or output handle from the process's
|
|
handle table.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
Handle - Handle to free.
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
The console lock must be held when calling this routine. The handle
|
|
is freed from the per-process handle table. Holding the console
|
|
lock serializes both threads within the calling process and any other
|
|
process that shares the console.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PHANDLE_DATA HandleData;
|
|
|
|
Status = DereferenceIoHandleNoCheck(ProcessData,
|
|
Handle,
|
|
&HandleData
|
|
);
|
|
ASSERT (NT_SUCCESS(Status));
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (HandleData->HandleType & CONSOLE_INPUT_HANDLE) {
|
|
FreeInputHandle(HandleData);
|
|
}
|
|
HandleData->HandleType = CONSOLE_FREE_HANDLE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
DereferenceIoHandleNoCheck(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN HANDLE Handle,
|
|
OUT PHANDLE_DATA *HandleData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies a handle's validity, then returns a pointer to
|
|
the handle data structure.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
Handle - Handle to dereference.
|
|
|
|
HandleData - On return, pointer to handle data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
if (((ULONG_PTR)Handle >= ProcessData->HandleTableSize) ||
|
|
(ProcessData->HandleTablePtr[(ULONG_PTR)Handle].HandleType == CONSOLE_FREE_HANDLE) ) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
*HandleData = &ProcessData->HandleTablePtr[(ULONG_PTR)Handle];
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
DereferenceIoHandle(
|
|
IN PCONSOLE_PER_PROCESS_DATA ProcessData,
|
|
IN HANDLE Handle,
|
|
IN ULONG HandleType,
|
|
IN ACCESS_MASK Access,
|
|
OUT PHANDLE_DATA *HandleData)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies a handle's validity, then returns a pointer to
|
|
the handle data structure.
|
|
|
|
Arguments:
|
|
|
|
ProcessData - Pointer to per process data structure.
|
|
|
|
Handle - Handle to dereference.
|
|
|
|
HandleData - On return, pointer to handle data structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG_PTR Index;
|
|
|
|
if (!CONSOLE_HANDLE(Handle)) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
Index = (ULONG_PTR)HANDLE_TO_INDEX(Handle);
|
|
if ((Index >= ProcessData->HandleTableSize) ||
|
|
(ProcessData->HandleTablePtr[Index].HandleType == CONSOLE_FREE_HANDLE) ||
|
|
!(ProcessData->HandleTablePtr[Index].HandleType & HandleType) ||
|
|
!(ProcessData->HandleTablePtr[Index].Access & Access) ) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
*HandleData = &ProcessData->HandleTablePtr[Index];
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
SrvVerifyConsoleIoHandle(
|
|
IN OUT PCSR_API_MSG m,
|
|
IN OUT PCSR_REPLY_STATUS ReplyStatus)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies that a console io handle is valid.
|
|
|
|
Arguments:
|
|
|
|
ApiMessageData - Points to parameter structure.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONSOLE_VERIFYIOHANDLE_MSG a = (PCONSOLE_VERIFYIOHANDLE_MSG)&m->u.ApiMessageData;
|
|
PCONSOLE_INFORMATION Console;
|
|
NTSTATUS Status;
|
|
PHANDLE_DATA HandleData;
|
|
PCONSOLE_PER_PROCESS_DATA ProcessData;
|
|
|
|
UNREFERENCED_PARAMETER(ReplyStatus);
|
|
|
|
Status = ApiPreamble(a->ConsoleHandle,
|
|
&Console
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
ProcessData = CONSOLE_PERPROCESSDATA();
|
|
Status = DereferenceIoHandleNoCheck(ProcessData,
|
|
HANDLE_TO_INDEX(a->Handle),
|
|
&HandleData
|
|
);
|
|
UnlockConsole(Console);
|
|
}
|
|
a->Valid = (NT_SUCCESS(Status));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ApiPreamble(
|
|
IN HANDLE ConsoleHandle,
|
|
OUT PCONSOLE_INFORMATION *Console)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// If this process doesn't have a console handle, bail immediately.
|
|
//
|
|
|
|
if (ConsoleHandle == NULL || ConsoleHandle != CONSOLE_GETCONSOLEHANDLE()) {
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
#ifdef i386
|
|
//Do not lock the console if we are in the special case:
|
|
//(1). we are in the middle of handshaking with ntvdm doing
|
|
// full-screen to windowed mode transition
|
|
//(2). the calling process is THE ntvdm process(this implies that the
|
|
// the console has vdm registered.
|
|
//(3). the console handle is the same one.
|
|
// if (1), (2) and (3) are true then the console is already locked
|
|
// (locked by the windowproc while processing the WM_FULLSCREEN
|
|
// message)
|
|
|
|
RtlEnterCriticalSection(&ConsoleVDMCriticalSection);
|
|
if (ConsoleVDMOnSwitching != NULL &&
|
|
ConsoleVDMOnSwitching->ConsoleHandle == ConsoleHandle &&
|
|
ConsoleVDMOnSwitching->VDMProcessId == CONSOLE_CLIENTPROCESSID()) {
|
|
RIPMSG1(RIP_WARNING, "ApiPreamble - Thread %lx Entered VDM CritSec", GetCurrentThreadId());
|
|
*Console = ConsoleVDMOnSwitching;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
RtlLeaveCriticalSection(&ConsoleVDMCriticalSection);
|
|
#endif
|
|
|
|
Status = RevalidateConsole(ConsoleHandle,
|
|
Console
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Make sure the console has been initialized and the window is valid
|
|
//
|
|
|
|
if ((*Console)->hWnd == NULL || ((*Console)->Flags & CONSOLE_TERMINATING)) {
|
|
KdPrint(("CONSRV: bogus window for console %lx\n", *Console));
|
|
UnlockConsole(*Console);
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RevalidateConsole(
|
|
IN HANDLE ConsoleHandle,
|
|
OUT PCONSOLE_INFORMATION *Console)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LockConsoleHandleTable();
|
|
Status = DereferenceConsoleHandle(ConsoleHandle,
|
|
Console
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
UnlockConsoleHandleTable();
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// The WaitCount ensures the console won't go away between the time
|
|
// we unlock the console handle table and we lock the console.
|
|
//
|
|
|
|
InterlockedIncrement(&(*Console)->WaitCount);
|
|
UnlockConsoleHandleTable();
|
|
try {
|
|
LockConsole(*Console);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
InterlockedDecrement(&(*Console)->WaitCount);
|
|
return GetExceptionCode();
|
|
}
|
|
InterlockedDecrement(&(*Console)->WaitCount);
|
|
|
|
//
|
|
// If the console was marked for destruction while we were waiting to
|
|
// lock it, try to destroy it and return.
|
|
//
|
|
|
|
if ((*Console)->Flags & CONSOLE_IN_DESTRUCTION) {
|
|
DestroyConsole(*Console);
|
|
*Console = NULL;
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
//
|
|
// If the console was marked for termination while we were waiting to
|
|
// lock it, bail out.
|
|
//
|
|
|
|
if ((*Console)->Flags & CONSOLE_TERMINATING) {
|
|
UnlockConsole(*Console);
|
|
*Console = NULL;
|
|
return STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
BOOLEAN
|
|
UnProtectHandle(
|
|
HANDLE hObject)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
|
|
|
|
Status = NtQueryObject(hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof(HandleInfo),
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
HandleInfo.ProtectFromClose = FALSE;
|
|
Status = NtSetInformationObject(hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof(HandleInfo)
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#endif // DBG
|