/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    process.c

Abstract:

    This module contains the worker routines called to create and
    maintain the application process structure for the Client-Server
    Runtime Subsystem to the Session Manager SubSystem.

Author:

    Steve Wood (stevewo) 10-Oct-1990

Revision History:

--*/


#include "csrsrv.h"

// ProcessSequenceCount will never be a value less than FIRST_SEQUENCE_COUNT
// currently GDI needs 0 - 4 to be reserved.

ULONG ProcessSequenceCount = FIRST_SEQUENCE_COUNT;


#define THREAD_HASH_SIZE 256
#define THREAD_ID_TO_HASH(id)   ((ULONG)(id)&(THREAD_HASH_SIZE-1))
LIST_ENTRY CsrThreadHashTable[THREAD_HASH_SIZE];

LIST_ENTRY CsrZombieThreadList;


SECURITY_QUALITY_OF_SERVICE CsrSecurityQos = {
    sizeof(SECURITY_QUALITY_OF_SERVICE), SecurityImpersonation,
    SECURITY_DYNAMIC_TRACKING, FALSE
};

PCSR_PROCESS
FindProcessForShutdown(
    PLUID CallerLuid
    );

ULONG CsrpNormalSeperation = 2;

VOID
CsrpSetToNormalPriority(
    VOID
    )
{

    KPRIORITY SetBasePriority;

    SetBasePriority = FOREGROUND_BASE_PRIORITY + 4;
    NtSetInformationProcess(
        NtCurrentProcess(),
        ProcessBasePriority,
        (PVOID) &SetBasePriority,
        sizeof(SetBasePriority)
        );
}

VOID
CsrpSetToShutdownPriority(
    VOID
    )
{

    KPRIORITY SetBasePriority;

    SetBasePriority = FOREGROUND_BASE_PRIORITY + 6;
    NtSetInformationProcess(
        NtCurrentProcess(),
        ProcessBasePriority,
        (PVOID) &SetBasePriority,
        sizeof(SetBasePriority)
        );
}

VOID
CsrpComputePriority(
    IN ULONG PriorityClass,
    OUT KPRIORITY *ForegroundPriority,
    OUT KPRIORITY *BackgroundPriority
    )
{
    if ( PriorityClass & CSR_NORMAL_PRIORITY_CLASS ) {
        *ForegroundPriority = 9;
        *BackgroundPriority = 7;
        }
    else if ( PriorityClass & CSR_IDLE_PRIORITY_CLASS ) {
        *ForegroundPriority = 4;
        *BackgroundPriority = 4;
        }
    else if ( PriorityClass & CSR_HIGH_PRIORITY_CLASS ) {
        *ForegroundPriority = 13;
        *BackgroundPriority = 13;
        }
    else if ( PriorityClass & CSR_REALTIME_PRIORITY_CLASS ) {
        *ForegroundPriority = 24;
        *BackgroundPriority = 24;
        }
    else {
        *ForegroundPriority = 9;
        *BackgroundPriority = 7;
        }
}

ULONG
CsrComputePriorityClass(
    IN PCSR_PROCESS Process
    )
{
    ULONG ReturnValue;

    if ( Process->ForegroundPriority == 9 && Process->BackgroundPriority == 7 ) {
        ReturnValue = CSR_NORMAL_PRIORITY_CLASS;
        }
    else if ( Process->ForegroundPriority == 4 && Process->BackgroundPriority == 4 ) {
        ReturnValue = CSR_IDLE_PRIORITY_CLASS;
        }
    else if ( Process->ForegroundPriority == 13 && Process->BackgroundPriority == 13 ) {
        ReturnValue = CSR_HIGH_PRIORITY_CLASS;
        }
    else if ( Process->ForegroundPriority == 24 && Process->BackgroundPriority == 24 ) {
        ReturnValue = CSR_REALTIME_PRIORITY_CLASS;
        }
    else {
        ReturnValue = 0;
        }
    return ReturnValue;
}
VOID
CsrSetForegroundPriority(
    IN PCSR_PROCESS Process
    )
{
    KPRIORITY SetBasePriority;

    //
    // priority seperation is set 0, 1, 2 as the process
    //

    if ( (ULONG)Process <= 2 ) {
        switch ((ULONG)Process) {
            case 0:
                CsrpNormalSeperation = 0;
                break;
            case 1:
                CsrpNormalSeperation = 1;
                break;
            case 2:
                CsrpNormalSeperation = 2;
                break;
            }
        return;
        }

    //
    // If we are in the normal class, then forground is background + seperation
    //

    if ( Process->BackgroundPriority == 7 ) {
        SetBasePriority = Process->BackgroundPriority + CsrpNormalSeperation;
        }
    else {
        SetBasePriority = Process->ForegroundPriority;
        }
    SetBasePriority |= 0x80000000;
    NtSetInformationProcess(
            Process->ProcessHandle,
            ProcessBasePriority,
            (PVOID)&SetBasePriority,
            sizeof(SetBasePriority)
            );
}

VOID
CsrSetBackgroundPriority(
    IN PCSR_PROCESS Process
    )
{
    KPRIORITY SetBasePriority;

    SetBasePriority = Process->BackgroundPriority;

    NtSetInformationProcess(
            Process->ProcessHandle,
            ProcessBasePriority,
            (PVOID)&SetBasePriority,
            sizeof(SetBasePriority)
            );
}


NTSTATUS
CsrInitializeProcessStructure( VOID )
{
    NTSTATUS Status;
    ULONG i;

    Status = RtlInitializeCriticalSection( &CsrProcessStructureLock );
    ASSERT( NT_SUCCESS( Status ) );

    CsrRootProcess = NULL;
    CsrRootProcess = CsrAllocateProcess();
    ASSERT( CsrRootProcess != NULL );
    InitializeListHead( &CsrRootProcess->ListLink );
    CsrRootProcess->ProcessHandle = (HANDLE)0xFFFFFFFF;
    CsrRootProcess->ClientId = NtCurrentTeb()->ClientId;
    for ( i=0; i<THREAD_HASH_SIZE; i++ ) {
        InitializeListHead(&CsrThreadHashTable[i]);
        }
    InitializeListHead(&CsrZombieThreadList);
    Status = RtlInitializeCriticalSection( &CsrWaitListsLock );
    ASSERT( NT_SUCCESS( Status ) );

    return( Status );
}


