/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    exatom.c

Abstract:

    This file contains functions for manipulating global atom tables
    stored in kernel space.

Author:

    Steve Wood (stevewo) 13-Dec-1995

Revision History:

--*/

#include "exp.h"
#pragma hdrstop

//
//  Local Procedure prototype
//

PVOID
ExpGetGlobalAtomTable (
    );

#if defined(ALLOC_PRAGMA)
#pragma alloc_text(PAGE, NtAddAtom)
#pragma alloc_text(PAGE, NtFindAtom)
#pragma alloc_text(PAGE, NtDeleteAtom)
#pragma alloc_text(PAGE, NtQueryInformationAtom)
#pragma alloc_text(PAGE, ExpGetGlobalAtomTable)
#endif

#define COPY_STACK_SIZE         128


NTSYSAPI
NTSTATUS
NTAPI
NtAddAtom (
    IN PWSTR AtomName,
    IN ULONG Length,
    OUT PRTL_ATOM Atom OPTIONAL
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    NTSTATUS Status;
    RTL_ATOM ReturnAtom;
    PVOID AtomTable = ExpGetGlobalAtomTable();
    KPROCESSOR_MODE PreviousMode;
    PWSTR CapturedAtomNameBuffer;
    ULONG AllocLength;
    UCHAR StackArray[COPY_STACK_SIZE];

    PAGED_CODE();

    if (AtomTable == NULL) {

        return STATUS_ACCESS_DENIED;
    }

    if (Length > (RTL_ATOM_MAXIMUM_NAME_LENGTH * sizeof(WCHAR))) {

        return STATUS_INVALID_PARAMETER;
    }

    PreviousMode = KeGetPreviousMode();
    CapturedAtomNameBuffer = AtomName;

    Status = STATUS_SUCCESS;

    if (PreviousMode != KernelMode) {

        if (ARGUMENT_PRESENT (Atom)) {
            try {
                ProbeForWriteUshort( Atom );
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode();
            }
        }

        if (ARGUMENT_PRESENT (AtomName)) {

            AllocLength = (Length + sizeof( UNICODE_NULL ))&~(sizeof (WCHAR)-1);

            try {
                ProbeForRead( AtomName, Length, sizeof( WCHAR ) );
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode();
            }

            if (AllocLength <= COPY_STACK_SIZE) {
                CapturedAtomNameBuffer = (PWSTR) StackArray;
            }
            else {
                CapturedAtomNameBuffer = ExAllocatePoolWithTag (PagedPool, AllocLength, 'motA');

                if (CapturedAtomNameBuffer == NULL) {
                    return STATUS_INSUFFICIENT_RESOURCES;
                }
            }

            try {
                RtlCopyMemory( CapturedAtomNameBuffer, AtomName, Length );
            } except (EXCEPTION_EXECUTE_HANDLER) {
                if (CapturedAtomNameBuffer != (PWSTR) StackArray) {
                    ExFreePool (CapturedAtomNameBuffer);
                }
                return GetExceptionCode();
            }

            CapturedAtomNameBuffer[Length / sizeof (WCHAR)] = '\0';
        }
    }

    if (NT_SUCCESS (Status)) {

        Status = RtlAddAtomToAtomTable (AtomTable, CapturedAtomNameBuffer, &ReturnAtom);

        if ((ARGUMENT_PRESENT (Atom)) && (NT_SUCCESS (Status))) {

            if (PreviousMode != KernelMode) {
                try {

                    *Atom = ReturnAtom;

                } except (EXCEPTION_EXECUTE_HANDLER) {

                    Status = GetExceptionCode();
                }
            }
            else {
                *Atom = ReturnAtom;
            }
        }
    }

    if ((CapturedAtomNameBuffer != AtomName) &&
        (CapturedAtomNameBuffer != (PWSTR) StackArray)) {
        ExFreePool (CapturedAtomNameBuffer);
    }

    return Status;
}


NTSYSAPI
NTSTATUS
NTAPI
NtFindAtom (
    IN PWSTR AtomName,
    IN ULONG Length,
    OUT PRTL_ATOM Atom OPTIONAL
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    NTSTATUS Status;
    RTL_ATOM ReturnAtom;
    PVOID AtomTable = ExpGetGlobalAtomTable();
    KPROCESSOR_MODE PreviousMode;
    PWSTR CapturedAtomNameBuffer;
    ULONG AllocLength;
    UCHAR StackArray[COPY_STACK_SIZE];

    PAGED_CODE();

    if (AtomTable == NULL) {

        return STATUS_ACCESS_DENIED;
    }

    if (Length > (RTL_ATOM_MAXIMUM_NAME_LENGTH * sizeof(WCHAR))) {

        return STATUS_INVALID_PARAMETER;
    }

    PreviousMode = KeGetPreviousMode();
    CapturedAtomNameBuffer = AtomName;

    Status = STATUS_SUCCESS;

    if (PreviousMode != KernelMode) {

        if (ARGUMENT_PRESENT (Atom)) {
            try {
                ProbeForWriteUshort (Atom);
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode();
            }
        }

        if (ARGUMENT_PRESENT (AtomName)) {

            AllocLength = (Length + sizeof( UNICODE_NULL ))&~(sizeof (WCHAR)-1);

            try {
                ProbeForRead (AtomName, Length, sizeof (WCHAR));
            } except (EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode();
            }

            if (AllocLength <= COPY_STACK_SIZE) {
                CapturedAtomNameBuffer = (PWSTR) StackArray;
            }
            else {
                CapturedAtomNameBuffer = ExAllocatePoolWithTag (PagedPool, AllocLength, 'motA');

                if (CapturedAtomNameBuffer == NULL) {
                    return STATUS_INSUFFICIENT_RESOURCES;
                }
            }

            try {
                RtlCopyMemory (CapturedAtomNameBuffer, AtomName, Length);
            } except (EXCEPTION_EXECUTE_HANDLER) {
                if (CapturedAtomNameBuffer != (PWSTR) StackArray) {
                    ExFreePool (CapturedAtomNameBuffer);
                }
                return GetExceptionCode();
            }

            CapturedAtomNameBuffer[Length / sizeof (WCHAR)] = '\0';
        }
    }

    if (NT_SUCCESS( Status )) {

        Status = RtlLookupAtomInAtomTable (AtomTable, CapturedAtomNameBuffer, &ReturnAtom);

        if (NT_SUCCESS(Status) && ARGUMENT_PRESENT(Atom)) {

            try {

                *Atom = ReturnAtom;

            } except (EXCEPTION_EXECUTE_HANDLER) {

                Status = GetExceptionCode();
            }
        }
    }

    if ((CapturedAtomNameBuffer != AtomName) &&
        (CapturedAtomNameBuffer != (PWSTR) StackArray)) {
        ExFreePool (CapturedAtomNameBuffer);
    }

    return Status;
}


