/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    psquery.c

Abstract:

    This module implements the set and query functions for
    process and thread objects.

Author:

    Mark Lucovsky (markl) 17-Aug-1989


Revision History:

--*/

#include "psp.h"
#include "winerror.h"

#if defined(_WIN64)
#include <wow64t.h>
#endif

//
// Process Pooled Quota Usage and Limits
//  NtQueryInformationProcess using ProcessPooledUsageAndLimits
//

//
// this is the csrss process !
//
extern PEPROCESS ExpDefaultErrorPortProcess;
BOOLEAN PsWatchEnabled = FALSE;



#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
const KPRIORITY PspPriorityTable[PROCESS_PRIORITY_CLASS_ABOVE_NORMAL+1] = {8,4,8,13,24,6,10};


NTSTATUS
PsConvertToGuiThread(
    VOID
    );

NTSTATUS
PspQueryWorkingSetWatch(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    );

NTSTATUS
PspQueryQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    );

NTSTATUS
PspQueryPooledQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    );

NTSTATUS
PspSetQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    IN PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    IN KPROCESSOR_MODE PreviousMode
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PsEstablishWin32Callouts)
#pragma alloc_text(PAGE, PsConvertToGuiThread)
#pragma alloc_text(PAGE, NtQueryInformationProcess)
#pragma alloc_text(PAGE, NtSetInformationProcess)
#pragma alloc_text(PAGE, NtQueryPortInformationProcess)
#pragma alloc_text(PAGE, NtQueryInformationThread)
#pragma alloc_text(PAGE, NtSetInformationThread)
#pragma alloc_text(PAGE, PsSetProcessPriorityByClass)
#pragma alloc_text(PAGE, PspSetPrimaryToken)
#pragma alloc_text(PAGE, PspSetQuotaLimits)
#pragma alloc_text(PAGE, PspQueryQuotaLimits)
#pragma alloc_text(PAGE, PspQueryPooledQuotaLimits)
#pragma alloc_text(PAGE, NtGetCurrentProcessorNumber)
#pragma alloc_text(PAGELK, PspQueryWorkingSetWatch)
#endif