PCSR_PROCESS
CsrAllocateProcess( VOID )
{
    PCSR_PROCESS Process;
    ULONG ProcessSize;

    //
    // Allocate an Windows Process Object.  At the end of the process
    // structure is an array of pointers to each server DLL's per process
    // data.  The per process data is contained in the memory after the
    // array.
    //

    ProcessSize = QUAD_ALIGN(sizeof( CSR_PROCESS ) +
            (CSR_MAX_SERVER_DLL * sizeof(PVOID))) + CsrTotalPerProcessDataLength;
    Process = (PCSR_PROCESS)RtlAllocateHeap( CsrHeap, 0,
                                             ProcessSize
                                           );
    ASSERT( Process != NULL );
    if (Process == NULL) {
        return( NULL );
        }

    //
    // Initialize the fields of the process object
    //

    RtlZeroMemory( Process, ProcessSize);

    //
    // grab the ProcessSequenceNumber and increment it, making sure that it
    // is never less than FIRST_SEQUENCE_COUNT.
    //

    Process->SequenceNumber = ProcessSequenceCount++;

    if (ProcessSequenceCount < FIRST_SEQUENCE_COUNT)
        ProcessSequenceCount = FIRST_SEQUENCE_COUNT;

    Process->ForegroundPriority = (KPRIORITY)FOREGROUND_BASE_PRIORITY;
    Process->BackgroundPriority = (KPRIORITY)NORMAL_BASE_PRIORITY;

    CsrLockedReferenceProcess(Process);

    InitializeListHead( &Process->ThreadList );
    return( Process );
}


VOID
CsrDeallocateProcess(
    IN PCSR_PROCESS Process
    )
{
    RtlFreeHeap( CsrHeap, 0, Process );
}

VOID
CsrInsertProcess(
    IN PCSR_PROCESS ParentProcess,
    IN PCSR_PROCESS CallingProcess,
    IN PCSR_PROCESS Process
    )
// NOTE: the process structure lock must be held when calling this routine
{
    PCSR_SERVER_DLL LoadedServerDll;
    ULONG i;

    Process->Parent = ParentProcess;
    InsertTailList( &CsrRootProcess->ListLink, &Process->ListLink );

    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        LoadedServerDll = CsrLoadedServerDll[ i ];
        if (LoadedServerDll && LoadedServerDll->AddProcessRoutine) {
            (*LoadedServerDll->AddProcessRoutine)( CallingProcess, Process );
            }
        }
}


VOID
CsrRemoveProcess(
    IN PCSR_PROCESS Process
    )
// NOTE: the process structure lock must be held when calling this routine
{
    PCSR_SERVER_DLL LoadedServerDll;
    ULONG i;

    RemoveEntryList( &Process->ListLink );
    ReleaseProcessStructureLock();

    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        LoadedServerDll = CsrLoadedServerDll[ i ];
        if (LoadedServerDll && LoadedServerDll->DisconnectRoutine) {
            (LoadedServerDll->DisconnectRoutine)( Process );
            }
        }

}

NTSTATUS
CsrSetProcessContext(
    IN PCSR_PROCESS Process,
    IN PCSR_THREAD Thread,
    IN BOOLEAN StartedBySm
    )
{
#if 0
    Process->InitialPebCsrData.Length = sizeof( Process->InitialPebCsrData );
    Process->InitialPebCsrData.StartedBySm = StartedBySm;
#endif

    return( STATUS_SUCCESS );
}

NTSTATUS
CsrCreateProcess(
    IN HANDLE ProcessHandle,
    IN HANDLE ThreadHandle,
    IN PCLIENT_ID ClientId,
    IN PCSR_NT_SESSION Session,
    IN ULONG DebugFlags,
    IN PCLIENT_ID DebugUserInterface OPTIONAL
    )
{
    PCSR_PROCESS Process;
    PCSR_THREAD Thread;
    NTSTATUS Status;
    ULONG i;
    PVOID ProcessDataPtr;
    CLIENT_ID CallingClientId;
    PCSR_THREAD CallingThread;
    PCSR_PROCESS CallingProcess;
    KERNEL_USER_TIMES TimeInfo;

    CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();

    //
    // remember the client id of the calling process.
    //

    CallingClientId = CallingThread->ClientId;

    AcquireProcessStructureLock();

    //
    // look for calling thread.
    //

    CallingThread = CsrLocateThreadByClientId( &CallingProcess,
                                               &CallingClientId
                                             );
    if (CallingThread == NULL) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
    }

    Process = CsrAllocateProcess();
    if (Process == NULL) {
        Status = STATUS_NO_MEMORY;
        ReleaseProcessStructureLock();
        return( Status );
        }

    //
    // copy per-process data from parent to child
    //

    CallingProcess = (CSR_SERVER_QUERYCLIENTTHREAD())->Process;
    ProcessDataPtr = (PVOID)QUAD_ALIGN(&Process->ServerDllPerProcessData[CSR_MAX_SERVER_DLL]);
    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        if (CsrLoadedServerDll[i] != NULL && CsrLoadedServerDll[i]->PerProcessDataLength) {
            Process->ServerDllPerProcessData[i] = ProcessDataPtr;
            RtlMoveMemory(ProcessDataPtr,
                          CallingProcess->ServerDllPerProcessData[i],
                          CsrLoadedServerDll[i]->PerProcessDataLength
                         );
            ProcessDataPtr = (PVOID)QUAD_ALIGN((ULONG)ProcessDataPtr + CsrLoadedServerDll[i]->PerProcessDataLength);
        }
        else {
            Process->ServerDllPerProcessData[i] = NULL;
        }
    }

    Status = NtSetInformationProcess(
                ProcessHandle,
                ProcessExceptionPort,
                (PVOID)&CsrApiPort,
                sizeof(HANDLE)
                );
    if ( !NT_SUCCESS(Status) ) {
        CsrDeallocateProcess( Process );
        ReleaseProcessStructureLock();
        return( STATUS_NO_MEMORY );
        }
        ASSERT(NT_SUCCESS(Status));

    CsrpComputePriority(
        DebugFlags,
        &Process->ForegroundPriority,
        &Process->BackgroundPriority
        );

    Status = NtSetInformationProcess(
        ProcessHandle,
        ProcessBasePriority,
        (PVOID)&Process->BackgroundPriority,
        sizeof(KPRIORITY)
        );
    ASSERT(NT_SUCCESS(Status));

    //
    // If we are creating a process group, the group leader has the same
    // process id and sequence number of itself. If the leader dies and
    // his pid is recycled, the sequence number mismatch will prevent it
    // from being viewed as a group leader.
    //

    if ( DebugFlags & CSR_CREATE_PROCESS_GROUP ) {
        Process->ProcessGroupId = (ULONG)ClientId->UniqueProcess;
        Process->ProcessGroupSequence = Process->SequenceNumber;
        }
    else {
        Process->ProcessGroupId = CallingProcess->ProcessGroupId;
        Process->ProcessGroupSequence = CallingProcess->ProcessGroupSequence;
        }

    if ( DebugFlags & CSR_PROCESS_CONSOLEAPP ) {
        Process->Flags |= CSR_PROCESS_CONSOLEAPP;
        }

    DebugFlags &= ~(CSR_PROCESS_CONSOLEAPP | CSR_NORMAL_PRIORITY_CLASS|CSR_IDLE_PRIORITY_CLASS|CSR_HIGH_PRIORITY_CLASS|CSR_REALTIME_PRIORITY_CLASS|CSR_CREATE_PROCESS_GROUP);

    if ( !DebugFlags && CallingProcess->DebugFlags & CSR_DEBUG_PROCESS_TREE ) {
        Process->DebugFlags = CSR_DEBUG_PROCESS_TREE;
        Process->DebugUserInterface = CallingProcess->DebugUserInterface;
        }
    if ( DebugFlags & (CSR_DEBUG_THIS_PROCESS | CSR_DEBUG_PROCESS_TREE) &&
         ARGUMENT_PRESENT(DebugUserInterface) ) {
        Process->DebugFlags = DebugFlags;
        Process->DebugUserInterface = *DebugUserInterface;
        }


    if ( Process->DebugFlags ) {

        //
        // Process is being debugged, so set up debug port
        //

        Status = NtSetInformationProcess(
                    ProcessHandle,
                    ProcessDebugPort,
                    (PVOID)&CsrApiPort,
                    sizeof(HANDLE)
                    );
        ASSERT(NT_SUCCESS(Status));
        if ( !NT_SUCCESS(Status) ) {
            CsrDeallocateProcess( Process );
            ReleaseProcessStructureLock();
            return( STATUS_NO_MEMORY );
            }
        }
    //
    // capture the thread's createtime so that we can use
    // this as a sequence number
    //

    Status = NtQueryInformationThread(
                ThreadHandle,
                ThreadTimes,
                (PVOID)&TimeInfo,
                sizeof(TimeInfo),
                NULL
                );
    if ( !NT_SUCCESS(Status) ) {
        CsrDeallocateProcess( Process );
        ReleaseProcessStructureLock();
        return( Status );
        }

    Thread = CsrAllocateThread( Process );
    if (Thread == NULL) {
        CsrDeallocateProcess( Process );
        ReleaseProcessStructureLock();
        return( STATUS_NO_MEMORY );
	}

    CsrInitializeThreadData(Thread, CSR_SERVER_QUERYCLIENTTHREAD());

    Thread->CreateTime = TimeInfo.CreateTime;

    Thread->ClientId = *ClientId;
    Thread->ThreadHandle = ThreadHandle;

ProtectHandle(ThreadHandle);

    Thread->Flags = 0;
    CsrInsertThread( Process, Thread );

    CsrReferenceNtSession(Session);
    Process->NtSession = Session;

    Process->ClientId = *ClientId;
    Process->ProcessHandle = ProcessHandle;

    Process->ShutdownLevel = 0x00000280;

    CsrInsertProcess( NULL, (CSR_SERVER_QUERYCLIENTTHREAD())->Process, Process );
    ReleaseProcessStructureLock();
    return STATUS_SUCCESS;
}


NTSTATUS
CsrDestroyProcess(
    IN PCLIENT_ID ClientId,
    IN NTSTATUS ExitStatus
    )
{
    PLIST_ENTRY ListHead, ListNext;
    PCSR_THREAD DyingThread;
    PCSR_PROCESS DyingProcess;

    CLIENT_ID DyingClientId;

    DyingClientId = *ClientId;

    AcquireProcessStructureLock();


    DyingThread = CsrLocateThreadByClientId( &DyingProcess,
                                             &DyingClientId
                                           );
    if (DyingThread == NULL) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
    }

    //
    // prevent multiple destroys from causing problems. Scottlu and Markl
    // beleive all known race conditions are now fixed. This is simply a
    // precaution since we know that if this happens we process reference
    // count underflow
    //

    if ( DyingProcess->Flags & CSR_PROCESS_DESTROYED ) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
    }

    DyingProcess->Flags |= CSR_PROCESS_DESTROYED;

    ListHead = &DyingProcess->ThreadList;
    ListNext = ListHead->Flink;
    while (ListNext != ListHead) {
        DyingThread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
        ListNext = ListNext->Flink;
        if ( DyingThread->Flags & CSR_THREAD_DESTROYED ) {
            continue;
            }
        else {
            DyingThread->Flags |= CSR_THREAD_DESTROYED;
            }
        AcquireWaitListsLock();
        if (DyingThread->WaitBlock != NULL) {
            CsrNotifyWaitBlock(DyingThread->WaitBlock,
                               NULL,
                               NULL,
                               NULL,
                               CSR_PROCESS_TERMINATING,
                               TRUE
                              );
            }
        ReleaseWaitListsLock();
        CsrLockedDereferenceThread(DyingThread);
        }

    ReleaseProcessStructureLock();
    return STATUS_SUCCESS;
}


NTSTATUS
CsrCreateThread(
    IN PCSR_PROCESS Process,
    IN HANDLE ThreadHandle,
    IN PCLIENT_ID ClientId
    )
{
    PCSR_THREAD Thread;
    CLIENT_ID CallingClientId;
    PCSR_THREAD CallingThread;
    PCSR_PROCESS CallingProcess;
    KERNEL_USER_TIMES TimeInfo;
    NTSTATUS Status;

    CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();

    //
    // remember the client id of the calling process.
    //

    CallingClientId = CallingThread->ClientId;

    AcquireProcessStructureLock();

    //
    // look for calling thread.
    //

    CallingThread = CsrLocateThreadByClientId( &CallingProcess,
                                               &CallingClientId
                                             );
    if (CallingThread == NULL) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
    }

    Status = NtQueryInformationThread(
                ThreadHandle,
                ThreadTimes,
                (PVOID)&TimeInfo,
                sizeof(TimeInfo),
                NULL
                );
    if ( !NT_SUCCESS(Status) ) {
        ReleaseProcessStructureLock();
        return( Status );
        }

    Thread = CsrAllocateThread( Process );
    if (Thread == NULL) {
        ReleaseProcessStructureLock();
        return( STATUS_NO_MEMORY );
	}

    CsrInitializeThreadData(Thread, CallingThread);

    Thread->CreateTime = TimeInfo.CreateTime;

    Thread->ClientId = *ClientId;
    Thread->ThreadHandle = ThreadHandle;

ProtectHandle(ThreadHandle);

    Thread->Flags = 0;
    CsrInsertThread( Process, Thread );
    ReleaseProcessStructureLock();
    return STATUS_SUCCESS;
}

NTSTATUS
CsrCreateRemoteThread(
    IN HANDLE ThreadHandle,
    IN PCLIENT_ID ClientId
    )
{
    PCSR_THREAD Thread;
    PCSR_PROCESS Process;
    NTSTATUS Status;
    HANDLE hThread;
    KERNEL_USER_TIMES TimeInfo;

    Status = NtQueryInformationThread(
                ThreadHandle,
                ThreadTimes,
                (PVOID)&TimeInfo,
                sizeof(TimeInfo),
                NULL
                );
    if ( !NT_SUCCESS(Status) ) {
        return( Status );
        }

    Status = CsrLockProcessByClientId( ClientId->UniqueProcess,
                                       &Process
                                     );
    if (!NT_SUCCESS( Status )) {
        return( Status );
        }

    Thread = CsrAllocateThread( Process );
    if (Thread == NULL) {
        CsrUnlockProcess( Process );
        return( STATUS_NO_MEMORY );
	}
    Status = NtDuplicateObject(
                NtCurrentProcess(),
                ThreadHandle,
                NtCurrentProcess(),
                &hThread,
                0L,
                0L,
                DUPLICATE_SAME_ACCESS
                );
    if (!NT_SUCCESS(Status)) {
        hThread = ThreadHandle;
    }

    CsrInitializeThreadData(Thread, NULL);

    Thread->CreateTime = TimeInfo.CreateTime;

    Thread->ClientId = *ClientId;
    Thread->ThreadHandle = hThread;

ProtectHandle(hThread);

    Thread->Flags = 0;
    CsrInsertThread( Process, Thread );
    CsrUnlockProcess( Process );
    return STATUS_SUCCESS;
}


NTSTATUS
CsrDestroyThread(
    IN PCLIENT_ID ClientId
    )
{
    CLIENT_ID DyingClientId;
    PCSR_THREAD DyingThread;
    PCSR_PROCESS DyingProcess;

    DyingClientId = *ClientId;

    AcquireProcessStructureLock();

    DyingThread = CsrLocateThreadByClientId( &DyingProcess,
                                             &DyingClientId
                                           );
    if (DyingThread == NULL) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
    }

    if ( DyingThread->Flags & CSR_THREAD_DESTROYED ) {
        ReleaseProcessStructureLock();
        return STATUS_THREAD_IS_TERMINATING;
        }
    else {
        DyingThread->Flags |= CSR_THREAD_DESTROYED;
        }

    AcquireWaitListsLock();
    if (DyingThread->WaitBlock != NULL) {
        CsrNotifyWaitBlock(DyingThread->WaitBlock,
                           NULL,
                           NULL,
                           NULL,
                           CSR_PROCESS_TERMINATING,
                           TRUE
                          );
        }
    ReleaseWaitListsLock();
    CsrLockedDereferenceThread(DyingThread);

    ReleaseProcessStructureLock();
    return STATUS_SUCCESS;
}


PCSR_THREAD
CsrAllocateThread(
    IN PCSR_PROCESS Process
    )
{
    PCSR_THREAD Thread;
    ULONG ThreadSize;

    //
    // Allocate an Windows Thread Object.  At the end of the thread
    // structure is an array of pointers to each server DLL's per thread
    // data.  The per thread data is contained in the memory after the
    // array.
    //
    //

    ThreadSize = QUAD_ALIGN(sizeof( CSR_THREAD ) +
            (CSR_MAX_SERVER_DLL * sizeof(PVOID))) + CsrTotalPerThreadDataLength;
    Thread = (PCSR_THREAD)RtlAllocateHeap( CsrHeap, 0,
                                           ThreadSize
                                         );
    if (Thread == NULL) {
        return( NULL );
        }

    //
    // Initialize the fields of the thread object
    //

    RtlZeroMemory( Thread, ThreadSize );

    CsrLockedReferenceThread(Thread);
    CsrLockedReferenceProcess(Process);
    Thread->Process = Process;

    return( Thread );
}


VOID
CsrDeallocateThread(
    IN PCSR_THREAD Thread
    )
{
    ASSERT (Thread->WaitBlock == NULL);
    RtlFreeHeap( CsrHeap, 0, Thread );
}


VOID
CsrInitializeThreadData(
    IN PCSR_THREAD Thread,
    IN PCSR_THREAD CallingThread
    )
{
    PVOID ThreadDataPtr;
    ULONG i;

    //
    // if there is a parent, copy per-thread data from parent to child
    //

    ThreadDataPtr = (PVOID)QUAD_ALIGN(&Thread->ServerDllPerThreadData[CSR_MAX_SERVER_DLL]);
    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        if (CsrLoadedServerDll[i] != NULL && CsrLoadedServerDll[i]->PerThreadDataLength) {
            Thread->ServerDllPerThreadData[i] = ThreadDataPtr;
            if (CallingThread) {
                RtlMoveMemory(ThreadDataPtr,
                            CallingThread->ServerDllPerThreadData[i],
                            CsrLoadedServerDll[i]->PerThreadDataLength
                            );
                }
            ThreadDataPtr = (PVOID)QUAD_ALIGN((ULONG)ThreadDataPtr + CsrLoadedServerDll[i]->PerThreadDataLength);
            }
        else {
            Thread->ServerDllPerThreadData[i] = NULL;
            }
        }
}

VOID
CsrInsertThread(
    IN PCSR_PROCESS Process,
    IN PCSR_THREAD Thread
    )

// NOTE: the process structure lock must be held exclusively while calling this routine

{
    PCSR_SERVER_DLL LoadedServerDll;
    ULONG i;

    InsertTailList( &Process->ThreadList, &Thread->Link );
    Process->ThreadCount++;
    i = THREAD_ID_TO_HASH(Thread->ClientId.UniqueThread);
    InsertHeadList( &CsrThreadHashTable[i], &Thread->HashLinks);
    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        LoadedServerDll = CsrLoadedServerDll[ i ];
        if (LoadedServerDll && LoadedServerDll->AddThreadRoutine) {
            (*LoadedServerDll->AddThreadRoutine)( Thread );
            }
        }
}

VOID
CsrRemoveThread(
    IN PCSR_THREAD Thread
    )


{
    PCSR_SERVER_DLL LoadedServerDll;
    ULONG i;

    RemoveEntryList( &Thread->Link );
    Thread->Process->ThreadCount--;
    if (Thread->HashLinks.Flink)
        RemoveEntryList( &Thread->HashLinks );

    //
    // if this is the last thread, then make sure we undo the reference
    // that this thread had on the process.
    //

    if ( Thread->Process->ThreadCount == 0 ) {
        if ( !(Thread->Process->Flags & CSR_PROCESS_LASTTHREADOK) ) {
            Thread->Process->Flags |= CSR_PROCESS_LASTTHREADOK;
            CsrLockedDereferenceProcess(Thread->Process);
            }
        }
    ReleaseProcessStructureLock();

    //
    // Set the termination thread *before* calling the delete thread routines
    // in each server .dll. Need to set this ahead of time because USER looks
    // at this flag from the context of a thread that gets woken up by this
    // thread while calling DeleteThreadRoutine.
    //

    Thread->Flags |= CSR_THREAD_TERMINATING;

    for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
        LoadedServerDll = CsrLoadedServerDll[ i ];
        if (LoadedServerDll && LoadedServerDll->DeleteThreadRoutine) {

            (*LoadedServerDll->DeleteThreadRoutine)( Thread );
            }
        }

    if (Thread->ThreadConnected) {

        //
        // Set the low event pair to wake up the gui thread if it is asleep
        // on this. Then wait on it's thread handle for it to go away. Must
        // do this before continuing cleanup so that we synchronize with the
        // gui threads cleaning up.
        //

        Thread->ShutDownStatus = NtSetLowEventPair( Thread->ServerEventPairHandle );

        if ( !NT_SUCCESS(Thread->ShutDownStatus) ) {
#if DBG
            DbgPrint("CSRSS: Shutdown Failed %lx Thread %lx\n",Thread->ShutDownStatus,Thread);
            DbgBreakPoint();
#endif
            NtTerminateProcess(NtCurrentProcess(),Thread->ShutDownStatus);
            }
        }
}


NTSTATUS
CsrLockProcessByClientId(
    IN HANDLE UniqueProcessId,
    OUT PCSR_PROCESS *Process
    )
{
    NTSTATUS Status;
    PLIST_ENTRY ListHead, ListNext;
    PCSR_PROCESS ProcessPtr;


    AcquireProcessStructureLock();

    if (ARGUMENT_PRESENT(Process)) {
        *Process = NULL;
    }

    Status = STATUS_UNSUCCESSFUL;
    ListHead = &CsrRootProcess->ListLink;
    ListNext = ListHead;
    do  {
        ProcessPtr = CONTAINING_RECORD( ListNext, CSR_PROCESS, ListLink );
        if (ProcessPtr->ClientId.UniqueProcess == UniqueProcessId) {
            Status = STATUS_SUCCESS;
            break;
            }
        ListNext = ListNext->Flink;
        } while (ListNext != ListHead);

    if (NT_SUCCESS( Status )) {
        CsrLockedReferenceProcess(ProcessPtr);
        *Process = ProcessPtr;
        }
    else {
        ReleaseProcessStructureLock();
        }

    return( Status );
}

NTSTATUS
CsrUnlockProcess(
    IN PCSR_PROCESS Process
    )
{
    CsrLockedDereferenceProcess( Process );
    ReleaseProcessStructureLock();
    return( STATUS_SUCCESS );
}

PCSR_THREAD
CsrLocateThreadByClientId(
    OUT PCSR_PROCESS *Process OPTIONAL,
    IN PCLIENT_ID ClientId
    )

// NOTE: process structure lock must be held while calling this routine

{
    ULONG Index;
    PLIST_ENTRY ListHead, ListNext;
    PCSR_THREAD Thread;

    Index = THREAD_ID_TO_HASH(ClientId->UniqueThread);

    if (ARGUMENT_PRESENT(Process)) {
        *Process = NULL;
    }
    ListHead = &CsrThreadHashTable[Index];
    ListNext = ListHead->Flink;
    while (ListNext != ListHead) {
        Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, HashLinks );
        if ( Thread->ClientId.UniqueThread == ClientId->UniqueThread &&
             Thread->ClientId.UniqueProcess == ClientId->UniqueProcess ) {
            if (ARGUMENT_PRESENT(Process)) {
                *Process = Thread->Process;
                }
            return Thread;
            }
        ListNext = ListNext->Flink;
        }
    return NULL;
}

PCSR_THREAD
CsrLocateThreadInProcess(
    IN PCSR_PROCESS Process OPTIONAL,
    IN PCLIENT_ID ClientId
    )

// NOTE: process structure lock must be held while calling this routine

{
    PLIST_ENTRY ListHead, ListNext;
    PCSR_THREAD Thread;

    if (Process == NULL)
        Process = CsrRootProcess;

    ListHead = &Process->ThreadList;
    ListNext = ListHead->Flink;
    while (ListNext != ListHead) {
        Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
        if (Thread->ClientId.UniqueThread == ClientId->UniqueThread) {
            return( Thread );
            }

        ListNext = ListNext->Flink;
        }

    return( NULL );
}

BOOLEAN
CsrImpersonateClient(
    IN PCSR_THREAD Thread
    )
{
    NTSTATUS Status;
    PCSR_THREAD CallingThread;

    CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();

    if (Thread == NULL) {
        Thread = CallingThread;
        }

    if (Thread == NULL) {
        return FALSE;
        }

    if (!NT_SUCCESS(Status = NtImpersonateThread(NtCurrentThread(),
            Thread->ThreadHandle, &CsrSecurityQos))) {
        IF_DEBUG {
            DbgPrint( "CSRSS: Can't impersonate client thread - Status = %lx\n",
                      Status
                    );
            if (Status != STATUS_BAD_IMPERSONATION_LEVEL)
                DbgBreakPoint();
            }
        CsrSetLastQlpcError(RtlNtStatusToDosError(Status));
        return FALSE;
        }

    //
    // Keep track of recursion by printer drivers
    //

    if (CallingThread != NULL)
        ++CallingThread->ImpersonateCount;

    return TRUE;
}

BOOLEAN
CsrRevertToSelf( VOID )
{
    HANDLE NewToken;
    NTSTATUS Status;
    PCSR_THREAD CallingThread;

    CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();

    //
    // Keep track of recursion by printer drivers
    //

    if (CallingThread != NULL) {
        if (CallingThread->ImpersonateCount == 0) {
            IF_DEBUG {
                DbgPrint( "CSRSS: CsrRevertToSelf called while not impersonating\n" );
                DbgBreakPoint();
                }
            return FALSE;
            }
        if (--CallingThread->ImpersonateCount > 0)
            return TRUE;
    }

    NewToken = NULL;
    Status = NtSetInformationThread(
                 NtCurrentThread(),
                 ThreadImpersonationToken,
                 (PVOID)&NewToken,
                 (ULONG)sizeof(HANDLE)
                 );
    ASSERT( NT_SUCCESS(Status) );

    return NT_SUCCESS(Status);
}

NTSTATUS
CsrUiLookup(
    IN PCLIENT_ID AppClientId,
    OUT PCLIENT_ID DebugUiClientId
    )
{
    PCSR_THREAD Thread;
    NTSTATUS Status;

    Status = STATUS_UNSUCCESSFUL;
    AcquireProcessStructureLock();
    Thread = CsrLocateThreadByClientId( NULL, AppClientId );
    if ( Thread ) {
        if ( Thread->Process->DebugFlags ) {
            *DebugUiClientId = Thread->Process->DebugUserInterface;
            Status = STATUS_SUCCESS;
            }
        }
#if DEVL
    else {
        extern PCSR_PROCESS CsrDebugProcessPtr;

        if (AppClientId->UniqueProcess ==
                NtCurrentTeb()->ClientId.UniqueProcess &&
                CsrDebugProcessPtr && CsrDebugProcessPtr != (PCSR_PROCESS)-1) {
            *DebugUiClientId = CsrDebugProcessPtr->DebugUserInterface;
            Status = STATUS_SUCCESS;
            }
        }
#endif // DEVL
    ReleaseProcessStructureLock();
    return Status;
}

PVOID
CsrAddStaticServerThread(
    IN HANDLE ThreadHandle,
    IN PCLIENT_ID ClientId,
    IN ULONG Flags
    )

/*++

Routine Description:

    This function must be called by client DLL's whenever they create a
    thread that runs in the context of CSR.  This function is not called
    for server threads that are attached to a client in the "server
    handle" field.  This function replaces the old static thread tables.

Arguments:

    ThreadHandle - Supplies a handle to the thread.

    ClientId - Supplies the address of the thread's client id.

    Flags - Not Used.

Return Value:

    Returns the address of the static server thread created by this
    function.

--*/

{
    PCSR_THREAD Thread;

    ASSERT(CsrRootProcess != NULL);
    AcquireProcessStructureLock();
    Thread = CsrAllocateThread(CsrRootProcess);
    if ( Thread ) {

        CsrInitializeThreadData(Thread, NULL);

        Thread->ThreadHandle = ThreadHandle;

ProtectHandle(ThreadHandle);

        Thread->ServerThreadHandle = ThreadHandle;
        Thread->ClientId = *ClientId;
        Thread->Flags = Flags;
        InsertTailList(&CsrRootProcess->ThreadList, &Thread->Link);
        CsrRootProcess->ThreadCount++;
        }
    ReleaseProcessStructureLock();
    return (PVOID)Thread;
}

NTSTATUS
CsrSrvIdentifyAlertableThread(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus
    )
{
    PCSR_THREAD t;

    t = CSR_SERVER_QUERYCLIENTTHREAD();
    t->Flags |= CSR_ALERTABLE_THREAD;
    return STATUS_SUCCESS;
    m;ReplyStatus;    // get rid of unreferenced parameter warning message
}


NTSTATUS
CsrSrvSetPriorityClass(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus
    )
{
    NTSTATUS Status;
    PCSR_SETPRIORITY_CLASS_MSG a = (PCSR_SETPRIORITY_CLASS_MSG)&m->u.ApiMessageData;
    HANDLE TargetProcess;
    PCSR_THREAD t;
    PROCESS_BASIC_INFORMATION BasicInfo;
    PLIST_ENTRY ListHead, ListNext;
    PCSR_PROCESS ProcessPtr;
    KPRIORITY ForegroundPriority,BackgroundPriority;

    t = CSR_SERVER_QUERYCLIENTTHREAD();

    //
    // Get a handle to the process whose priority we are planning to
    // change.
    //

    Status = NtDuplicateObject(
                t->Process->ProcessHandle,
                a->ProcessHandle,
                NtCurrentProcess(),
                &TargetProcess,
                0,
                0,
                DUPLICATE_SAME_ACCESS
                );
    if ( !NT_SUCCESS(Status) ) {
        m->ReturnValue = Status;
        return Status;
        }

    //
    // Now that we have a handle to the process, get its process
    // id so we can look it up
    //

    Status = NtQueryInformationProcess(
                TargetProcess,
                ProcessBasicInformation,
                (PVOID)&BasicInfo,
                sizeof(BasicInfo),
                NULL
                );
    if ( !NT_SUCCESS(Status) ) {
        NtClose(TargetProcess);
        m->ReturnValue = Status;
        return Status;
        }

    //
    // The process id is now in hand. Now we have to locate the CSR
    // process with this ID. To do this, we need to drop our current
    // locks and scan the process table while holding the process
    // structure lock
    //

    AcquireProcessStructureLock();

    ListHead = &CsrRootProcess->ListLink;
    ListNext = ListHead->Flink;
    Status = STATUS_ACCESS_DENIED;
    while (ListNext != ListHead) {
        ProcessPtr = CONTAINING_RECORD( ListNext, CSR_PROCESS, ListLink );
        if (ProcessPtr->ClientId.UniqueProcess == (HANDLE)BasicInfo.UniqueProcessId) {

            //
            // The process was found. Now set or get its priority
            //
            // I guess we need to know if it is currently in the
            // foreground or backgroud ! For now (until I add a CsrSetFg)
            // I will just assign it to the background
            //

            if ( a->PriorityClass ) {

                //
                // Compute the base priorities for the
                // process and call NT to set its base.
                //

                CsrpComputePriority(
                    a->PriorityClass,
                    &ForegroundPriority,
                    &BackgroundPriority
                    );

                Status = NtSetInformationProcess(
                    TargetProcess,
                    ProcessBasePriority,
                    (PVOID)&BackgroundPriority,
                    sizeof(KPRIORITY)
                    );

                if ( NT_SUCCESS(Status) ) {
                    ProcessPtr->ForegroundPriority = ForegroundPriority;
                    ProcessPtr->BackgroundPriority = BackgroundPriority;
                    }
                }
            else {

                //
                // We are trying to get the processes priority
                //

                a->PriorityClass = CsrComputePriorityClass(ProcessPtr);
                if ( a->PriorityClass ) {
                    Status = STATUS_SUCCESS;
                    }
                }
            break;
            }
        ListNext = ListNext->Flink;
        }

    ReleaseProcessStructureLock();

    NtClose(TargetProcess);

    m->ReturnValue = Status;
    return Status;
}


VOID
CsrReferenceProcess(
    PCSR_PROCESS p
    )
{
    AcquireProcessStructureLock();
    p->ReferenceCount++;

    ReleaseProcessStructureLock();
}

VOID
CsrReferenceThread(
    PCSR_THREAD t
    )
{
    AcquireProcessStructureLock();
    t->ReferenceCount++;
    ReleaseProcessStructureLock();
}

VOID
CsrProcessRefcountZero(
    PCSR_PROCESS p
    )
{
    CsrRemoveProcess(p);
    if (p->NtSession) {
        CsrDereferenceNtSession(p->NtSession,0);
        }

    //
    // process might not have made it through dll init routine.
    //

    if ( p->ClientPort ) {
        NtClose(p->ClientPort);
        }
    NtClose(p->ProcessHandle );
    CsrDeallocateProcess(p);
}

VOID
CsrDereferenceProcess(
    PCSR_PROCESS p
    )
{
    LONG LockCount;
    AcquireProcessStructureLock();

    LockCount = --(p->ReferenceCount);

ASSERT(LockCount >= 0);
    if ( !LockCount ) {
        CsrProcessRefcountZero(p);
        }
    else {
        ReleaseProcessStructureLock();
        }
}

VOID
CsrThreadRefcountZero(
    PCSR_THREAD t
    )
{
    PCSR_PROCESS p;
    NTSTATUS Status;

    p = t->Process;
    CsrRemoveThread(t);

    if (!t->ThreadConnected) {
UnProtectHandle(t->ThreadHandle);
        Status = NtClose(t->ThreadHandle);
        ASSERT(NT_SUCCESS(Status));
        CsrDeallocateThread(t);

        CsrDereferenceProcess(p);
        }
}

VOID
CsrDereferenceThread(
    PCSR_THREAD t
    )
{
    LONG LockCount;
    AcquireProcessStructureLock();

    LockCount = --(t->ReferenceCount);

    ASSERT(LockCount >= 0);
    if ( !LockCount ) {
        CsrThreadRefcountZero(t);
        }
    else {
        ReleaseProcessStructureLock();
        }
}

VOID
CsrLockedReferenceProcess(
    PCSR_PROCESS p
    )
{
    p->ReferenceCount++;

}

VOID
CsrLockedReferenceThread(
    PCSR_THREAD t
    )
{
    t->ReferenceCount++;
}

VOID
CsrLockedDereferenceProcess(
    PCSR_PROCESS p
    )
{
    LONG LockCount;

    LockCount = --(p->ReferenceCount);

    ASSERT(LockCount >= 0);
    if ( !LockCount ) {
        AcquireProcessStructureLock();
        CsrProcessRefcountZero(p);
        }
}

VOID
CsrLockedDereferenceThread(
    PCSR_THREAD t
    )
{
    LONG LockCount;

    LockCount = --(t->ReferenceCount);

    ASSERT(LockCount >= 0);
    if ( !LockCount ) {
        AcquireProcessStructureLock();
        CsrThreadRefcountZero(t);
        }
}

//
// This routine will shutdown processes so either a logoff or a shutdown can
// occur. This simply calls the shutdown process handlers for each .dll until
// one .dll recognizes this process and will shut it down. Only the processes
// with the passed sid are shutdown.
//

NTSTATUS
CsrShutdownProcesses(
    PLUID CallerLuid,
    ULONG Flags
    )
{
    PLIST_ENTRY ListHead, ListNext;
    PCSR_PROCESS Process;
    ULONG i;
    PCSR_SERVER_DLL LoadedServerDll;
    ULONG Command;
    BOOLEAN fFirstPass;
#ifdef DEVL
    BOOLEAN fFindDebugger;
    extern BOOLEAN fWin32ServerDebugger;
    extern CLIENT_ID ClientIdWin32ServerDebugger;

    //
    // Remember if we need to look for a debugger debugging the win32 server.
    // We can't close this down because it'll hang the system.
    //
    fFindDebugger = fWin32ServerDebugger;
#endif // DEVL

    //
    // Question: how do we avoid processes starting when we're in shutdown
    // mode? Can't just set a global because this'll mean no processes can
    // start. Probably need to do it based on the security context of the
    // user shutting down.
    //

    AcquireProcessStructureLock();

    //
    // Mark the root process as system context.
    //

    CsrRootProcess->ShutdownFlags |= SHUTDOWN_SYSTEMCONTEXT;

    //
    // Clear all the bits indicating that shutdown has visited this process.
    //

    ListHead = &CsrRootProcess->ListLink;
    ListNext = ListHead->Flink;
    while (ListNext != ListHead) {
        Process = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
        Process->Flags &= ~CSR_PROCESS_SHUTDOWNSKIP;
        ListNext = ListNext->Flink;
        }
    try {
        CsrpSetToShutdownPriority();
        while (TRUE) {

            //
            // Find the next process to shutdown.
            //

            Process = FindProcessForShutdown(CallerLuid);

            if (Process == NULL) {
                ReleaseProcessStructureLock();
                return STATUS_SUCCESS;
                }

            //
            // If this process is debugging the server, don't shut it down or
            // else the system will hang.
            //
#ifdef DEVL
            if (fFindDebugger) {
                if (Process->ClientId.UniqueProcess ==
                        ClientIdWin32ServerDebugger.UniqueProcess) {
                    Process->Flags |= CSR_PROCESS_SHUTDOWNSKIP;
                    Process->ShutdownFlags |= SHUTDOWN_SYSTEMCONTEXT;
                    fFindDebugger = FALSE;
                    continue;
                    }
                }
#endif // DEVL

            CsrLockedReferenceProcess(Process);

            fFirstPass = TRUE;
TryAgain:
            for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
                LoadedServerDll = CsrLoadedServerDll[ i ];
                if (LoadedServerDll && LoadedServerDll->ShutdownProcessRoutine) {

                    //
                    // Release process structure lock before calling off.
                    // CSR_PROCESS structure is still reference counted.
                    //
                    ReleaseProcessStructureLock();
                    Command = (*LoadedServerDll->ShutdownProcessRoutine)(
                            Process, Flags, fFirstPass);
                    AcquireProcessStructureLock();

                    if (Command == SHUTDOWN_KNOWN_PROCESS) {
                        //
                        // Process structure is unlocked.
                        //
                        break;
                        }
                    if (Command == SHUTDOWN_UNKNOWN_PROCESS) {
                        //
                        // Process structure is locked.
                        //
                        continue;
                        }
                    if (Command == SHUTDOWN_CANCEL) {
#if DBG
                        if (Flags & 4) {
                            DbgPrint("Process %x cancelled forced shutdown (Dll = %d)\n",
                                    Process->ClientId.UniqueProcess, i);
                            DbgBreakPoint();
                        }
#endif
                        //
                        // Unlock process structure.
                        //
                        ReleaseProcessStructureLock();
                        return STATUS_CANCELLED;
                        }
                    }
                }

            //
            // No subsystem has an exact match. Now go through them again and
            // let them know there was no exact match. Some .dll should terminate
            // it for us (most likely, console).
            //

            if (fFirstPass && Command == SHUTDOWN_UNKNOWN_PROCESS) {
                fFirstPass = FALSE;
                goto TryAgain;
                }

            //
            // Dereference this process structure if nothing knows about it
            // we hit the end of our loop.
            //
            if (i == CSR_MAX_SERVER_DLL)
                CsrLockedDereferenceProcess(Process);

            }
        }
    finally {
        CsrpSetToNormalPriority();
        }
}

PCSR_PROCESS
FindProcessForShutdown(
    PLUID CallerLuid
    )
{
    LUID ProcessLuid;
    LUID SystemLuid = SYSTEM_LUID;
    PLIST_ENTRY ListHead, ListNext;
    PCSR_PROCESS Process;
    PCSR_PROCESS ProcessT;
    PCSR_THREAD Thread;
    ULONG dwLevel;
    BOOLEAN fEqual;
    NTSTATUS Status;

    ProcessT = NULL;
    dwLevel = 0;

    ListHead = &CsrRootProcess->ListLink;
    ListNext = ListHead->Flink;
    while (ListNext != ListHead) {
        Process = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
        ListNext = ListNext->Flink;

        //
        // If we've visited this process already, then skip it.
        //

        if (Process->Flags & CSR_PROCESS_SHUTDOWNSKIP)
            continue;

        //
        // See if this process is running under the passed sid. If not, mark
        // it as visited and continue.
        //

        Status = CsrGetProcessLuid(Process->ProcessHandle, &ProcessLuid);
        if (Status == STATUS_ACCESS_DENIED && Process->ThreadCount > 0) {

            //
            // Impersonate one of the threads and try again.
            //
            Thread = CONTAINING_RECORD( Process->ThreadList.Flink,
                    CSR_THREAD, Link );
            if (NT_SUCCESS(CsrImpersonateClient(Thread))) {
                Status = CsrGetProcessLuid(NULL, &ProcessLuid);
                CsrRevertToSelf();
            }
        }
        if (!NT_SUCCESS(Status)) {

            //
            // We don't have access to this process' luid, so skip it
            //

            Process->Flags |= CSR_PROCESS_SHUTDOWNSKIP;
            continue;
            }

        //
        // is it equal to the system context luid? If so, we want to
        // remember this because we don't terminate this process:
        // we only notify them.
        //

        fEqual = RtlLargeIntegerEqualTo(ProcessLuid, SystemLuid);
        if (fEqual) {
            Process->ShutdownFlags |= SHUTDOWN_SYSTEMCONTEXT;
            }

        //
        // See if this process's luid is the same as the luid we're supposed
        // to shut down (CallerSid).
        //

        if (!fEqual) {
            fEqual = RtlLargeIntegerEqualTo(ProcessLuid, *CallerLuid);
            }

        //
        // If not equal to either, mark it as such and return
        //

        if (!fEqual) {
            Process->ShutdownFlags |= SHUTDOWN_OTHERCONTEXT;
            }

        if (Process->ShutdownLevel > dwLevel) {
            dwLevel = Process->ShutdownLevel;
            ProcessT = Process;
            }
        }

    if (ProcessT != NULL) {
        ProcessT->Flags |= CSR_PROCESS_SHUTDOWNSKIP;
        return ProcessT;
        }

    return NULL;
}

NTSTATUS
CsrGetProcessLuid(
    HANDLE ProcessHandle,
    PLUID LuidProcess
    )
{
    HANDLE UserToken = NULL;
    PTOKEN_STATISTICS pStats;
    ULONG BytesRequired;
    NTSTATUS Status, CloseStatus;

    if (ProcessHandle == NULL) {

        //
        // Check for a thread token first
        //

        Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE,
                &UserToken);

        if (!NT_SUCCESS(Status)) {
            if (Status != STATUS_NO_TOKEN)
                return Status;

            //
            // No thread token, go to the process
            //

            ProcessHandle = NtCurrentProcess();
            UserToken = NULL;
            }
        }

    if (UserToken == NULL) {
        Status = NtOpenProcessToken(ProcessHandle, TOKEN_QUERY, &UserToken);
        if (!NT_SUCCESS(Status))
            return Status;
        }

    Status = NtQueryInformationToken(
                 UserToken,                 // Handle
                 TokenStatistics,           // TokenInformationClass
                 NULL,                      // TokenInformation
                 0,                         // TokenInformationLength
                 &BytesRequired             // ReturnLength
                 );

    if (Status != STATUS_BUFFER_TOO_SMALL) {
        NtClose(UserToken);
        return Status;
        }

    //
    // Allocate space for the user info
    //

    pStats = (PTOKEN_STATISTICS)RtlAllocateHeap(CsrHeap, 0, BytesRequired);
    if (pStats == NULL) {
        NtClose(UserToken);
        return Status;
        }

    //
    // Read in the user info
    //

    Status = NtQueryInformationToken(
                 UserToken,             // Handle
                 TokenStatistics,       // TokenInformationClass
                 pStats,                // TokenInformation
                 BytesRequired,         // TokenInformationLength
                 &BytesRequired         // ReturnLength
                 );

    //
    // We're finished with the token handle
    //

    CloseStatus = NtClose(UserToken);
    ASSERT(NT_SUCCESS(CloseStatus));

    //
    // Return the authentication LUID
    //

    *LuidProcess = pStats->AuthenticationId;

    RtlFreeHeap(CsrHeap, 0, pStats);
    return Status;
}

VOID
CsrSetCallingSpooler(
    BOOLEAN fSet)
{

    //
    // Obsolete function that may be called by third part drivers.
    //
    return;
    
    fSet;
}

#if CSRSS_PROTECT_HANDLES
BOOLEAN
ProtectHandle(
    HANDLE hObject
    )
{
    NTSTATUS Status;
    OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;

    Status = NtQueryObject( hObject,
                            ObjectHandleFlagInformation,
                            &HandleInfo,
                            sizeof( HandleInfo ),
                            NULL
                          );
    if (NT_SUCCESS( Status )) {
        HandleInfo.ProtectFromClose = TRUE;

        Status = NtSetInformationObject( hObject,
                                         ObjectHandleFlagInformation,
                                         &HandleInfo,
                                         sizeof( HandleInfo )
                                       );
        if (NT_SUCCESS( Status )) {
            return TRUE;
            }
        }

    return FALSE;
}


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