NTSYSAPI
NTSTATUS
NTAPI
NtDeleteAtom (
    IN RTL_ATOM Atom
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    NTSTATUS Status;
    PVOID AtomTable = ExpGetGlobalAtomTable();

    PAGED_CODE();

    if (AtomTable == NULL) {

        return STATUS_ACCESS_DENIED;
    }

    Status = RtlDeleteAtomFromAtomTable( AtomTable, Atom );

    return Status;
}


NTSYSAPI
NTSTATUS
NTAPI
NtQueryInformationAtom(
    IN RTL_ATOM Atom,
    IN ATOM_INFORMATION_CLASS AtomInformationClass,
    OUT PVOID AtomInformation,
    IN ULONG AtomInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    NTSTATUS Status;
    KPROCESSOR_MODE PreviousMode;
    ULONG RequiredLength;
    ULONG UsageCount;
    ULONG NameLength;
    ULONG AtomFlags;
    PATOM_BASIC_INFORMATION BasicInfo;
    PATOM_TABLE_INFORMATION TableInfo;
    PVOID AtomTable = ExpGetGlobalAtomTable();

    PAGED_CODE();

    if (AtomTable == NULL) {
        return STATUS_ACCESS_DENIED;
    }

    //
    //  Assume successful completion.
    //

    Status = STATUS_SUCCESS;

    try {

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

        PreviousMode = KeGetPreviousMode();

        if (PreviousMode != KernelMode) {

            ProbeForWrite( AtomInformation,
                           AtomInformationLength,
                           sizeof( ULONG ));

            if (ARGUMENT_PRESENT( ReturnLength )) {

                ProbeForWriteUlong( ReturnLength );
            }
        }

        RequiredLength = 0;

        switch (AtomInformationClass) {

        case AtomBasicInformation:

            RequiredLength = FIELD_OFFSET( ATOM_BASIC_INFORMATION, Name );

            if (AtomInformationLength < RequiredLength) {

                return STATUS_INFO_LENGTH_MISMATCH;
            }

            BasicInfo = (PATOM_BASIC_INFORMATION)AtomInformation;
            UsageCount = 0;
            NameLength = AtomInformationLength - RequiredLength;
            BasicInfo->Name[ 0 ] = UNICODE_NULL;

            Status = RtlQueryAtomInAtomTable( AtomTable,
                                              Atom,
                                              &UsageCount,
                                              &AtomFlags,
                                              &BasicInfo->Name[0],
                                              &NameLength );

            if (NT_SUCCESS(Status)) {

                BasicInfo->UsageCount = (USHORT)UsageCount;
                BasicInfo->Flags = (USHORT)AtomFlags;
                BasicInfo->NameLength = (USHORT)NameLength;
                RequiredLength += NameLength + sizeof( UNICODE_NULL );
            }

            break;

        case AtomTableInformation:

            RequiredLength = FIELD_OFFSET( ATOM_TABLE_INFORMATION, Atoms );

            if (AtomInformationLength < RequiredLength) {

                return STATUS_INFO_LENGTH_MISMATCH;
            }

            TableInfo = (PATOM_TABLE_INFORMATION)AtomInformation;

            Status = RtlQueryAtomsInAtomTable( AtomTable,
                                               (AtomInformationLength - RequiredLength) / sizeof( RTL_ATOM ),
                                               &TableInfo->NumberOfAtoms,
                                               &TableInfo->Atoms[0] );

            if (NT_SUCCESS(Status)) {

                RequiredLength += TableInfo->NumberOfAtoms * sizeof( RTL_ATOM );
            }

            break;

        default:

            Status = STATUS_INVALID_INFO_CLASS;

            break;
        }

        if (ARGUMENT_PRESENT( ReturnLength )) {

            *ReturnLength = RequiredLength;
        }

    } except (EXCEPTION_EXECUTE_HANDLER) {

        Status = GetExceptionCode();
    }

    return Status;
}


//
//  Local support routine
//

PKWIN32_GLOBALATOMTABLE_CALLOUT ExGlobalAtomTableCallout;

PVOID
ExpGetGlobalAtomTable (
    )

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    if (ExGlobalAtomTableCallout != NULL) {

        return ((*ExGlobalAtomTableCallout)());

    }

#if DBG
    DbgPrint( "EX: ExpGetGlobalAtomTable is about to return NULL!\n" );
    DbgBreakPoint();
#endif
    return NULL;
}