NTSTATUS
PspQueryWorkingSetWatch(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    )
{
    PPAGEFAULT_HISTORY WorkingSetCatcher;
    ULONG SpaceNeeded;
    PEPROCESS Process;
    KIRQL OldIrql;
    NTSTATUS st;

    UNREFERENCED_PARAMETER (ProcessInformationClass);

    st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_QUERY_INFORMATION,
                                    PsProcessType,
                                    PreviousMode,
                                    (PVOID *)&Process,
                                    NULL);

    if (!NT_SUCCESS (st)) {
        return st;
    }

    WorkingSetCatcher = Process->WorkingSetWatch;
    if (WorkingSetCatcher == NULL) {
        ObDereferenceObject (Process);
        return STATUS_UNSUCCESSFUL;
    }

    MmLockPagableSectionByHandle (ExPageLockHandle);
    ExAcquireSpinLock (&WorkingSetCatcher->SpinLock,&OldIrql);

    if (WorkingSetCatcher->CurrentIndex) {

        //
        // Null Terminate the first empty entry in the buffer
        //

        WorkingSetCatcher->WatchInfo[WorkingSetCatcher->CurrentIndex].FaultingPc = NULL;

        //Store a special Va value if the buffer was full and
        //page faults could have been lost

        if (WorkingSetCatcher->CurrentIndex != WorkingSetCatcher->MaxIndex) {
            WorkingSetCatcher->WatchInfo[WorkingSetCatcher->CurrentIndex].FaultingVa = NULL;
        } else {
            WorkingSetCatcher->WatchInfo[WorkingSetCatcher->CurrentIndex].FaultingVa = (PVOID) 1;
        }

        SpaceNeeded = (WorkingSetCatcher->CurrentIndex+1) * sizeof(PROCESS_WS_WATCH_INFORMATION);
    } else {
        ExReleaseSpinLock (&WorkingSetCatcher->SpinLock, OldIrql);
        MmUnlockPagableImageSection (ExPageLockHandle);
        ObDereferenceObject (Process);
        return STATUS_NO_MORE_ENTRIES;
    }

    if (ProcessInformationLength < SpaceNeeded) {
        ExReleaseSpinLock (&WorkingSetCatcher->SpinLock, OldIrql);
        MmUnlockPagableImageSection (ExPageLockHandle);
        ObDereferenceObject (Process);
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // Mark the Working Set buffer as full and then drop the lock
    // and copy the bytes
    //

    WorkingSetCatcher->CurrentIndex = MAX_WS_CATCH_INDEX;

    ExReleaseSpinLock (&WorkingSetCatcher->SpinLock,OldIrql);

    try {
        RtlCopyMemory (ProcessInformation, &WorkingSetCatcher->WatchInfo[0], SpaceNeeded);
        if (ARGUMENT_PRESENT (ReturnLength) ) {
            *ReturnLength = SpaceNeeded;
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        st = GetExceptionCode ();
    }

    ExAcquireSpinLock (&WorkingSetCatcher->SpinLock, &OldIrql);
    WorkingSetCatcher->CurrentIndex = 0;
    ExReleaseSpinLock (&WorkingSetCatcher->SpinLock, OldIrql);

    MmUnlockPagableImageSection (ExPageLockHandle);
    ObDereferenceObject (Process);

    return st;
}

NTSTATUS
PspQueryQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    )
{
    QUOTA_LIMITS_EX QuotaLimits={0};
    PEPROCESS Process;
    NTSTATUS Status;
    PEPROCESS_QUOTA_BLOCK QuotaBlock;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    ULONG HardEnforcement;
    KAPC_STATE ApcState;

    UNREFERENCED_PARAMETER (ProcessInformationClass);

    if (ProcessInformationLength != sizeof (QUOTA_LIMITS) &&
        ProcessInformationLength != sizeof (QUOTA_LIMITS_EX)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    Status = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    QuotaBlock = Process->QuotaBlock;

    if (QuotaBlock != &PspDefaultQuotaBlock) {
        QuotaLimits.PagedPoolLimit = QuotaBlock->QuotaEntry[PsPagedPool].Limit;
        QuotaLimits.NonPagedPoolLimit = QuotaBlock->QuotaEntry[PsNonPagedPool].Limit;
        QuotaLimits.PagefileLimit = QuotaBlock->QuotaEntry[PsPageFile].Limit;
    } else {
        QuotaLimits.PagedPoolLimit = (SIZE_T)-1;
        QuotaLimits.NonPagedPoolLimit = (SIZE_T)-1;
        QuotaLimits.PagefileLimit = (SIZE_T)-1;
    }

    QuotaLimits.TimeLimit.LowPart = 0xffffffff;
    QuotaLimits.TimeLimit.HighPart = 0xffffffff;

    KeStackAttachProcess (&Process->Pcb, &ApcState);

    Status = MmQueryWorkingSetInformation (&PeakWorkingSetSize,
                                           &WorkingSetSize,
                                           &QuotaLimits.MinimumWorkingSetSize,
                                           &QuotaLimits.MaximumWorkingSetSize,
                                           &HardEnforcement);
    KeUnstackDetachProcess (&ApcState);

    if (HardEnforcement & MM_WORKING_SET_MIN_HARD_ENABLE) {
        QuotaLimits.Flags = QUOTA_LIMITS_HARDWS_MIN_ENABLE;
    } else {
        QuotaLimits.Flags = QUOTA_LIMITS_HARDWS_MIN_DISABLE;
    }

    if (HardEnforcement & MM_WORKING_SET_MAX_HARD_ENABLE) {
        QuotaLimits.Flags |= QUOTA_LIMITS_HARDWS_MAX_ENABLE;
    } else {
        QuotaLimits.Flags |= QUOTA_LIMITS_HARDWS_MAX_DISABLE;
    }

    ObDereferenceObject (Process);

    if (!NT_SUCCESS (Status)) {
        return Status;
    }
    //
    // Either of these may cause an access violation. The
    // exception handler will return access violation as
    // status code.
    //

    try {
        ASSERT (ProcessInformationLength <= sizeof (QuotaLimits));

        RtlCopyMemory (ProcessInformation, &QuotaLimits, ProcessInformationLength);

        if (ARGUMENT_PRESENT (ReturnLength)) {
            *ReturnLength = ProcessInformationLength;
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode ();
    }

    return Status;
}

NTSTATUS
PspQueryPooledQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN KPROCESSOR_MODE PreviousMode
    )
{
    PEPROCESS Process;
    NTSTATUS st;
    PEPROCESS_QUOTA_BLOCK QuotaBlock;
    POOLED_USAGE_AND_LIMITS UsageAndLimits;

    UNREFERENCED_PARAMETER (ProcessInformationClass);

    if (ProcessInformationLength != (ULONG) sizeof (POOLED_USAGE_AND_LIMITS)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_QUERY_INFORMATION,
                                    PsProcessType,
                                    PreviousMode,
                                    &Process,
                                    NULL);
    if (!NT_SUCCESS (st)) {
        return st;
    }


    QuotaBlock = Process->QuotaBlock;

    UsageAndLimits.PagedPoolLimit        = QuotaBlock->QuotaEntry[PsPagedPool].Limit;
    UsageAndLimits.NonPagedPoolLimit     = QuotaBlock->QuotaEntry[PsNonPagedPool].Limit;
    UsageAndLimits.PagefileLimit         = QuotaBlock->QuotaEntry[PsPageFile].Limit;


    UsageAndLimits.PagedPoolUsage        = QuotaBlock->QuotaEntry[PsPagedPool].Usage;
    UsageAndLimits.NonPagedPoolUsage     = QuotaBlock->QuotaEntry[PsNonPagedPool].Usage;
    UsageAndLimits.PagefileUsage         = QuotaBlock->QuotaEntry[PsPageFile].Usage;

    UsageAndLimits.PeakPagedPoolUsage    = QuotaBlock->QuotaEntry[PsPagedPool].Peak;
    UsageAndLimits.PeakNonPagedPoolUsage = QuotaBlock->QuotaEntry[PsNonPagedPool].Peak;
    UsageAndLimits.PeakPagefileUsage     = QuotaBlock->QuotaEntry[PsPageFile].Peak;

    //
    // Since the quota charge and return are lock free we may see Peak and Limit out of step.
    // Usage <= Limit and Usage <= Peak
    // Since Limit is adjusted up and down it does not hold that Peak <= Limit.
    //
#define PSMAX(a,b) (((a) > (b))?(a):(b))

    UsageAndLimits.PagedPoolLimit        = PSMAX (UsageAndLimits.PagedPoolLimit,    UsageAndLimits.PagedPoolUsage);
    UsageAndLimits.NonPagedPoolLimit     = PSMAX (UsageAndLimits.NonPagedPoolLimit, UsageAndLimits.NonPagedPoolUsage);
    UsageAndLimits.PagefileLimit         = PSMAX (UsageAndLimits.PagefileLimit,     UsageAndLimits.PagefileUsage);

    UsageAndLimits.PeakPagedPoolUsage    = PSMAX (UsageAndLimits.PeakPagedPoolUsage,    UsageAndLimits.PagedPoolUsage);
    UsageAndLimits.PeakNonPagedPoolUsage = PSMAX (UsageAndLimits.PeakNonPagedPoolUsage, UsageAndLimits.NonPagedPoolUsage);
    UsageAndLimits.PeakPagefileUsage     = PSMAX (UsageAndLimits.PeakPagefileUsage,     UsageAndLimits.PagefileUsage);

    ObDereferenceObject(Process);

    //
    // Either of these may cause an access violation. The
    // exception handler will return access violation as
    // status code. No further cleanup needs to be done.
    //

    try {
        *(PPOOLED_USAGE_AND_LIMITS) ProcessInformation = UsageAndLimits;

        if (ARGUMENT_PRESENT(ReturnLength) ) {
            *ReturnLength = sizeof(POOLED_USAGE_AND_LIMITS);
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode ();
    }

    return STATUS_SUCCESS;
}


NTSTATUS
PspSetPrimaryToken(
    IN HANDLE ProcessHandle OPTIONAL,
    IN PEPROCESS ProcessPointer OPTIONAL,
    IN HANDLE TokenHandle OPTIONAL,
    IN PACCESS_TOKEN TokenPointer OPTIONAL,
    IN BOOLEAN PrivilegeChecked
    )
/*++

    Sets the primary token for a process.
    The token and process supplied can be either by
    handle or by pointer.

--*/
{
    NTSTATUS Status;
    BOOLEAN HasPrivilege;
    BOOLEAN IsChildToken;
    PEPROCESS Process;
    KPROCESSOR_MODE PreviousMode;
    ACCESS_MASK GrantedAccess;
    PACCESS_TOKEN Token;

    //
    // Check to see if the supplied token is a child of the caller's
    // token. If so, we don't need to do the privilege check.
    //

    PreviousMode = KeGetPreviousMode ();

    if (TokenPointer == NULL) {
        //
        // Reference the specified token, and make sure it can be assigned
        // as a primary token.
        //

        Status = ObReferenceObjectByHandle (TokenHandle,
                                            TOKEN_ASSIGN_PRIMARY,
                                            SeTokenObjectType,
                                            PreviousMode,
                                            &Token,
                                            NULL);

        if (!NT_SUCCESS (Status)) {
            return Status;
        }
    } else {
        Token = TokenPointer;
    }

    //
    // If the privilege check has already been done (when the token was
    // assign to a job for example). We don't want to do it here.
    //
    if (!PrivilegeChecked) {
        Status = SeIsChildTokenByPointer (Token,
                                          &IsChildToken);

        if (!NT_SUCCESS (Status)) {
            goto exit_and_deref_token;
        }

        if (!IsChildToken) {


            //
            // SeCheckPrivilegedObject will perform auditing as appropriate
            //

            HasPrivilege = SeCheckPrivilegedObject (SeAssignPrimaryTokenPrivilege,
                                                    ProcessHandle,
                                                    PROCESS_SET_INFORMATION,
                                                    PreviousMode);

            if (!HasPrivilege) {

                Status = STATUS_PRIVILEGE_NOT_HELD;

                goto exit_and_deref_token;
            }
        }

    }

    if (ProcessPointer == NULL) {
        Status = ObReferenceObjectByHandle (ProcessHandle,
                                            PROCESS_SET_INFORMATION,
                                            PsProcessType,
                                            PreviousMode,
                                            &Process,
                                            NULL);

        if (!NT_SUCCESS (Status)) {

            goto exit_and_deref_token;
        }
    } else {
        Process = ProcessPointer;
    }


    //
    // Check for proper access to the token, and assign the primary
    // token for the process.
    //

    Status = PspAssignPrimaryToken (Process, NULL, Token);

    //
    // Recompute the process's access to itself for use
    // with the CurrentProcess() pseudo handle.
    //

    if (NT_SUCCESS (Status)) {

        NTSTATUS accesst;
        BOOLEAN AccessCheck;
        BOOLEAN MemoryAllocated;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
        SECURITY_SUBJECT_CONTEXT SubjectContext;

        Status = ObGetObjectSecurity (Process,
                                      &SecurityDescriptor,
                                      &MemoryAllocated);

        if (NT_SUCCESS (Status)) {
            SubjectContext.ProcessAuditId = Process;
            SubjectContext.PrimaryToken = PsReferencePrimaryToken (Process);
            SubjectContext.ClientToken = NULL;
            AccessCheck = SeAccessCheck (SecurityDescriptor,
                                         &SubjectContext,
                                         FALSE,
                                         MAXIMUM_ALLOWED,
                                         0,
                                         NULL,
                                         &PsProcessType->TypeInfo.GenericMapping,
                                         PreviousMode,
                                         &GrantedAccess,
                                         &accesst);

            PsDereferencePrimaryTokenEx(Process, SubjectContext.PrimaryToken);
            ObReleaseObjectSecurity (SecurityDescriptor,
                                     MemoryAllocated);

            if (!AccessCheck) {
                GrantedAccess = 0;
            }

            //
            // To keep consistency with process creation, grant these
            // bits otherwise CreateProcessAsUser messes up really badly for
            // restricted tokens and we end up with a process that has no
            // access to itself when new token is set on the suspended
            // process.
            //
            GrantedAccess |= (PROCESS_VM_OPERATION | PROCESS_VM_READ |
                              PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION |
                              PROCESS_TERMINATE | PROCESS_CREATE_THREAD |
                              PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS |
                              PROCESS_SET_INFORMATION | STANDARD_RIGHTS_ALL);

            Process->GrantedAccess = GrantedAccess;
        }
        //
        // Since the process token is being set,
        // Set the device map for process to NULL.
        // During the next reference to the process' device map,
        // the object manager will set the device map for the process
        //
        if (ObIsLUIDDeviceMapsEnabled() != 0) {
            ObDereferenceDeviceMap( Process );
        }
    }

    if (ProcessPointer == NULL) {
        ObDereferenceObject (Process);
    }

exit_and_deref_token:

    if (TokenPointer == NULL) {
        ObDereferenceObject (Token);
    }

    return Status;
}


NTSTATUS
NtQueryInformationProcess(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    )

{
    PEPROCESS Process;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS st;
    PROCESS_BASIC_INFORMATION BasicInfo;
    VM_COUNTERS_EX VmCounters;
    IO_COUNTERS IoCounters;
    KERNEL_USER_TIMES SysUserTime;
    HANDLE DebugPort;
    ULONG HandleCount;
    ULONG DefaultHardErrorMode;
    ULONG DisableBoost;
    ULONG BreakOnTerminationEnabled;
    PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo;
    PROCESS_SESSION_INFORMATION SessionInfo;
    PROCESS_PRIORITY_CLASS PriorityClass;
    ULONG_PTR Wow64Info;
    ULONG Flags;
    PUNICODE_STRING pTempNameInfo;
    SIZE_T MinimumWorkingSetSize;
    SIZE_T MaximumWorkingSetSize;
    ULONG HardEnforcement;
    KAPC_STATE ApcState;

    PAGED_CODE();

    //
    // Get previous processor mode and probe output argument if necessary.
    //

    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        try {
            //
            // Since these functions don't change any state thats not reversible
            // in the error paths we only probe the output buffer for write access.
            // This improves performance by not touching the buffer multiple times
            // And only writing the portions of the buffer that change.
            //
            ProbeForRead (ProcessInformation,
                          ProcessInformationLength,
                          sizeof (ULONG));

            if (ARGUMENT_PRESENT (ReturnLength)) {
                ProbeForWriteUlong (ReturnLength);
            }
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }
    }

    //
    // Check argument validity.
    //

    switch ( ProcessInformationClass ) {

    case ProcessImageFileName:
        {
            ULONG LengthNeeded = 0;

            st = ObReferenceObjectByHandle (ProcessHandle,
                                            PROCESS_QUERY_INFORMATION,
                                            PsProcessType,
                                            PreviousMode,
                                            &Process,
                                            NULL);

            if (!NT_SUCCESS (st)) {
                return st;
            }

            //
            // SeLocateProcessImageName will allocate space for a UNICODE_STRING and point pTempNameInfo
            // at that string.  This memory will be freed later in the routine.
            //

            st = SeLocateProcessImageName (Process, &pTempNameInfo);

            if (!NT_SUCCESS(st)) {
                ObDereferenceObject(Process);
                return st;
            }

            LengthNeeded = sizeof(UNICODE_STRING) + pTempNameInfo->MaximumLength;

            //
            // Either of these may cause an access violation. The
            // exception handler will return access violation as
            // status code. No further cleanup needs to be done.
            //

            try {

                if (ARGUMENT_PRESENT(ReturnLength) ) {
                    *ReturnLength = LengthNeeded;
                }

                if (ProcessInformationLength >= LengthNeeded) {
                    RtlCopyMemory(
                        ProcessInformation,
                        pTempNameInfo,
                        sizeof(UNICODE_STRING) + pTempNameInfo->MaximumLength
                        );
                    ((PUNICODE_STRING) ProcessInformation)->Buffer = (PWSTR)((PUCHAR) ProcessInformation + sizeof(UNICODE_STRING));

                } else {
                    st = STATUS_INFO_LENGTH_MISMATCH;
                }

            } except(EXCEPTION_EXECUTE_HANDLER) {
                st = GetExceptionCode ();
            }

            ObDereferenceObject(Process);
            ExFreePool( pTempNameInfo );

            return st;

        }

    case ProcessWorkingSetWatch:

        return PspQueryWorkingSetWatch (ProcessHandle,
                                        ProcessInformationClass,
                                        ProcessInformation,
                                        ProcessInformationLength,
                                        ReturnLength,
                                        PreviousMode);

    case ProcessBasicInformation:

        if (ProcessInformationLength != (ULONG) sizeof(PROCESS_BASIC_INFORMATION)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        BasicInfo.ExitStatus = Process->ExitStatus;
        BasicInfo.PebBaseAddress = Process->Peb;
        BasicInfo.AffinityMask = Process->Pcb.Affinity;
        BasicInfo.BasePriority = Process->Pcb.BasePriority;
        BasicInfo.UniqueProcessId = (ULONG_PTR)Process->UniqueProcessId;
        BasicInfo.InheritedFromUniqueProcessId = (ULONG_PTR)Process->InheritedFromUniqueProcessId;

        ObDereferenceObject(Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PPROCESS_BASIC_INFORMATION) ProcessInformation = BasicInfo;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(PROCESS_BASIC_INFORMATION);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessDefaultHardErrorMode:

        if (ProcessInformationLength != sizeof(ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        DefaultHardErrorMode = Process->DefaultHardErrorProcessing;

        ObDereferenceObject(Process);

        try {
            *(PULONG) ProcessInformation = DefaultHardErrorMode;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessQuotaLimits:

        return PspQueryQuotaLimits (ProcessHandle,
                                    ProcessInformationClass,
                                    ProcessInformation,
                                    ProcessInformationLength,
                                    ReturnLength,
                                    PreviousMode);

    case ProcessPooledUsageAndLimits:

        return PspQueryPooledQuotaLimits (ProcessHandle,
                                          ProcessInformationClass,
                                          ProcessInformation,
                                          ProcessInformationLength,
                                          ReturnLength,
                                          PreviousMode);

    case ProcessIoCounters:

        if (ProcessInformationLength != (ULONG) sizeof (IO_COUNTERS)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        IoCounters.ReadOperationCount = Process->ReadOperationCount.QuadPart;
        IoCounters.WriteOperationCount = Process->WriteOperationCount.QuadPart;
        IoCounters.OtherOperationCount = Process->OtherOperationCount.QuadPart;
        IoCounters.ReadTransferCount = Process->ReadTransferCount.QuadPart;
        IoCounters.WriteTransferCount = Process->WriteTransferCount.QuadPart;
        IoCounters.OtherTransferCount = Process->OtherTransferCount.QuadPart;

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PIO_COUNTERS) ProcessInformation = IoCounters;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(IO_COUNTERS);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessVmCounters:

        if (ProcessInformationLength != (ULONG) sizeof (VM_COUNTERS)
            && ProcessInformationLength != (ULONG) sizeof (VM_COUNTERS_EX)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }


        //
        // Note: At some point, we might have to grab the statistics
        // lock to reliably read this stuff
        //

        VmCounters.PeakVirtualSize = Process->PeakVirtualSize;
        VmCounters.VirtualSize = Process->VirtualSize;
        VmCounters.PageFaultCount = Process->Vm.PageFaultCount;

        KeStackAttachProcess (&Process->Pcb, &ApcState);

        st = MmQueryWorkingSetInformation (&VmCounters.PeakWorkingSetSize,
                                           &VmCounters.WorkingSetSize,
                                           &MinimumWorkingSetSize,
                                           &MaximumWorkingSetSize,
                                           &HardEnforcement);


        KeUnstackDetachProcess (&ApcState);

        VmCounters.QuotaPeakPagedPoolUsage = Process->QuotaPeak[PsPagedPool];
        VmCounters.QuotaPagedPoolUsage = Process->QuotaUsage[PsPagedPool];
        VmCounters.QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[PsNonPagedPool];
        VmCounters.QuotaNonPagedPoolUsage = Process->QuotaUsage[PsNonPagedPool];
        VmCounters.PagefileUsage = ((SIZE_T) Process->QuotaUsage[PsPageFile]) << PAGE_SHIFT;
        VmCounters.PeakPagefileUsage = ((SIZE_T) Process->QuotaPeak[PsPageFile]) << PAGE_SHIFT;
        VmCounters.PrivateUsage = ((SIZE_T) Process->CommitCharge) << PAGE_SHIFT;

        ObDereferenceObject (Process);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            RtlCopyMemory(ProcessInformation,
                          &VmCounters,
                          ProcessInformationLength);
            
            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = ProcessInformationLength;
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessTimes:

        if ( ProcessInformationLength != (ULONG) sizeof(KERNEL_USER_TIMES) ) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Need some type of interlock on KiTimeLock
        //

        SysUserTime.KernelTime.QuadPart = UInt32x32To64(Process->Pcb.KernelTime,
                                                        KeMaximumIncrement);

        SysUserTime.UserTime.QuadPart = UInt32x32To64(Process->Pcb.UserTime,
                                                      KeMaximumIncrement);

        SysUserTime.CreateTime = Process->CreateTime;
        SysUserTime.ExitTime = Process->ExitTime;

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PKERNEL_USER_TIMES) ProcessInformation = SysUserTime;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (KERNEL_USER_TIMES);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessDebugPort :

        //
        if (ProcessInformationLength != (ULONG) sizeof (HANDLE)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (Process->DebugPort == NULL) {

            DebugPort = NULL;

        } else {

            DebugPort = (HANDLE)-1;

        }

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PHANDLE) ProcessInformation = DebugPort;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(HANDLE);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessDebugObjectHandle :
        //
        if (ProcessInformationLength != sizeof (HANDLE)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = DbgkOpenProcessDebugPort (Process,
                                       PreviousMode,
                                       &DebugPort);

        if (!NT_SUCCESS (st)) {
            DebugPort = NULL;
        }

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PHANDLE) ProcessInformation = DebugPort;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (HANDLE);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            if (DebugPort != NULL) {
                ObCloseHandle (DebugPort, PreviousMode);
            }
            return GetExceptionCode ();
        }

        return st;

    case ProcessDebugFlags :

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        try {
            *(PULONG) ProcessInformation = (Process->Flags&PS_PROCESS_FLAGS_NO_DEBUG_INHERIT)?0:PROCESS_DEBUG_INHERIT;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(HANDLE);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            st = GetExceptionCode ();
        }

        ObDereferenceObject (Process);

        return st;


    case ProcessHandleCount :

        if (ProcessInformationLength != (ULONG) sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        HandleCount = ObGetProcessHandleCount (Process);

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PULONG) ProcessInformation = HandleCount;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessLdtInformation :

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PspQueryLdtInformation (Process,
                                     ProcessInformation,
                                     ProcessInformationLength,
                                     ReturnLength);

        ObDereferenceObject(Process);
        return st;


    case ProcessWx86Information :

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Flags = Process->Flags & PS_PROCESS_FLAGS_VDM_ALLOWED ? 1 : 0;

        ObDereferenceObject (Process);

        //
        // The returned flags is used as a BOOLEAN to indicate whether the
        // ProcessHandle specifies a NtVdm Process.  In another words, the caller
        // can simply do a
        //      if (ReturnedValue == TRUE) {
        //          a ntvdm process;
        //      } else {
        //          NOT a ntvdm process;
        //      }
        //

        try {
            *(PULONG)ProcessInformation = Flags;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return st;

    case ProcessPriorityBoost:
        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        DisableBoost = Process->Pcb.DisableBoost ? 1 : 0;

        ObDereferenceObject (Process);

        try {
            *(PULONG)ProcessInformation = DisableBoost;

            if (ARGUMENT_PRESENT( ReturnLength) ) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return st;

    case ProcessDeviceMap:
        DeviceMapInfo = (PPROCESS_DEVICEMAP_INFORMATION)ProcessInformation;
        if (ProcessInformationLength < sizeof (DeviceMapInfo->Query)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        if (ProcessInformationLength == sizeof (PROCESS_DEVICEMAP_INFORMATION_EX)) {
            try {
                Flags = ((PPROCESS_DEVICEMAP_INFORMATION_EX)DeviceMapInfo)->Flags;
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode ();
            }
            if ( (Flags & ~(PROCESS_LUID_DOSDEVICES_ONLY)) ||
                 (ObIsLUIDDeviceMapsEnabled () == 0) ) {
                return STATUS_INVALID_PARAMETER;
            }
        }
        else {
            if (ProcessInformationLength == sizeof (DeviceMapInfo->Query)) {
                Flags = 0;
            }
            else {
                return STATUS_INFO_LENGTH_MISMATCH;
            }
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = ObQueryDeviceMapInformation (Process, DeviceMapInfo, Flags);
        ObDereferenceObject(Process);
        return st;

    case ProcessSessionInformation :

        if (ProcessInformationLength != (ULONG) sizeof (PROCESS_SESSION_INFORMATION)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        SessionInfo.SessionId = MmGetSessionId (Process);

        ObDereferenceObject (Process);

        try {
            *(PPROCESS_SESSION_INFORMATION) ProcessInformation = SessionInfo;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof(PROCESS_SESSION_INFORMATION);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;



    case ProcessPriorityClass:

        if (ProcessInformationLength != sizeof (PROCESS_PRIORITY_CLASS)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        PriorityClass.Foreground = FALSE;
        PriorityClass.PriorityClass = Process->PriorityClass;

        ObDereferenceObject (Process);

        try {
            *(PPROCESS_PRIORITY_CLASS) ProcessInformation = PriorityClass;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof(PROCESS_PRIORITY_CLASS);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;


    case ProcessWow64Information:

        if (ProcessInformationLength != sizeof (ULONG_PTR)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        Wow64Info = 0;

        //
        // Acquire process rundown protection as we are about to look at process structures torn down at
        // process exit.
        //
        if (ExAcquireRundownProtection (&Process->RundownProtect)) {
            PWOW64_PROCESS Wow64Process;

            if ((Wow64Process = PS_GET_WOW64_PROCESS (Process)) != NULL) {
                Wow64Info = (ULONG_PTR)(Wow64Process->Wow64);
            }

            ExReleaseRundownProtection (&Process->RundownProtect);
        }


        ObDereferenceObject (Process);

        try {
            *(PULONG_PTR)ProcessInformation = Wow64Info;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (ULONG_PTR);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;


    case ProcessLUIDDeviceMapsEnabled:

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            *(PULONG)ProcessInformation = ObIsLUIDDeviceMapsEnabled ();

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessBreakOnTermination:

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (Process->Flags & PS_PROCESS_FLAGS_BREAK_ON_TERMINATION) {

            BreakOnTerminationEnabled = 1;

        } else {

            BreakOnTerminationEnabled = 0;

        }

        ObDereferenceObject (Process);

        try {

            *(PULONG)ProcessInformation = BreakOnTerminationEnabled;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (ULONG);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ProcessHandleTracing: {
        PPROCESS_HANDLE_TRACING_QUERY Pht;
        PHANDLE_TABLE HandleTable;
        PHANDLE_TRACE_DEBUG_INFO DebugInfo;
        HANDLE_TRACE_DB_ENTRY Trace;
        PPROCESS_HANDLE_TRACING_ENTRY NextTrace;
        ULONG StacksLeft;
        ULONG i, j;

        if (ProcessInformationLength < FIELD_OFFSET (PROCESS_HANDLE_TRACING_QUERY,
                                                     HandleTrace)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        Pht = (PPROCESS_HANDLE_TRACING_QUERY) ProcessInformation;
        StacksLeft = (ProcessInformationLength - FIELD_OFFSET (PROCESS_HANDLE_TRACING_QUERY,
                                                              HandleTrace)) /
                     sizeof (Pht->HandleTrace[0]);
        NextTrace = &Pht->HandleTrace[0];

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }
        HandleTable = ObReferenceProcessHandleTable (Process);

        if (HandleTable != NULL) {
            DebugInfo = ExReferenceHandleDebugInfo (HandleTable);
            if (DebugInfo != NULL) {
                try {
                    Pht->TotalTraces = 0;
                    j = DebugInfo->CurrentStackIndex % DebugInfo->TableSize;
                    for (i = 0; i < DebugInfo->TableSize; i++) {
                        RtlCopyMemory (&Trace, &DebugInfo->TraceDb[j], sizeof (Trace));
                        if ((Pht->Handle == Trace.Handle || Pht->Handle == 0) && Trace.Type != 0) {
                            Pht->TotalTraces++;
                            if (StacksLeft > 0) {
                                StacksLeft--;
                                NextTrace->Handle = Trace.Handle;
                                NextTrace->ClientId = Trace.ClientId;
                                NextTrace->Type = Trace.Type;
                                RtlCopyMemory (NextTrace->Stacks,
                                               Trace.StackTrace,
                                               min (sizeof (NextTrace->Stacks),
                                                    sizeof (Trace.StackTrace)));
                                NextTrace++;

                            } else {
                                st = STATUS_INFO_LENGTH_MISMATCH;
                            }
                        }
                        if (j == 0) {
                            j = DebugInfo->TableSize - 1;
                        } else {
                            j--;
                        }
                    }
                    if (ARGUMENT_PRESENT (ReturnLength)) {
                        *ReturnLength = (ULONG) ((PUCHAR) NextTrace - (PUCHAR) Pht);
                    }
                } except (EXCEPTION_EXECUTE_HANDLER) {
                    st = GetExceptionCode ();
                }

                ExDereferenceHandleDebugInfo (HandleTable, DebugInfo);

            } else {
                st = STATUS_INVALID_PARAMETER;
            }
            ObDereferenceProcessHandleTable (Process);
        } else {
            st = STATUS_PROCESS_IS_TERMINATING;
        }

        ObDereferenceObject(Process);
        return st;
    }

    case ProcessCookie: {
        ULONG Cookie;
        LARGE_INTEGER Time;
        PKPRCB Prcb;
  

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        if (ProcessHandle != NtCurrentProcess ()) {
            return STATUS_INVALID_PARAMETER;
        }

        Process = PsGetCurrentProcess ();

        while (1) {
            Cookie = Process->Cookie;
            if (Cookie != 0) {

                try {
                    *(PULONG)ProcessInformation = Process->Cookie;

                    if (ARGUMENT_PRESENT (ReturnLength)) {
                        *ReturnLength = sizeof (ULONG);
                    }
                } except(EXCEPTION_EXECUTE_HANDLER) {
                    return GetExceptionCode ();
                }

                return STATUS_SUCCESS;

            } else {
                KeQuerySystemTime (&Time);
                Prcb = KeGetCurrentPrcb ();
                Cookie = Time.LowPart ^ Time.HighPart ^ Prcb->InterruptTime ^ Prcb->KeSystemCalls;
                InterlockedCompareExchange ((PLONG)&Process->Cookie, Cookie, 0);
            }
        }
    }

    default:

        return STATUS_INVALID_INFO_CLASS;
    }

}

NTSTATUS
NtQueryPortInformationProcess(
    VOID
    )

/*++

Routine Description:

    This function tests whether a debug port or an exception port is attached
    to the current process and returns a corresponding value. This function is
    used to bypass raising an exception through the system when no associated
    ports are present.

    N.B. This improves performance considerably with respect to raising
         software exceptions in user mode on AMD64 and IA64 systems.

Arguments:

    None.

Return Value:

    A success value of TRUE is returned if either a debug or exception port
    is associated with the current process. Otherwise, a success value of
    FALSE is returned.

--*/

{

    PEPROCESS Process;
    PETHREAD Thread;

    //
    // If the process has a debug port and it is not being hidden from the
    // debugger, then return a success status of TRUE. Otherwise, is the
    // process has an exception port, then return a success status of TRUE.
    // Otherwise, return a success status of FALSE.
    //

    Thread = PsGetCurrentThread();
    Process = PsGetCurrentProcessByThread (Thread);
    if ((Process->DebugPort != NULL) &&
        ((Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) == 0)) {

        return TRUE;

    } else if (Process->ExceptionPort != NULL) {
        return TRUE;

    } else {
        return FALSE;
    }
}

NTSTATUS
PspSetQuotaLimits(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    IN PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    IN KPROCESSOR_MODE PreviousMode
    )
{
    PEPROCESS Process;
    PETHREAD CurrentThread;
    QUOTA_LIMITS_EX RequestedLimits;
    PEPROCESS_QUOTA_BLOCK NewQuotaBlock;
    NTSTATUS st, ReturnStatus;
    BOOLEAN OkToIncrease, IgnoreError;
    PEJOB Job;
    KAPC_STATE ApcState;
    ULONG EnableHardLimits;
    BOOLEAN PurgeRequest;

    UNREFERENCED_PARAMETER (ProcessInformationClass);

    try {

        if (ProcessInformationLength == sizeof (QUOTA_LIMITS)) {
            RtlCopyMemory (&RequestedLimits,
                           ProcessInformation,
                           sizeof (QUOTA_LIMITS));
            RequestedLimits.Reserved1 = 0;
            RequestedLimits.Reserved2 = 0;
            RequestedLimits.Reserved3 = 0;
            RequestedLimits.Reserved4 = 0;
            RequestedLimits.Reserved5 = 0;
            RequestedLimits.Flags = 0;
        } else if (ProcessInformationLength == sizeof (QUOTA_LIMITS_EX)) {
            RequestedLimits = *(PQUOTA_LIMITS_EX) ProcessInformation;
        } else {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

    } except (EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode ();
    }

    //
    // All unused flags must be zero
    //
    if (RequestedLimits.Flags & ~(QUOTA_LIMITS_HARDWS_MAX_ENABLE|QUOTA_LIMITS_HARDWS_MAX_DISABLE|
                                  QUOTA_LIMITS_HARDWS_MIN_ENABLE|QUOTA_LIMITS_HARDWS_MIN_DISABLE)) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Disallow both enable and disable bits set at the same time.
    //
    if (PS_TEST_ALL_BITS_SET (RequestedLimits.Flags, QUOTA_LIMITS_HARDWS_MIN_ENABLE|QUOTA_LIMITS_HARDWS_MIN_DISABLE) ||
        PS_TEST_ALL_BITS_SET (RequestedLimits.Flags, QUOTA_LIMITS_HARDWS_MAX_ENABLE|QUOTA_LIMITS_HARDWS_MAX_DISABLE)) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // See if we are changing the hard limits or not
    //

    EnableHardLimits = 0;

    if (RequestedLimits.Flags&QUOTA_LIMITS_HARDWS_MIN_ENABLE) {
        EnableHardLimits = MM_WORKING_SET_MIN_HARD_ENABLE;
    } else if (RequestedLimits.Flags&QUOTA_LIMITS_HARDWS_MIN_DISABLE) {
        EnableHardLimits = MM_WORKING_SET_MIN_HARD_DISABLE;
    }

    if (RequestedLimits.Flags&QUOTA_LIMITS_HARDWS_MAX_ENABLE) {
        EnableHardLimits |= MM_WORKING_SET_MAX_HARD_ENABLE;
    } else if (RequestedLimits.Flags&QUOTA_LIMITS_HARDWS_MAX_DISABLE) {
        EnableHardLimits |= MM_WORKING_SET_MAX_HARD_DISABLE;
    }


    //
    // All reserved fields must be zero
    //
    if (RequestedLimits.Reserved1 != 0 || RequestedLimits.Reserved2 != 0 ||
        RequestedLimits.Reserved3 != 0 || RequestedLimits.Reserved4 != 0 ||
        RequestedLimits.Reserved5 != 0) {
        return STATUS_INVALID_PARAMETER;
    }


    st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_SET_QUOTA,
                                    PsProcessType,
                                    PreviousMode,
                                    &Process,
                                    NULL);

    if (!NT_SUCCESS (st)) {
        return st;
    }

    CurrentThread = PsGetCurrentThread ();

    //
    // Now we are ready to set the quota limits for the process
    //
    // If the process already has a quota block, then all we allow
    // is working set changes.
    //
    // If the process has no quota block, all that can be done is a
    // quota set operation.
    //
    // If a quota field is zero, we pick the value.
    //
    // Setting quotas requires the SeIncreaseQuotaPrivilege (except for
    // working set size since this is only advisory).
    //


    ReturnStatus = STATUS_SUCCESS;

    if ((Process->QuotaBlock == &PspDefaultQuotaBlock) &&
         (RequestedLimits.MinimumWorkingSetSize == 0 || RequestedLimits.MaximumWorkingSetSize == 0)) {

        //
        // You must have a privilege to assign quotas
        //

        if (!SeSinglePrivilegeCheck (SeIncreaseQuotaPrivilege, PreviousMode)) {
            ObDereferenceObject (Process);
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        NewQuotaBlock = ExAllocatePoolWithTag (NonPagedPool, sizeof(*NewQuotaBlock), 'bQsP');
        if (NewQuotaBlock == NULL) {
            ObDereferenceObject (Process);
            return STATUS_NO_MEMORY;
        }

        RtlZeroMemory (NewQuotaBlock, sizeof (*NewQuotaBlock));

        //
        // Initialize the quota block
        //
        NewQuotaBlock->ReferenceCount = 1;
        NewQuotaBlock->ProcessCount   = 1;

        NewQuotaBlock->QuotaEntry[PsNonPagedPool].Peak  = Process->QuotaPeak[PsNonPagedPool];
        NewQuotaBlock->QuotaEntry[PsPagedPool].Peak     = Process->QuotaPeak[PsPagedPool];
        NewQuotaBlock->QuotaEntry[PsPageFile].Peak      = Process->QuotaPeak[PsPageFile];

        //
        // Now compute limits
        //

        //
        // Get the defaults that the system would pick.
        //

        NewQuotaBlock->QuotaEntry[PsPagedPool].Limit    = PspDefaultPagedLimit;
        NewQuotaBlock->QuotaEntry[PsNonPagedPool].Limit = PspDefaultNonPagedLimit;
        NewQuotaBlock->QuotaEntry[PsPageFile].Limit     = PspDefaultPagefileLimit;

        // Everything is set. Now double check to quota block field
        // If we still have no quota block then assign and succeed.
        // Otherwise punt.
        //

        if (InterlockedCompareExchangePointer (&Process->QuotaBlock,
                                               NewQuotaBlock,
                                               &PspDefaultQuotaBlock) != &PspDefaultQuotaBlock) {
            ExFreePool (NewQuotaBlock);
        } else {
            PspInsertQuotaBlock (NewQuotaBlock);
        }


    } else {

        //
        // Only allow a working set size change
        //

        if (RequestedLimits.MinimumWorkingSetSize &&
            RequestedLimits.MaximumWorkingSetSize) {

            //
            // See if the caller just wants to purge the working set.
            // This is an unprivileged operation.
            //
            if (RequestedLimits.MinimumWorkingSetSize == (SIZE_T)-1 &&
                RequestedLimits.MaximumWorkingSetSize == (SIZE_T)-1) {
                PurgeRequest = TRUE;
                OkToIncrease = FALSE;
            } else {
                PurgeRequest = FALSE;

                if (SeSinglePrivilegeCheck (SeIncreaseBasePriorityPrivilege,
                                            PreviousMode)) {
                    OkToIncrease = TRUE;
                } else {
                    OkToIncrease = FALSE;
                }
            }

            do {
                IgnoreError = FALSE;

                KeStackAttachProcess (&Process->Pcb, &ApcState);

                KeEnterGuardedRegionThread (&CurrentThread->Tcb);

                Job = Process->Job;
                if (Job != NULL) {
                    ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);

                    if (Job->LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) {
                        //
                        // Don't let a process in a job change if job limits are applied
                        // except purge requests which can always be done.
                        //

                        EnableHardLimits = MM_WORKING_SET_MAX_HARD_ENABLE;
                        OkToIncrease = TRUE;
                        IgnoreError = TRUE; // we must always set enforcement value

                        if (!PurgeRequest) {
                            RequestedLimits.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
                            RequestedLimits.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
                        }
                    }

                    PspLockWorkingSetChangeExclusiveUnsafe ();

                    ExReleaseResourceLite (&Job->JobLock);
                }

                ReturnStatus = MmAdjustWorkingSetSizeEx (RequestedLimits.MinimumWorkingSetSize,
                                                         RequestedLimits.MaximumWorkingSetSize,
                                                         FALSE,
                                                         OkToIncrease,
                                                         EnableHardLimits);

                if (!NT_SUCCESS (ReturnStatus) && IgnoreError) {
                    MmEnforceWorkingSetLimit (Process,
                                              EnableHardLimits);
                }

                if (Job != NULL) {
                    PspUnlockWorkingSetChangeExclusiveUnsafe ();
                }

                KeLeaveGuardedRegionThread (&CurrentThread->Tcb);

                KeUnstackDetachProcess (&ApcState);

                //
                // We loop here in case this process was added to a job
                // after we checked but before we set the limits
                //

            } while (Process->Job != Job);

        }
    }

    ObDereferenceObject(Process);

    return ReturnStatus;
}

NTSTATUS
NtSetInformationProcess(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    IN PVOID ProcessInformation,
    IN ULONG ProcessInformationLength
    )

/*++

Routine Description:

    This function sets the state of a process object.

Arguments:

    ProcessHandle - Supplies a handle to a process object.

    ProcessInformationClass - Supplies the class of information being
        set.

    ProcessInformation - Supplies a pointer to a record that contains the
        information to set.

    ProcessInformationLength - Supplies the length of the record that contains
        the information to set.

Return Value:

    TBS

--*/

{

    PEPROCESS Process;
    PETHREAD Thread;
    PETHREAD CurrentThread;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS st;
    KPRIORITY BasePriority;
    ULONG BoostValue;
    ULONG DefaultHardErrorMode;
    PVOID ExceptionPort;
    BOOLEAN EnableAlignmentFaultFixup;
    HANDLE ExceptionPortHandle;
    ULONG ProbeAlignment;
    HANDLE PrimaryTokenHandle;
    BOOLEAN HasPrivilege = FALSE;
    UCHAR MemoryPriority;
    PROCESS_PRIORITY_CLASS LocalPriorityClass;
    PROCESS_FOREGROUND_BACKGROUND LocalForeground;
    KAFFINITY Affinity, AffinityWithMasks;
    ULONG DisableBoost;
    BOOLEAN bDisableBoost;
    PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo;
    HANDLE DirectoryHandle;
    PROCESS_SESSION_INFORMATION SessionInfo;
    ULONG EnableBreakOnTermination;
    PEJOB Job;

    PAGED_CODE();

    //
    // Get previous processor mode and probe input argument if necessary.
    //

    CurrentThread = PsGetCurrentThread ();
    PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);

    if (PreviousMode != KernelMode) {

        if (ProcessInformationClass == ProcessBasePriority) {
            ProbeAlignment = sizeof (KPRIORITY);
        } else if (ProcessInformationClass == ProcessEnableAlignmentFaultFixup) {
            ProbeAlignment = sizeof (BOOLEAN);
        } else if (ProcessInformationClass == ProcessForegroundInformation) {
            ProbeAlignment = sizeof (PROCESS_FOREGROUND_BACKGROUND);
        } else if (ProcessInformationClass == ProcessPriorityClass) {
            ProbeAlignment = sizeof (BOOLEAN);
        } else if (ProcessInformationClass == ProcessAffinityMask) {
            ProbeAlignment = sizeof (ULONG_PTR);
        } else {
            ProbeAlignment = sizeof (ULONG);
        }

        try {

            ProbeForRead (ProcessInformation,
                          ProcessInformationLength,
                          ProbeAlignment);

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }
    }

    //
    // Check argument validity.
    //

    switch (ProcessInformationClass) {

    case ProcessWorkingSetWatch: {
        PPAGEFAULT_HISTORY WorkingSetCatcher;

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PsChargeProcessNonPagedPoolQuota (Process, WS_CATCH_SIZE);
        if (NT_SUCCESS (st)) {

            WorkingSetCatcher = ExAllocatePoolWithTag (NonPagedPool, WS_CATCH_SIZE, 'sWsP');
            if (!WorkingSetCatcher) {
                st = STATUS_NO_MEMORY;
            } else {

                PsWatchEnabled = TRUE;
                WorkingSetCatcher->CurrentIndex = 0;
                WorkingSetCatcher->MaxIndex = MAX_WS_CATCH_INDEX;
                KeInitializeSpinLock (&WorkingSetCatcher->SpinLock);

                //
                // This only ever goes on the process and isn't removed till process object deletion.
                // We just need to protect against multiple callers here.
                //
                if (InterlockedCompareExchangePointer (&Process->WorkingSetWatch,
                                                       WorkingSetCatcher, NULL) == NULL) {
                    st = STATUS_SUCCESS;
                } else {
                    ExFreePool (WorkingSetCatcher);
                    st = STATUS_PORT_ALREADY_SET;
                }
            }
            if (!NT_SUCCESS (st)) {
                PsReturnProcessNonPagedPoolQuota (Process, WS_CATCH_SIZE);
            }
        }

        ObDereferenceObject (Process);

        return st;
    }

    case ProcessBasePriority: {


        //
        // THIS ITEM CODE IS OBSOLETE !
        //

        if (ProcessInformationLength != sizeof (KPRIORITY)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            BasePriority = *(KPRIORITY *)ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }

        if (BasePriority & 0x80000000) {
            MemoryPriority = MEMORY_PRIORITY_FOREGROUND;
            BasePriority &= ~0x80000000;
        } else {
            MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
        }

        if (BasePriority > HIGH_PRIORITY ||
            BasePriority <= LOW_PRIORITY) {

            return STATUS_INVALID_PARAMETER;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        if (BasePriority > Process->Pcb.BasePriority) {

            //
            // Increasing the base priority of a process is a
            // privileged operation.  Check for the privilege
            // here.
            //

            HasPrivilege = SeCheckPrivilegedObject (SeIncreaseBasePriorityPrivilege,
                                                    ProcessHandle,
                                                    PROCESS_SET_INFORMATION,
                                                    PreviousMode);

            if (!HasPrivilege) {

                ObDereferenceObject (Process);
                return STATUS_PRIVILEGE_NOT_HELD;
            }
        }

        KeSetPriorityProcess (&Process->Pcb, BasePriority);
        MmSetMemoryPriorityProcess (Process, MemoryPriority);
        ObDereferenceObject (Process);

        return STATUS_SUCCESS;
    }

    case ProcessPriorityClass: {
        if (ProcessInformationLength != sizeof (PROCESS_PRIORITY_CLASS)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            LocalPriorityClass = *(PPROCESS_PRIORITY_CLASS)ProcessInformation;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (LocalPriorityClass.PriorityClass > PROCESS_PRIORITY_CLASS_ABOVE_NORMAL) {
            return STATUS_INVALID_PARAMETER;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        if (LocalPriorityClass.PriorityClass != Process->PriorityClass &&
            LocalPriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME) {

            //
            // Increasing the base priority of a process is a
            // privileged operation.  Check for the privilege
            // here.
            //

            HasPrivilege = SeCheckPrivilegedObject (SeIncreaseBasePriorityPrivilege,
                                                    ProcessHandle,
                                                    PROCESS_SET_INFORMATION,
                                                    PreviousMode);

            if (!HasPrivilege) {

                ObDereferenceObject (Process);
                return STATUS_PRIVILEGE_NOT_HELD;
            }
        }

        //
        // If the process has a job object, override whatever the process
        // is calling with with the value from the job object
        //
        Job = Process->Job;
        if (Job != NULL) {
            KeEnterCriticalRegionThread (&CurrentThread->Tcb);
            ExAcquireResourceSharedLite (&Job->JobLock, TRUE);

            if (Job->LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS) {
                LocalPriorityClass.PriorityClass = Job->PriorityClass;
            }

            ExReleaseResourceLite (&Job->JobLock);
            KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
        }

        Process->PriorityClass = LocalPriorityClass.PriorityClass;

        PsSetProcessPriorityByClass (Process,
                                     LocalPriorityClass.Foreground ?
                                         PsProcessPriorityForeground : PsProcessPriorityBackground);

        ObDereferenceObject (Process);

        return STATUS_SUCCESS;
    }

    case ProcessForegroundInformation: {

        if (ProcessInformationLength != sizeof (PROCESS_FOREGROUND_BACKGROUND)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            LocalForeground = *(PPROCESS_FOREGROUND_BACKGROUND)ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        PsSetProcessPriorityByClass (Process,
                                     LocalForeground.Foreground ?
                                         PsProcessPriorityForeground : PsProcessPriorityBackground);

        ObDereferenceObject (Process);

        return STATUS_SUCCESS;
    }

    case ProcessRaisePriority: {
        //
        // This code is used to boost the priority of all threads
        // within a process. It cannot be used to change a thread into
        // a realtime class, or to lower the priority of a thread. The
        // argument is a boost value that is added to the base priority
        // of the specified process.
        //


        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            BoostValue = *(PULONG)ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Get the process create/delete lock and walk through the
        // thread list boosting each thread.
        //


        if (ExAcquireRundownProtection (&Process->RundownProtect)) {
            for (Thread = PsGetNextProcessThread (Process, NULL);
                 Thread != NULL;
                 Thread = PsGetNextProcessThread (Process, Thread)) {

                 KeBoostPriorityThread (&Thread->Tcb, (KPRIORITY)BoostValue);
            }
            ExReleaseRundownProtection (&Process->RundownProtect);
        } else {
            st = STATUS_PROCESS_IS_TERMINATING;
        }

        ObDereferenceObject (Process);

        return st;
    }

    case ProcessDefaultHardErrorMode: {
        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            DefaultHardErrorMode = *(PULONG)ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Process->DefaultHardErrorProcessing = DefaultHardErrorMode;
        if (DefaultHardErrorMode & PROCESS_HARDERROR_ALIGNMENT_BIT) {
            KeSetAutoAlignmentProcess (&Process->Pcb,TRUE);
        } else {
            KeSetAutoAlignmentProcess (&Process->Pcb,FALSE);
        }

        ObDereferenceObject (Process);

        return STATUS_SUCCESS;
    }

    case ProcessQuotaLimits: {
        return PspSetQuotaLimits (ProcessHandle,
                                  ProcessInformationClass,
                                  ProcessInformation,
                                  ProcessInformationLength,
                                  PreviousMode);
    }

    case ProcessExceptionPort : {
        if (ProcessInformationLength != sizeof (HANDLE)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            ExceptionPortHandle = *(PHANDLE) ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (!SeSinglePrivilegeCheck (SeTcbPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }



        st = ObReferenceObjectByHandle (ExceptionPortHandle,
                                        0,
                                        LpcPortObjectType,
                                        PreviousMode,
                                        &ExceptionPort,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_PORT,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            ObDereferenceObject (ExceptionPort);
            return st;
        }

        //
        // We are only allowed to put the exception port on. It doesn't get removed till process delete.
        //
        if (InterlockedCompareExchangePointer (&Process->ExceptionPort, ExceptionPort, NULL) == NULL) {
            st = STATUS_SUCCESS;
        } else {
            ObDereferenceObject (ExceptionPort);
            st = STATUS_PORT_ALREADY_SET;
        }
        ObDereferenceObject (Process);

        return st;
    }

    case ProcessAccessToken : {

        if (ProcessInformationLength != sizeof (PROCESS_ACCESS_TOKEN)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            PrimaryTokenHandle  = ((PROCESS_ACCESS_TOKEN *)ProcessInformation)->Token;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }


        st = PspSetPrimaryToken (ProcessHandle,
                                 NULL,
                                 PrimaryTokenHandle,
                                 NULL,
                                 FALSE);

        return st;
    }


    case ProcessLdtInformation:

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION | PROCESS_VM_WRITE,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PspSetLdtInformation (Process,
                                   ProcessInformation,
                                   ProcessInformationLength);

        ObDereferenceObject (Process);
        return st;

    case ProcessLdtSize:

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION | PROCESS_VM_WRITE,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PspSetLdtSize (Process,
                            ProcessInformation,
                            ProcessInformationLength);

        ObDereferenceObject(Process);
        return st;

    case ProcessIoPortHandlers:

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PspSetProcessIoHandlers (Process,
                                      ProcessInformation,
                                      ProcessInformationLength);

        ObDereferenceObject (Process);

        return st;

    case ProcessUserModeIOPL:

        //
        // Must make sure the caller is a trusted subsystem with the
        // appropriate privilege level before executing this call.
        // If the calls returns FALSE we must return an error code.
        //

        if (!SeSinglePrivilegeCheck (SeTcbPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (NT_SUCCESS (st)) {

#if defined (_X86_)

            Ke386SetIOPL ();

#endif

            ObDereferenceObject (Process);
        }

        return st;

        //
        // Enable/disable auto-alignment fixup for a process and all its threads.
        //

    case ProcessEnableAlignmentFaultFixup: {

        if (ProcessInformationLength != sizeof (BOOLEAN)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            EnableAlignmentFaultFixup = *(PBOOLEAN)ProcessInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (EnableAlignmentFaultFixup) {
            Process->DefaultHardErrorProcessing |= PROCESS_HARDERROR_ALIGNMENT_BIT;
        } else {
            Process->DefaultHardErrorProcessing &= ~PROCESS_HARDERROR_ALIGNMENT_BIT;
        }

        KeSetAutoAlignmentProcess (&(Process->Pcb), EnableAlignmentFaultFixup);

        ObDereferenceObject (Process);

        return STATUS_SUCCESS;
    }

    case ProcessWx86Information : {

        ULONG  VdmAllowedFlags;

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {

            VdmAllowedFlags = *(PULONG)ProcessInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        //
        // Must make sure the caller is a trusted subsystem with the
        // appropriate privilege level before executing this call.
        // If the calls returns FALSE we must return an error code.
        //
        if (!SeSinglePrivilegeCheck (SeTcbPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        //
        // Make sure the ProcessHandle is indeed a process handle.
        //

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (NT_SUCCESS (st)) {

            //
            // For now, non zero Flags will allowed VDM.
            //

            if (VdmAllowedFlags) {
                PS_SET_BITS(&Process->Flags, PS_PROCESS_FLAGS_VDM_ALLOWED);
            } else {
                PS_CLEAR_BITS(&Process->Flags, PS_PROCESS_FLAGS_VDM_ALLOWED);
            }
            ObDereferenceObject(Process);
        }

        return st;
    }

    case ProcessAffinityMask:

        if (ProcessInformationLength != sizeof (KAFFINITY)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            Affinity = *(PKAFFINITY)ProcessInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        AffinityWithMasks = Affinity & KeActiveProcessors;

        if (!Affinity || (AffinityWithMasks != Affinity)) {
            return STATUS_INVALID_PARAMETER;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // If the process has a job object, override whatever the process
        // is calling with with the value from the job object
        //
        Job = Process->Job;
        if (Job != NULL) {
            KeEnterCriticalRegionThread (&CurrentThread->Tcb);
            ExAcquireResourceSharedLite (&Job->JobLock, TRUE);

            if (Job->LimitFlags & JOB_OBJECT_LIMIT_AFFINITY) {
                AffinityWithMasks = Job->Affinity;
            }

            ExReleaseResourceLite (&Job->JobLock);
            KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
        }

        if (ExAcquireRundownProtection (&Process->RundownProtect)) {

            PspLockProcessExclusive (Process, CurrentThread);

            KeSetAffinityProcess (&Process->Pcb, AffinityWithMasks);

            PspUnlockProcessExclusive (Process, CurrentThread);

            ExReleaseRundownProtection (&Process->RundownProtect);

            st = STATUS_SUCCESS;
        } else {
            st = STATUS_PROCESS_IS_TERMINATING;
        }
        ObDereferenceObject (Process);
        return st;

    case ProcessPriorityBoost:
        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            DisableBoost = *(PULONG)ProcessInformation;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        bDisableBoost = (DisableBoost ? TRUE : FALSE);

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Acquire rundown protection to give back the correct error
        // if the process has or is being terminated.
        //


        if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
            st = STATUS_PROCESS_IS_TERMINATING;
        } else {
            PLIST_ENTRY Next;

            PspLockProcessExclusive (Process, CurrentThread);

            Process->Pcb.DisableBoost = bDisableBoost;

            for (Next = Process->ThreadListHead.Flink;
                 Next != &Process->ThreadListHead;
                 Next = Next->Flink) {
                Thread = (PETHREAD)(CONTAINING_RECORD(Next, ETHREAD, ThreadListEntry));
                KeSetDisableBoostThread (&Thread->Tcb, bDisableBoost);
            }

            PspUnlockProcessExclusive (Process, CurrentThread);

            ExReleaseRundownProtection (&Process->RundownProtect);
        }

        ObDereferenceObject (Process);
        return st;

    case ProcessDebugFlags : {
        ULONG Flags;

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        try {
            Flags = *(PULONG) ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            Flags = 0;
            st = GetExceptionCode ();
        }
        if (NT_SUCCESS (st)) {
            if (Flags & ~PROCESS_DEBUG_INHERIT) {
                st = STATUS_INVALID_PARAMETER;
            } else {
                if (Flags&PROCESS_DEBUG_INHERIT) {
                    PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
                } else {
                    PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
                }
            }
        }

        ObDereferenceObject (Process);

        return st;
    }

    case ProcessDeviceMap:
        DeviceMapInfo = (PPROCESS_DEVICEMAP_INFORMATION)ProcessInformation;
        if (ProcessInformationLength != sizeof (DeviceMapInfo->Set)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            DirectoryHandle = DeviceMapInfo->Set.DirectoryHandle;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        //
        // The devmap fields here are synchronized using a private ob spinlock. We don't need to protect with a
        // lock at this level.
        //
        st = ObSetDeviceMap (Process, DirectoryHandle);

        ObDereferenceObject (Process);
        return st;

    case ProcessSessionInformation :

        //
        // Update Multi-User session specific process information
        //
        if (ProcessInformationLength != (ULONG) sizeof (PROCESS_SESSION_INFORMATION)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            SessionInfo = *(PPROCESS_SESSION_INFORMATION) ProcessInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        //
        // We only allow TCB to set SessionId's
        //
        if (!SeSinglePrivilegeCheck (SeTcbPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        //
        // Reference process object
        //
        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION | PROCESS_SET_SESSIONID,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Update SessionId in the Token
        //
        if (SessionInfo.SessionId != MmGetSessionId (Process)) {
            st = STATUS_ACCESS_DENIED;
        } else {
            st = STATUS_SUCCESS;
        }

        ObDereferenceObject (Process);

        return( st );

    case ProcessBreakOnTermination:

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {

            EnableBreakOnTermination = *(PULONG)ProcessInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (!SeSinglePrivilegeCheck (SeDebugPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if ( EnableBreakOnTermination ) {

            PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_BREAK_ON_TERMINATION);

        } else {

            PS_CLEAR_BITS (&Process->Flags, PS_PROCESS_FLAGS_BREAK_ON_TERMINATION);

        }

        ObDereferenceObject (Process);

        return STATUS_SUCCESS;

    case ProcessHandleTracing: {

        PPROCESS_HANDLE_TRACING_ENABLE_EX Pht;
        PHANDLE_TABLE HandleTable;
        ULONG Slots;

        Slots = 0;

        //
        // Zero length disables otherwise we enable
        //
        if (ProcessInformationLength != 0) {
            if (ProcessInformationLength != sizeof (PROCESS_HANDLE_TRACING_ENABLE) &&
                ProcessInformationLength != sizeof (PROCESS_HANDLE_TRACING_ENABLE_EX)) {
                return STATUS_INFO_LENGTH_MISMATCH;
            }

            Pht = (PPROCESS_HANDLE_TRACING_ENABLE_EX) ProcessInformation;


            try {
                if (Pht->Flags != 0) {
                    return STATUS_INVALID_PARAMETER;
                }
                if (ProcessInformationLength == sizeof (PROCESS_HANDLE_TRACING_ENABLE_EX)) {
                    Slots = Pht->TotalSlots;
                }

            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode ();
            }
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_SET_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }
        HandleTable = ObReferenceProcessHandleTable (Process);

        if (HandleTable != NULL) {
            if (ProcessInformationLength != 0) {
                st = ExEnableHandleTracing (HandleTable, Slots);
            } else {
                st = ExDisableHandleTracing (HandleTable);
            }
            ObDereferenceProcessHandleTable (Process);
        } else {
            st = STATUS_PROCESS_IS_TERMINATING;
        }

        ObDereferenceObject(Process);
        return st;
    }


    default:
        return STATUS_INVALID_INFO_CLASS;
    }

}


NTSTATUS
NtQueryInformationThread(
    IN HANDLE ThreadHandle,
    IN THREADINFOCLASS ThreadInformationClass,
    OUT PVOID ThreadInformation,
    IN ULONG ThreadInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    )

/*++

Routine Description:

    This function queries the state of a thread object and returns the
    requested information in the specified record structure.

Arguments:

    ThreadHandle - Supplies a handle to a thread object.

    ThreadInformationClass - Supplies the class of information being
        requested.

    ThreadInformation - Supplies a pointer to a record that is to
        receive the requested information.

    ThreadInformationLength - Supplies the length of the record that is
        to receive the requested information.

    ReturnLength - Supplies an optional pointer to a variable that is to
        receive the actual length of information that is returned.

Return Value:

    TBS

--*/

{

    LARGE_INTEGER PerformanceCount;
    PETHREAD Thread;
    PEPROCESS Process;
    ULONG LastThread;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS st;
    THREAD_BASIC_INFORMATION BasicInfo;
    KERNEL_USER_TIMES SysUserTime;
    PVOID Win32StartAddressValue;
    ULONG DisableBoost;
    ULONG IoPending ;
    ULONG BreakOnTerminationEnabled;
    PETHREAD CurrentThread;

    //
    // Get previous processor mode and probe output argument if necessary.
    //

    PAGED_CODE();

    CurrentThread = PsGetCurrentThread ();

    PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);

    if (PreviousMode != KernelMode) {
        try {
            //
            // Since these functions don't change any state thats not reversible
            // in the error paths we only probe the output buffer for write access.
            // This improves performance by not touching the buffer multiple times
            // And only writing the portions of the buffer that change.
            //

            ProbeForRead (ThreadInformation,
                          ThreadInformationLength,
                          sizeof(ULONG));

            if (ARGUMENT_PRESENT( ReturnLength)) {
                ProbeForWriteUlong (ReturnLength);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }
    }

    //
    // Check argument validity.
    //

    switch (ThreadInformationClass) {

    case ThreadBasicInformation:

        if (ThreadInformationLength != (ULONG) sizeof (THREAD_BASIC_INFORMATION)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (KeReadStateThread (&Thread->Tcb)) {
            BasicInfo.ExitStatus = Thread->ExitStatus;
        } else {
            BasicInfo.ExitStatus = STATUS_PENDING;
        }

        BasicInfo.TebBaseAddress = (PTEB) Thread->Tcb.Teb;
        BasicInfo.ClientId = Thread->Cid;
        BasicInfo.AffinityMask = Thread->Tcb.Affinity;
        BasicInfo.Priority = Thread->Tcb.Priority;
        BasicInfo.BasePriority = KeQueryBasePriorityThread (&Thread->Tcb);

        ObDereferenceObject (Thread);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PTHREAD_BASIC_INFORMATION) ThreadInformation = BasicInfo;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (THREAD_BASIC_INFORMATION);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ThreadTimes:

        if (ThreadInformationLength != (ULONG) sizeof (KERNEL_USER_TIMES)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        SysUserTime.KernelTime.QuadPart = UInt32x32To64(Thread->Tcb.KernelTime,
                                                        KeMaximumIncrement);

        SysUserTime.UserTime.QuadPart = UInt32x32To64(Thread->Tcb.UserTime,
                                                      KeMaximumIncrement);

        SysUserTime.CreateTime.QuadPart = PS_GET_THREAD_CREATE_TIME(Thread);
        if (KeReadStateThread(&Thread->Tcb)) {
            SysUserTime.ExitTime = Thread->ExitTime;
        } else {
            SysUserTime.ExitTime.QuadPart = 0;
        }

        ObDereferenceObject (Thread);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PKERNEL_USER_TIMES) ThreadInformation = SysUserTime;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (KERNEL_USER_TIMES);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ThreadDescriptorTableEntry :

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = PspQueryDescriptorThread (Thread,
                                       ThreadInformation,
                                       ThreadInformationLength,
                                       ReturnLength);

        ObDereferenceObject (Thread);

        return st;

    case ThreadQuerySetWin32StartAddress:
        if (ThreadInformationLength != sizeof (ULONG_PTR)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Win32StartAddressValue = Thread->Win32StartAddress;
        ObDereferenceObject (Thread);

        try {
            *(PVOID *) ThreadInformation = Win32StartAddressValue;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (ULONG_PTR);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return st;

        //
        // Query thread cycle counter.
        //

    case ThreadPerformanceCount:
        if (ThreadInformationLength != sizeof (LARGE_INTEGER)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

#if defined (PERF_DATA)
        PerformanceCount.LowPart = Thread->PerformanceCountLow;
        PerformanceCount.HighPart = Thread->PerformanceCountHigh;
#else
        PerformanceCount.QuadPart = 0;
#endif
        ObDereferenceObject(Thread);

        try {
            *(PLARGE_INTEGER)ThreadInformation = PerformanceCount;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (LARGE_INTEGER);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return st;

    case ThreadAmILastThread:
        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        Process = THREAD_TO_PROCESS (CurrentThread);

        if (Process->ActiveThreads == 1) {
            LastThread = 1;
        } else {
            LastThread = 0;
        }

        try {
            *(PULONG)ThreadInformation = LastThread;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    case ThreadPriorityBoost:
        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        DisableBoost = Thread->Tcb.DisableBoost ? 1 : 0;

        ObDereferenceObject (Thread);

        try {
            *(PULONG)ThreadInformation = DisableBoost;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return st;

    case ThreadIsIoPending:

        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Its impossible to synchronize this cross thread.
        // Since the result is worthless the second its fetched
        // this isn't a problem.
        //
        IoPending = !IsListEmpty (&Thread->IrpList);


        ObDereferenceObject (Thread);

        try {
            *(PULONG)ThreadInformation = IoPending ;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof (ULONG);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }

        return STATUS_SUCCESS ;

    case ThreadBreakOnTermination:

        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_QUERY_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION) {

            BreakOnTerminationEnabled = 1;

        } else {

            BreakOnTerminationEnabled = 0;

        }

        ObDereferenceObject(Thread);

        try {

            *(PULONG) ThreadInformation = BreakOnTerminationEnabled;

            if (ARGUMENT_PRESENT(ReturnLength) ) {
                *ReturnLength = sizeof(ULONG);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        return STATUS_SUCCESS;

    default:
        return STATUS_INVALID_INFO_CLASS;
    }

}

NTSTATUS
NtSetInformationThread(
    IN HANDLE ThreadHandle,
    IN THREADINFOCLASS ThreadInformationClass,
    IN PVOID ThreadInformation,
    IN ULONG ThreadInformationLength
    )

/*++

Routine Description:

    This function sets the state of a thread object.

Arguments:

    ThreadHandle - Supplies a handle to a thread object.

    ThreadInformationClass - Supplies the class of information being
        set.

    ThreadInformation - Supplies a pointer to a record that contains the
        information to set.

    ThreadInformationLength - Supplies the length of the record that contains
        the information to set.

Return Value:

    TBS

--*/

{
    PETHREAD Thread;
    PETHREAD CurrentThread;
    PEPROCESS Process;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS st;
    KAFFINITY Affinity, AffinityWithMasks;
    KPRIORITY Priority;
    LONG BasePriority;
    ULONG TlsIndex;
    PVOID TlsArrayAddress;
    PVOID Win32StartAddressValue;
    ULONG ProbeAlignment;
    BOOLEAN EnableAlignmentFaultFixup;
    ULONG EnableBreakOnTermination;
    ULONG IdealProcessor;
    ULONG DisableBoost;
    PVOID *ExpansionSlots;
    HANDLE ImpersonationTokenHandle;
    BOOLEAN HasPrivilege;
    PEJOB Job;
    PTEB Teb;

    PAGED_CODE();

    //
    // Get previous processor mode and probe input argument if necessary.
    //

    CurrentThread = PsGetCurrentThread ();

    PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);

    if (PreviousMode != KernelMode) {
        try {

            switch (ThreadInformationClass) {

            case ThreadPriority :
                ProbeAlignment = sizeof(KPRIORITY);
                break;
            case ThreadAffinityMask :
            case ThreadQuerySetWin32StartAddress :
                ProbeAlignment = sizeof (ULONG_PTR);
                break;
            case ThreadEnableAlignmentFaultFixup :
                ProbeAlignment = sizeof (BOOLEAN);
                break;
            default :
                ProbeAlignment = sizeof(ULONG);
            }

            ProbeForRead(
                ThreadInformation,
                ThreadInformationLength,
                ProbeAlignment);

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }
    }

    //
    // Check argument validity.
    //

    switch (ThreadInformationClass) {

    case ThreadPriority:

        if (ThreadInformationLength != sizeof (KPRIORITY)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            Priority = *(KPRIORITY *)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (Priority > HIGH_PRIORITY ||
            Priority <= LOW_PRIORITY) {

            return STATUS_INVALID_PARAMETER;
        }

        if (Priority >= LOW_REALTIME_PRIORITY) {

            //
            // Increasing the priority of a thread beyond
            // LOW_REALTIME_PRIORITY is a privileged operation.
            //

            HasPrivilege = SeCheckPrivilegedObject (SeIncreaseBasePriorityPrivilege,
                                                    ThreadHandle,
                                                    THREAD_SET_INFORMATION,
                                                    PreviousMode);

            if (!HasPrivilege) {
                return STATUS_PRIVILEGE_NOT_HELD;
            }
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        KeSetPriorityThread (&Thread->Tcb, Priority);

        ObDereferenceObject (Thread);

        return STATUS_SUCCESS;

    case ThreadBasePriority:

        if (ThreadInformationLength != sizeof (LONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            BasePriority = *(PLONG)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }
        Process = THREAD_TO_PROCESS (Thread);


        if (BasePriority > THREAD_BASE_PRIORITY_MAX ||
            BasePriority < THREAD_BASE_PRIORITY_MIN) {
            if (BasePriority == THREAD_BASE_PRIORITY_LOWRT+1 ||
                BasePriority == THREAD_BASE_PRIORITY_IDLE-1) {
                ;
            } else {

                //
                // Allow csrss, or realtime processes to select any
                // priority
                //

                if (PsGetCurrentProcessByThread (CurrentThread) == ExpDefaultErrorPortProcess ||
                    Process->PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME) {
                    ;
                } else {
                    ObDereferenceObject (Thread);
                    return STATUS_INVALID_PARAMETER;
                }
            }
        }

        //
        // If the thread is running within a job object, and the job
        // object has a priority class limit, do not allow
        // priority adjustments that raise the thread's priority, unless
        // the priority class is realtime
        //

        Job = Process->Job;
        if (Job != NULL && (Job->LimitFlags & JOB_OBJECT_LIMIT_PRIORITY_CLASS)) {
            if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_REALTIME){
                if (BasePriority > 0) {
                    ObDereferenceObject (Thread);
                    return STATUS_SUCCESS;
                }
            }
        }

        KeSetBasePriorityThread (&Thread->Tcb, BasePriority);

        ObDereferenceObject (Thread);

        return STATUS_SUCCESS;

    case ThreadEnableAlignmentFaultFixup:

        if (ThreadInformationLength != sizeof (BOOLEAN) ) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            EnableAlignmentFaultFixup = *(PBOOLEAN)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        KeSetAutoAlignmentThread (&Thread->Tcb, EnableAlignmentFaultFixup);

        ObDereferenceObject (Thread);

        return STATUS_SUCCESS;

    case ThreadAffinityMask:

        if (ThreadInformationLength != sizeof (KAFFINITY)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            Affinity = *(PKAFFINITY)ThreadInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (!Affinity) {
            return STATUS_INVALID_PARAMETER;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Process = THREAD_TO_PROCESS (Thread);

        if (ExAcquireRundownProtection (&Process->RundownProtect)) {

            PspLockProcessShared (Process, CurrentThread);

            AffinityWithMasks = Affinity & Process->Pcb.Affinity;
            if (AffinityWithMasks != Affinity) {
                st = STATUS_INVALID_PARAMETER;
            } else {
                KeSetAffinityThread (&Thread->Tcb,
                                     AffinityWithMasks);
                st = STATUS_SUCCESS;
            }

            PspUnlockProcessShared (Process, CurrentThread);

            ExReleaseRundownProtection (&Process->RundownProtect);
        } else {
            st = STATUS_PROCESS_IS_TERMINATING;
        }

        ObDereferenceObject (Thread);

        return st;

    case ThreadImpersonationToken:


        if (ThreadInformationLength != sizeof (HANDLE)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }


        try {
            ImpersonationTokenHandle = *(PHANDLE) ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }


        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_THREAD_TOKEN,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // Check for proper access to (and type of) the token, and assign
        // it as the thread's impersonation token.
        //

        st = PsAssignImpersonationToken (Thread, ImpersonationTokenHandle);


        ObDereferenceObject (Thread);

        return st;

    case ThreadQuerySetWin32StartAddress:
        if (ThreadInformationLength != sizeof (ULONG_PTR)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }


        try {
            Win32StartAddressValue = *(PVOID *) ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }


        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Thread->Win32StartAddress = (PVOID)Win32StartAddressValue;

        ObDereferenceObject (Thread);

        return st;


    case ThreadIdealProcessor:

        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }


        try {
            IdealProcessor = *(PULONG)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        if (IdealProcessor > MAXIMUM_PROCESSORS) {
            return STATUS_INVALID_PARAMETER;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);
        if (!NT_SUCCESS (st)) {
            return st;
        }

        //
        // this is sort of a slimey way of returning info from this set only
        // api
        //

        st = (NTSTATUS)KeSetIdealProcessorThread (&Thread->Tcb, (CCHAR)IdealProcessor);

        //
        // We could be making cross process and/or cross thread references here.
        // Acquire rundown protection to make sure the teb can't go away.
        //
        Teb = Thread->Tcb.Teb;
        if (Teb != NULL && ExAcquireRundownProtection (&Thread->RundownProtect)) {
            PEPROCESS TargetProcess;
            BOOLEAN Attached;
            KAPC_STATE ApcState;

            Attached = FALSE;
            //
            // See if we are crossing process boundaries and if so attach to the target
            //
            TargetProcess = THREAD_TO_PROCESS (Thread);
            if (TargetProcess != PsGetCurrentProcessByThread (CurrentThread)) {
                KeStackAttachProcess (&TargetProcess->Pcb, &ApcState);
                Attached = TRUE;
            }

            try {

                Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
            } except (EXCEPTION_EXECUTE_HANDLER) {
            }

            if (Attached) {
                KeUnstackDetachProcess (&ApcState);
            }

            ExReleaseRundownProtection (&Thread->RundownProtect);


        }


        ObDereferenceObject (Thread);

        return st;


    case ThreadPriorityBoost:
        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            DisableBoost = *(PULONG)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        KeSetDisableBoostThread (&Thread->Tcb,DisableBoost ? TRUE : FALSE);

        ObDereferenceObject (Thread);

        return st;

    case ThreadZeroTlsCell:
        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {
            TlsIndex = *(PULONG) ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        ObDereferenceObject (Thread);

        if (Thread != CurrentThread) {
            return STATUS_INVALID_PARAMETER;
        }

        Process = THREAD_TO_PROCESS (Thread);


        // The 32bit TEB needs to be set if this is a WOW64 process on a 64BIT system.
        // This code isn't 100% correct since threads have a conversion state where they
        // are chaning from 64 to 32 and they don't have a TEB32 yet.  Fortunatly, the slots
        // will be zero when the thread is created so no damage is done by not clearing it here.

        // Note that the test for the process type is inside the inner loop. This
        // is bad programming, but this function is hardly time constrained and
        // fixing this with complex macros would not be worth it due to the loss of clairity.

        for (Thread = PsGetNextProcessThread (Process, NULL);
             Thread != NULL;
             Thread = PsGetNextProcessThread (Process, Thread)) {

            //
            // We are doing cross thread TEB references and need to prevent TEB deletion.
            //
            if (ExAcquireRundownProtection (&Thread->RundownProtect)) {
                Teb = Thread->Tcb.Teb;
                if (Teb != NULL) {
                    try {
#if defined(_WIN64)
                        PTEB32 Teb32 = NULL;
                        PLONG ExpansionSlots32;

                        if (Process->Wow64Process) { //wow64 process
                            Teb32 = WOW64_GET_TEB32(Teb);  //No probing needed on regular TEB.
                        }
#endif
                        if (TlsIndex > TLS_MINIMUM_AVAILABLE-1) {
                            if ( TlsIndex < (TLS_MINIMUM_AVAILABLE+TLS_EXPANSION_SLOTS) - 1 ) {
                                //
                                // This is an expansion slot, so see if the thread
                                // has an expansion cell
                                //
#if defined(_WIN64)
                                if (Process->Wow64Process) { //Wow64 process.
                                    if (Teb32) {
                                        ExpansionSlots32 = ULongToPtr(ProbeAndReadUlong(&(Teb32->TlsExpansionSlots)));
                                        if (ExpansionSlots32) {
                                            ProbeAndWriteLong(ExpansionSlots32 + TlsIndex - TLS_MINIMUM_AVAILABLE, 0);
                                        }
                                    }
                                } else {
#endif
                                    ExpansionSlots = Teb->TlsExpansionSlots;
                                    ProbeForReadSmallStructure (ExpansionSlots, TLS_EXPANSION_SLOTS*4, 8);
                                    if ( ExpansionSlots ) {
                                        ExpansionSlots[TlsIndex-TLS_MINIMUM_AVAILABLE] = 0;
                                    }

#if defined(_WIN64)
                                }
#endif
                            }
                        } else {
#if defined(_WIN64)
                            if (Process->Wow64Process) { //wow64 process
                               if(Teb32) {
                                  ProbeAndWriteUlong(Teb32->TlsSlots + TlsIndex, 0);
                               }
                            } else {
#endif
                               Teb->TlsSlots[TlsIndex] = NULL;
#if defined(_WIN64)
                            }
#endif
                        }
                    } except (EXCEPTION_EXECUTE_HANDLER) {
                        st = GetExceptionCode ();
                    }

                }
                ExReleaseRundownProtection (&Thread->RundownProtect);
            }
        }

        return st;
        break;

    case ThreadSetTlsArrayAddress:
        if (ThreadInformationLength != sizeof (PVOID)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }


        try {
            TlsArrayAddress = *(PVOID *)ThreadInformation;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        Thread->Tcb.TlsArray = TlsArrayAddress;

        ObDereferenceObject (Thread);

        return st;
        break;

    case ThreadHideFromDebugger:
        if (ThreadInformationLength != 0) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HIDEFROMDBG);

        ObDereferenceObject (Thread);

        return st;
        break;

    case ThreadBreakOnTermination:

        if (ThreadInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        try {

            EnableBreakOnTermination = *(PULONG)ThreadInformation;

        } except (EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode();
        }

        if (!SeSinglePrivilegeCheck (SeDebugPrivilege, PreviousMode)) {
            return STATUS_PRIVILEGE_NOT_HELD;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        if (EnableBreakOnTermination) {

            PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION);

        } else {

            PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION);

        }

        ObDereferenceObject (Thread);

        return STATUS_SUCCESS;

    default:
        return STATUS_INVALID_INFO_CLASS;
    }
}

ULONG
NtGetCurrentProcessorNumber(
    VOID
    )
{
    return KeGetCurrentProcessorNumber();
}

VOID
PsWatchWorkingSet(
    IN NTSTATUS Status,
    IN PVOID PcValue,
    IN PVOID Va
    )

/*++

Routine Description:

    This function collects data about page faults and stores information
    about the page fault in the current process's data structure.

Arguments:

    Status - Supplies the success completion status.

    PcValue - Supplies the instruction address that caused the page fault.

    Va - Supplies the virtual address that caused the page fault.

--*/

{

    PEPROCESS Process;
    PPAGEFAULT_HISTORY WorkingSetCatcher;
    KIRQL OldIrql;
    BOOLEAN TransitionFault = FALSE;

    //
    // Both transition and demand zero faults count as soft faults. Only disk
    // reads count as hard faults.
    //

    if ( Status <= STATUS_PAGE_FAULT_DEMAND_ZERO ) {
        TransitionFault = TRUE;
    }

    Process = PsGetCurrentProcess();
    WorkingSetCatcher = Process->WorkingSetWatch;
    if (WorkingSetCatcher == NULL) {
        return;
    }

    ExAcquireSpinLock(&WorkingSetCatcher->SpinLock,&OldIrql);
    if (WorkingSetCatcher->CurrentIndex >= WorkingSetCatcher->MaxIndex) {
        ExReleaseSpinLock(&WorkingSetCatcher->SpinLock,OldIrql);
        return;
    }

    //
    // Store the Pc and Va values in the buffer. Use the least sig. bit
    // of the Va to store whether it was a soft or hard fault
    //

    WorkingSetCatcher->WatchInfo[WorkingSetCatcher->CurrentIndex].FaultingPc = PcValue;
    WorkingSetCatcher->WatchInfo[WorkingSetCatcher->CurrentIndex].FaultingVa = TransitionFault ? (PVOID)((ULONG_PTR)Va | 1) : (PVOID)((ULONG_PTR)Va & 0xfffffffe) ;
    WorkingSetCatcher->CurrentIndex++;
    ExReleaseSpinLock(&WorkingSetCatcher->SpinLock,OldIrql);
    return;
}

#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif

PKWIN32_PROCESS_CALLOUT PspW32ProcessCallout = NULL;
PKWIN32_THREAD_CALLOUT PspW32ThreadCallout = NULL;
PKWIN32_JOB_CALLOUT PspW32JobCallout = NULL;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif
extern PKWIN32_POWEREVENT_CALLOUT PopEventCallout;
extern PKWIN32_POWERSTATE_CALLOUT PopStateCallout;



NTKERNELAPI
VOID
PsEstablishWin32Callouts(
   IN PKWIN32_CALLOUTS_FPNS pWin32Callouts )

/*++

Routine Description:

    This function is used by the Win32 kernel mode component to
    register callout functions for process/thread init/deinit functions
    and to report the sizes of the structures.

Arguments:

    ProcessCallout - Supplies the address of the function to be called when
        a process is either created or deleted.

    ThreadCallout - Supplies the address of the function to be called when
        a thread is either created or deleted.

    GlobalAtomTableCallout - Supplies the address of the function to be called
        to get the correct global atom table for the current process

    PowerEventCallout - Supplies the address of a function to be called when
        a power event occurs.

    PowerStateCallout - Supplies the address of a function to be called when
        the power state changes.

    JobCallout - Supplies the address of a function to be called when
        the job state changes or a process is assigned to a job.

    BatchFlushRoutine - Supplies the address of the function to be called

Return Value:

    None.

--*/

{
    PAGED_CODE();

    PspW32ProcessCallout = pWin32Callouts->ProcessCallout;
    PspW32ThreadCallout = pWin32Callouts->ThreadCallout;
    ExGlobalAtomTableCallout = pWin32Callouts->GlobalAtomTableCallout;
    KeGdiFlushUserBatch = (PGDI_BATCHFLUSH_ROUTINE)pWin32Callouts->BatchFlushRoutine;
    PopEventCallout = pWin32Callouts->PowerEventCallout;
    PopStateCallout = pWin32Callouts->PowerStateCallout;
    PspW32JobCallout = pWin32Callouts->JobCallout;
//    PoSetSystemState(ES_SYSTEM_REQUIRED);


    ExDesktopOpenProcedureCallout = pWin32Callouts->DesktopOpenProcedure;
    ExDesktopOkToCloseProcedureCallout = pWin32Callouts->DesktopOkToCloseProcedure;
    ExDesktopCloseProcedureCallout = pWin32Callouts->DesktopCloseProcedure;
    ExDesktopDeleteProcedureCallout = pWin32Callouts->DesktopDeleteProcedure;
    ExWindowStationOkToCloseProcedureCallout = pWin32Callouts->WindowStationOkToCloseProcedure;
    ExWindowStationCloseProcedureCallout = pWin32Callouts->WindowStationCloseProcedure;
    ExWindowStationDeleteProcedureCallout = pWin32Callouts->WindowStationDeleteProcedure;
    ExWindowStationParseProcedureCallout = pWin32Callouts->WindowStationParseProcedure;
    ExWindowStationOpenProcedureCallout = pWin32Callouts->WindowStationOpenProcedure;

}


VOID
PsSetProcessPriorityByClass(
    IN PEPROCESS Process,
    IN PSPROCESSPRIORITYMODE PriorityMode
    )
{
    KPRIORITY BasePriority;
    UCHAR MemoryPriority;
    ULONG QuantumIndex;
    PEJOB Job;

    PAGED_CODE();


    BasePriority = PspPriorityTable[Process->PriorityClass];


    if (PriorityMode == PsProcessPriorityForeground ) {
        QuantumIndex = PsPrioritySeperation;
        MemoryPriority = MEMORY_PRIORITY_FOREGROUND;
    } else {
        QuantumIndex = 0;
        MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
    }

    if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE) {
        Job = Process->Job;
        if (Job != NULL && PspUseJobSchedulingClasses ) {
            Process->Pcb.ThreadQuantum = PspJobSchedulingClasses[Job->SchedulingClass];
        } else {
            Process->Pcb.ThreadQuantum = PspForegroundQuantum[QuantumIndex];
        }
    } else {
        Process->Pcb.ThreadQuantum = THREAD_QUANTUM;
    }

    KeSetPriorityProcess (&Process->Pcb,BasePriority);
    if (PriorityMode != PsProcessPrioritySpinning ) {
        MmSetMemoryPriorityProcess(Process, MemoryPriority);
    }
}



#if defined(_X86_)
#pragma optimize ("y",off)
#endif

NTSTATUS
PsConvertToGuiThread(
    VOID
    )

/*++

Routine Description:

    This function converts a thread to a GUI thread. This involves giving the
    thread a larger variable sized stack, and allocating appropriate w32
    thread and process objects.

Arguments:

    None.

Environment:

    On x86 this function needs to build an EBP frame.  The function
    KeSwitchKernelStack depends on this fact.   The '#pragma optimize
    ("y",off)' below disables frame pointer omission for all builds.

Return Value:

    TBD

--*/

{
    PVOID NewStack;
    PVOID OldStack;
    PETHREAD Thread;
    PEPROCESS Process;
    NTSTATUS Status;
    PKNODE Node;

    PAGED_CODE();

    Thread = PsGetCurrentThread();

    if (KeGetPreviousModeByThread(&Thread->Tcb) == KernelMode) {
        return STATUS_INVALID_PARAMETER;
    }

    if (!PspW32ProcessCallout) {
        return STATUS_ACCESS_DENIED;
    }

    //
    // If the thread is using the shadow service table, then an attempt is
    // being made to convert a thread that has already been converted, or
    // a limit violation has occured on the Win32k system service table.
    //

    if (Thread->Tcb.ServiceTable != (PVOID)&KeServiceDescriptorTable[0]) {
        return STATUS_ALREADY_WIN32;
    }

    Process = PsGetCurrentProcessByThread (Thread);

    //
    // Get a larger kernel stack if we haven't already.
    //

    if (!Thread->Tcb.LargeStack) {

        Node = KiProcessorBlock[Thread->Tcb.IdealProcessor]->ParentNode;
        NewStack = MmCreateKernelStack(TRUE,
                                       Node->NodeNumber);

        if ( !NewStack ) {

            try {
                NtCurrentTeb()->LastErrorValue = (LONG)ERROR_NOT_ENOUGH_MEMORY;
            } except (EXCEPTION_EXECUTE_HANDLER) {
            }

            return STATUS_NO_MEMORY;
        }

        //
        // Switching kernel stacks will copy the base trap frame. This needs
        // to be protected from context changes by disabline kernel APC's.
        //

        KeEnterGuardedRegionThread (&Thread->Tcb);

#if defined(_IA64_)
        OldStack = KeSwitchKernelStack(NewStack,
                                   (UCHAR *)NewStack - KERNEL_LARGE_STACK_COMMIT,
                                   (UCHAR *)NewStack + KERNEL_LARGE_BSTORE_COMMIT);
#else
        OldStack = KeSwitchKernelStack(NewStack,
                                   (UCHAR *)NewStack - KERNEL_LARGE_STACK_COMMIT);
#endif // defined(_IA64_)

        KeLeaveGuardedRegionThread (&Thread->Tcb);

        MmDeleteKernelStack(OldStack, FALSE);

    }

    PERFINFO_CONVERT_TO_GUI_THREAD(Thread);

    //
    // We are all clean on the stack, now call out and then link the Win32 structures
    // to the base exec structures
    //

    Status = (PspW32ProcessCallout) (Process, TRUE);

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    //
    // Switch the thread to use the shadow system serive table which will
    // enable it to execute Win32k services.
    //

    Thread->Tcb.ServiceTable = (PVOID)&KeServiceDescriptorTableShadow[0];

    ASSERT (Thread->Tcb.Win32Thread == 0);


    //
    // Make the thread callout.
    //

    Status = (PspW32ThreadCallout)(Thread,PsW32ThreadCalloutInitialize);
    if (!NT_SUCCESS (Status)) {
        Thread->Tcb.ServiceTable = (PVOID)&KeServiceDescriptorTable[0];
    }

    return Status;

}

#if defined(_X86_)
#pragma optimize ("y",on)
#endif

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif

