/*++

Copyright (c) 1991-1996,  Microsoft Corporation  All rights reserved.

Module Name:

    section.c

Abstract:

    This file contains functions that deal with creating, opening, or
    mapping a section for data table files for the NLS API.

    External Routines found in this file:
      CreateNlsObjectDirectory
      OpenRegKey
      QueryRegValue
      CreateSectionFromReg
      CreateSectionOneValue
      CreateSection
      CreateSectionTemp
      OpenSection
      MapSection
      UnMapSection
      GetNlsSectionName
      GetScriptMemberWeights

Revision History:

    05-31-91    JulieB    Created.

--*/



//
//  Include Files.
//

#include "nls.h"




//
//  Forward Declarations.
//

ULONG
OpenDataFile(
    HANDLE *phFile,
    LPWSTR pFile);

ULONG
GetNTFileName(
    LPWSTR pFile,
    PUNICODE_STRING pFileName);

ULONG
CreateSecurityDescriptor(
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    PSID *ppWorldSid,
    ACCESS_MASK AccessMask);

ULONG
AppendAccessAllowedACE(
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    ACCESS_MASK AccessMask);





//-------------------------------------------------------------------------//
//                           INTERNAL MACROS                               //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  NLS_REG_BUFFER_ALLOC
//
//  Allocates the buffer used by the registry enumeration and query calls
//  and sets the pKeyValueFull variable to point at the newly created buffer.
//
//  NOTE: This macro may return if an error is encountered.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_REG_BUFFER_ALLOC( pKeyValueFull,                               \
                              BufSize,                                     \
                              pBuffer,                                     \
                              CritSect )                                   \
{                                                                          \
    if ((pBuffer = (PVOID)NLS_ALLOC_MEM(BufSize)) == NULL)                 \
    {                                                                      \
        KdPrint(("NLSAPI: Could NOT Allocate Memory.\n"));                 \
        if (CritSect)                                                      \
        {                                                                  \
            RtlLeaveCriticalSection(&gcsTblPtrs);                          \
        }                                                                  \
        return ((ULONG)STATUS_NO_MEMORY);                                  \
    }                                                                      \
                                                                           \
    pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pBuffer;                  \
}


////////////////////////////////////////////////////////////////////////////
//
//  NLS_REG_BUFFER_FREE
//
//  Frees the buffer used by the registry enumeration and query calls.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_REG_BUFFER_FREE(pBuffer)        (NLS_FREE_MEM(pBuffer))


////////////////////////////////////////////////////////////////////////////
//
//  NLS_INTEGER_TO_UNICODE_STR
//
//  Converts an integer value to a unicode string and stores it in the
//  buffer provided.
//
//  NOTE: This macro may return if an error is encountered.
//
//  DEFINED AS A MACRO.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define NLS_INTEGER_TO_UNICODE_STR( Value,                                 \
                                    Base,                                  \
                                    Padding,                               \
                                    pResultBuf,                            \
                                    Size )                                 \
{                                                                          \
    UNICODE_STRING ObString;           /* value string */                  \
    WCHAR pBuffer[Size];               /* ptr to buffer */                 \
    UINT LpCtr;                        /* loop counter */                  \
    ULONG rc = 0L;                     /* return code */                   \
    LPWSTR pBufPtr;                    /* ptr to result buffer */          \
                                                                           \
                                                                           \
    /*                                                                     \
     *  Set up unicode string structure.                                   \
     */                                                                    \
    ObString.Length = Size * 2;                                            \
    ObString.MaximumLength = Size * 2;                                     \
    ObString.Buffer = pBuffer;                                             \
                                                                           \
    /*                                                                     \
     *  Get the value as a string.                                         \
     */                                                                    \
    if (rc = RtlIntegerToUnicodeString(Value, Base, &ObString))            \
    {                                                                      \
        return (rc);                                                       \
    }                                                                      \
                                                                           \
    /*                                                                     \
     *  Pad the string with the appropriate number of zeros.               \
     */                                                                    \
    pBufPtr = pResultBuf;                                                  \
    for (LpCtr = GET_WC_COUNT(ObString.Length);                            \
         LpCtr < Padding;                                                  \
         LpCtr++, pBufPtr++)                                               \
    {                                                                      \
        *pBufPtr = (WCHAR)'0';                                             \
    }                                                                      \
    NlsStrCpyW(pBufPtr, ObString.Buffer);                                  \
}




//-------------------------------------------------------------------------//
//                           EXTERNAL ROUTINES                             //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  CreateNlsObjectDirectory
//
//  This routine creates the object directory for the NLS memory mapped
//  sections.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG CreateNlsObjectDirectory()
{
    ULONG pSecurityDescriptor[MAX_PATH_LEN]; // security descriptor buffer
    PSID pWorldSid;                          // ptr to world SID
    UNICODE_STRING ObDirName;                // directory name
    OBJECT_ATTRIBUTES ObjA;                  // object attributes structure
    HANDLE hDirHandle;                       // directory handle
    ULONG rc = 0L;                           // return code


    //
    //  Create the security descriptor.
    //
    if (rc = CreateSecurityDescriptor( pSecurityDescriptor,
                                       &pWorldSid,
                                       DIRECTORY_TRAVERSE |
                                       DIRECTORY_CREATE_OBJECT ))
    {
        NLS_FREE_MEM(pWorldSid);
        return (rc);
    }

    //
    //  Add Admin Access for Query.
    //
    if (rc = AppendAccessAllowedACE( pSecurityDescriptor,
                                     DIRECTORY_QUERY |
                                     DIRECTORY_TRAVERSE |
                                     DIRECTORY_CREATE_OBJECT ))
    {
        NLS_FREE_MEM(pWorldSid);
        return (rc);
    }

    //
    //  Create the object directory.
    //
    RtlInitUnicodeString(&ObDirName, NLS_OBJECT_DIRECTORY_NAME);
    InitializeObjectAttributes( &ObjA,
                                &ObDirName,
                                OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
                                NULL,
                                pSecurityDescriptor );

    rc = NtCreateDirectoryObject( &hDirHandle,
                                  DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT,
                                  &ObjA );

    //
    //  Free the memory used for the SID and close the directory handle.
    //
    NLS_FREE_MEM(pWorldSid);
    NtClose(hDirHandle);

    //
    //  Check for error from NtCreateDirectoryObject.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Create Object Directory %wZ - %lx.\n",
                 &ObDirName, rc));
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  OpenRegKey
//
//  This routine opens a key in the registry.
//
//  08-02-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG OpenRegKey(
    PHANDLE phKeyHandle,
    LPWSTR pBaseName,
    LPWSTR pKey,
    ULONG fAccess)
{
    WCHAR pwszKeyName[MAX_PATH_LEN];   // ptr to the full key name
    HANDLE UserKeyHandle;              // HKEY_CURRENT_USER equivalent
    OBJECT_ATTRIBUTES ObjA;            // object attributes structure
    UNICODE_STRING ObKeyName;          // key name
    ULONG rc = 0L;                     // return code


    //
    //  Get the full key name.
    //
    if (pBaseName == NULL)
    {
        //
        //  Get current user's key handle.
        //
        rc = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &UserKeyHandle);
        if (!NT_SUCCESS(rc))
        {
            KdPrint(("NLSAPI: Could NOT Open HKEY_CURRENT_USER - %lx.\n", rc));
            return (rc);
        }
        pwszKeyName[ 0 ] = UNICODE_NULL;
    }
    else
    {
        //
        //  Base name exists, so not current user.
        //
        UserKeyHandle = NULL;
        NlsStrCpyW(pwszKeyName, pBaseName);
    }
    NlsStrCatW(pwszKeyName, pKey);

    //
    //  Open the registry key.
    //
    RtlInitUnicodeString(&ObKeyName, pwszKeyName);
    InitializeObjectAttributes( &ObjA,
                                &ObKeyName,
                                OBJ_CASE_INSENSITIVE,
                                UserKeyHandle,
                                NULL );
    rc = NtOpenKey( phKeyHandle,
                    fAccess,
                    &ObjA );

    //
    //  Close the current user handle, if necessary.
    //
    if (UserKeyHandle != NULL)
    {
        NtClose(UserKeyHandle);
    }

    //
    //  Check for error from NtOpenKey.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Open Registry Key %wZ - %lx.\n",
                 &ObKeyName, rc));
        *phKeyHandle = NULL;
    }

    //
    //  Return the status from NtOpenKey.
    //
    return (rc);
}


////////////////////////////////////////////////////////////////////////////
//
//  QueryRegValue
//
//  This routine queries the given value from the registry.
//
//  NOTE: If pIfAlloc is NULL, then no buffer will be allocated.
//        If this routine is successful, the CALLER must free the
//        ppKeyValueFull information buffer if *pIfAlloc is TRUE.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG QueryRegValue(
    HANDLE hKeyHandle,
    LPWSTR pValue,
    PKEY_VALUE_FULL_INFORMATION *ppKeyValueFull,
    ULONG Length,
    LPBOOL pIfAlloc)
{
    UNICODE_STRING ObValueName;        // value name
    PVOID pBuffer;                     // ptr to buffer for enum
    ULONG ResultLength;                // # bytes written
    ULONG rc = 0L;                     // return code


    //
    //  Set contents of pIfAlloc to FALSE to show that we did NOT do a
    //  memory allocation (yet).
    //
    if (pIfAlloc)
    {
        *pIfAlloc = FALSE;
    }

    //
    //  Query the value from the registry.
    //
    RtlInitUnicodeString(&ObValueName, pValue);

    RtlZeroMemory(*ppKeyValueFull, Length);
    rc = NtQueryValueKey( hKeyHandle,
                          &ObValueName,
                          KeyValueFullInformation,
                          *ppKeyValueFull,
                          Length,
                          &ResultLength );

    //
    //  Check the error code.  If the buffer is too small, allocate
    //  a new one and try the query again.
    //
    if ((rc == STATUS_BUFFER_OVERFLOW) && (pIfAlloc))
    {
        //
        //  Buffer is too small, so allocate a new one.
        //
        NLS_REG_BUFFER_ALLOC(*ppKeyValueFull, ResultLength, pBuffer, FALSE);
        RtlZeroMemory(*ppKeyValueFull, ResultLength);
        rc = NtQueryValueKey( hKeyHandle,
                              &ObValueName,
                              KeyValueFullInformation,
                              *ppKeyValueFull,
                              ResultLength,
                              &ResultLength );

        //
        //  Set contents of pIfAlloc to TRUE to show that we DID do
        //  a memory allocation.
        //
        *pIfAlloc = TRUE;
    }

    //
    //  If there is an error at this point, then the query failed.
    //
    if (rc != NO_ERROR)
    {
        if ((pIfAlloc) && (*pIfAlloc))
        {
            NLS_REG_BUFFER_FREE(pBuffer);
        }
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  CreateSectionFromReg
//
//  This routine creates a named memory mapped section for the given full
//  information for the key value and returns the handle to the section.
//  The section name and the data file name are retrieved and formed from
//  information given in the key_value_full_information structure.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG CreateSectionFromReg(
    HANDLE *phSec,
    PKEY_VALUE_FULL_INFORMATION pKeyValueFull,
    LPWSTR pwszNlsPrefix)
{
    HANDLE hFile = (HANDLE)0;                // file handle
    ULONG pSecurityDescriptor[MAX_PATH_LEN]; // security descriptor buffer
    PSID pWorldSid;                          // ptr to world SID
    UNICODE_STRING ObSecName;                // section name
    OBJECT_ATTRIBUTES ObjA;                  // object attributes structure
    WCHAR pwszSecName[MAX_PATH_LEN];         // ptr to section name string
    ULONG rc = 0L;                           // return code

    BASE_API_MSG m;
    PBASE_NLS_PRESERVE_SECTION_MSG a = &m.u.NlsPreserveSection;


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  Open the data file.
    //
    if (rc = OpenDataFile( &hFile,
                           GET_VALUE_DATA_PTR(pKeyValueFull) ))
    {
        return (rc);
    }

    //
    //  Create the security descriptor.
    //
    if (rc = CreateSecurityDescriptor( pSecurityDescriptor,
                                       &pWorldSid,
                                       GENERIC_READ ))
    {
        NLS_FREE_MEM(pWorldSid);
        NtClose(hFile);
        return (rc);
    }

    //
    //  Create the section.
    //
    RtlZeroMemory(pwszSecName, MAX_PATH_LEN);
    NlsStrCpyW(pwszSecName, pwszNlsPrefix);
    NlsStrNCatW( pwszSecName,
                 (LPWSTR)pKeyValueFull->Name,
                 GET_WC_COUNT(pKeyValueFull->NameLength) );

    RtlInitUnicodeString(&ObSecName, pwszSecName);
    InitializeObjectAttributes( &ObjA,
                                &ObSecName,
                                OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
                                NULL,
                                pSecurityDescriptor );

    rc = NtCreateSection( phSec,
                          SECTION_MAP_READ,
                          &ObjA,
                          NULL,
                          PAGE_READONLY,
                          SEC_COMMIT,
                          hFile );

    //
    //  Free the memory used for the SID and close the file.
    //
    NLS_FREE_MEM(pWorldSid);
    NtClose(hFile);

    //
    //  Check for error from NtCreateSection.
    //
    if (!NT_SUCCESS(rc))
    {
        //
        //  If the name has already been created, ignore the error.
        //
        if (rc != STATUS_OBJECT_NAME_COLLISION)
        {
            KdPrint(("NLSAPI: Could NOT Create Section %wZ - %lx.\n",
                     &ObSecName, rc));
            return (rc);
        }
    }
    else
    {
        //
        //  Call the server to preserve the section handle.
        //  Don't bother checking the error return, because the
        //  section was successfully created for this process.
        //
        a->hSection = *phSec;

        CsrClientCallServer( (PCSR_API_MSG)&m,
                             NULL,
                             CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
                                                 BasepNlsPreserveSection),
                             sizeof(*a) );
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  CreateSectionOneValue
//
//  This routine creates a named memory mapped section for the given
//  value under the given key in the registry.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

#define MAX_BUF 20

ULONG CreateSectionOneValue(
    HANDLE hKeyHandle,
    UINT Value,
    UINT Base,
    UINT Padding,
    LPWSTR pNlsPrefix,
    PVOID *ppBaseAddr)
{
    WCHAR pTmpBuf[MAX_BUF];                      // temp buffer
    PKEY_VALUE_FULL_INFORMATION pKeyValueFull;   // ptr to query info
    BYTE pStatic[MAX_KEY_VALUE_FULLINFO];        // ptr to static buffer
    HANDLE hSec = (HANDLE)0;                     // section handle
    ULONG rc = 0L;                               // return code
    BOOL IfAlloc = FALSE;                        // if buffer was allocated


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  Convert value to unicode string.
    //
    NLS_INTEGER_TO_UNICODE_STR( Value,
                                Base,
                                Padding,
                                pTmpBuf,
                                MAX_BUF );

    //
    //  Query the registry for the value.
    //
    pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
    if (rc = QueryRegValue( hKeyHandle,
                            pTmpBuf,
                            &pKeyValueFull,
                            MAX_KEY_VALUE_FULLINFO,
                            &IfAlloc ))
    {
        return (rc);
    }

    //
    //  Make sure there is data with this value.
    //
    if (pKeyValueFull->DataLength <= 2)
    {
        if (IfAlloc)
        {
            NLS_FREE_MEM(pKeyValueFull);
        }
        return (1);
    }

    //
    //  Create the section.
    //
    if (rc = CreateSectionFromReg( &hSec,
                                   pKeyValueFull,
                                   pNlsPrefix ))
    {
        if (IfAlloc)
        {
            NLS_FREE_MEM(pKeyValueFull);
        }
        return (rc);
    }

    //
    //  Free the buffer used for the query.
    //
    if (IfAlloc)
    {
        NLS_FREE_MEM(pKeyValueFull);
    }

    //
    //  Map a View of the Section.
    //
    if (rc = MapSection( hSec,
                         ppBaseAddr,
                         PAGE_READONLY,
                         TRUE ))
    {
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  CreateSection
//
//  This routine creates a named memory mapped section for the given file
//  name and section name and returns the handle to the section.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG CreateSection(
    HANDLE *phSec,
    LPWSTR pwszFileName,
    LPWSTR pwszSecName)
{
    HANDLE hFile = (HANDLE)0;                // file handle
    ULONG pSecurityDescriptor[MAX_PATH_LEN]; // security descriptor buffer
    PSID pWorldSid;                          // ptr to world SID
    UNICODE_STRING ObSecName;                // section name
    OBJECT_ATTRIBUTES ObjA;                  // object attributes structure
    ULONG rc = 0L;                           // return code

    BASE_API_MSG m;
    PBASE_NLS_PRESERVE_SECTION_MSG a = &m.u.NlsPreserveSection;


    //
    //  Don't need to be in the critical section here, since the
    //  server init routine calls this.  All other calls made to
    //  this function should ensure that they are in a critical
    //  section.
    //
    //  ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);
    //

    //
    //  Open the data file.
    //
    if (rc = OpenDataFile( &hFile,
                           pwszFileName ))
    {
        return (rc);
    }

    //
    //  Create the security descriptor.
    //
    if (rc = CreateSecurityDescriptor( pSecurityDescriptor,
                                       &pWorldSid,
                                       GENERIC_READ ))
    {
        NLS_FREE_MEM(pWorldSid);
        NtClose(hFile);
        return (rc);
    }

    //
    //  Create the section.
    //
    RtlInitUnicodeString(&ObSecName, pwszSecName);
    InitializeObjectAttributes( &ObjA,
                                &ObSecName,
                                OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
                                NULL,
                                pSecurityDescriptor );

    rc = NtCreateSection( phSec,
                          SECTION_MAP_READ,
                          &ObjA,
                          NULL,
                          PAGE_READONLY,
                          SEC_COMMIT,
                          hFile );

    //
    //  Free the memory used for the SID and close the file.
    //
    NLS_FREE_MEM(pWorldSid);
    NtClose(hFile);

    //
    //  Check for error from NtCreateSection.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Create Section %wZ - %lx.\n",
                 &ObSecName, rc));
        return (rc);
    }
    else if (rc != STATUS_OBJECT_NAME_EXISTS)
    {
        //
        //  Call the server to preserve the section handle.
        //  Don't bother checking the error return, because the
        //  section was successfully created for this process.
        //
        a->hSection = *phSec;

        CsrClientCallServer( (PCSR_API_MSG)&m,
                             NULL,
                             CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
                                                 BasepNlsPreserveSection),
                             sizeof(*a) );
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  CreateSectionTemp
//
//  This routine creates a temporary memory mapped section for the given file
//  name and returns the handle to the section.
//
//  09-01-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG CreateSectionTemp(
    HANDLE *phSec,
    LPWSTR pwszFileName)
{
    HANDLE hFile = (HANDLE)0;          // file handle
    OBJECT_ATTRIBUTES ObjA;            // object attributes structure
    ULONG rc = 0L;                     // return code


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  Open the data file.
    //
    if (rc = OpenDataFile( &hFile,
                           pwszFileName ))
    {
        return (rc);
    }

    //
    //  Create the section.
    //
    InitializeObjectAttributes( &ObjA,
                                NULL,
                                0,
                                NULL,
                                NULL );

    rc = NtCreateSection( phSec,
                          SECTION_MAP_READ,
                          &ObjA,
                          NULL,
                          PAGE_READONLY,
                          SEC_COMMIT,
                          hFile );

    //
    //  Close the file.
    //
    NtClose(hFile);

    //
    //  Check for error from NtCreateSection.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Create Temp Section for %ws - %lx.\n",
                 pwszFileName, rc));
    }

    //
    //  Return success.
    //
    return (rc);
}


////////////////////////////////////////////////////////////////////////////
//
//  OpenSection
//
//  This routine opens the named memory mapped section for the given section
//  name and returns the handle to the section.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG OpenSection(
    HANDLE *phSec,
    PUNICODE_STRING pObSectionName,
    PVOID *ppBaseAddr,
    ULONG AccessMask,
    BOOL bCloseHandle)
{
    OBJECT_ATTRIBUTES ObjA;            // object attributes structure
    ULONG rc = 0L;                     // return code


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  Open the Section.
    //
    InitializeObjectAttributes( &ObjA,
                                pObSectionName,
                                OBJ_CASE_INSENSITIVE,
                                NULL,
                                NULL );

    rc = NtOpenSection( phSec,
                        AccessMask,
                        &ObjA );

    //
    //  Check for error from NtOpenSection.
    //
    if (!NT_SUCCESS(rc))
    {
        return (rc);
    }

    //
    //  Map a View of the Section.
    //
    if (rc = MapSection( *phSec,
                         ppBaseAddr,
                         PAGE_READONLY,
                         FALSE ))
    {
        NtClose(*phSec);
        return (rc);
    }

    //
    //  Close the handle to the section.  Once the section has been mapped,
    //  the pointer to the base address will remain valid until the section
    //  is unmapped.  It is not necessary to leave the handle to the section
    //  around.
    //
    if (bCloseHandle)
    {
        NtClose(*phSec);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  MapSection
//
//  This routine maps a view of the section to the current process and adds
//  the appropriate information to the hash table.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG MapSection(
    HANDLE hSec,
    PVOID *ppBaseAddr,
    ULONG PageProtection,
    BOOL bCloseHandle)
{
    ULONG ViewSize;                    // view size of mapped section
    ULONG rc = 0L;                     // return code


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  Map a View of the Section.
    //
    *ppBaseAddr = (PVOID)NULL;
    ViewSize = 0L;

    rc = NtMapViewOfSection( hSec,
                             NtCurrentProcess(),
                             ppBaseAddr,
                             0L,
                             0L,
                             NULL,
                             &ViewSize,
                             ViewUnmap,
                             0L,
                             PageProtection );

    //
    //  Close the handle to the section.  Once the section has been mapped,
    //  the pointer to the base address will remain valid until the section
    //  is unmapped.  It is not necessary to leave the handle to the section
    //  around.
    //
    if (bCloseHandle)
    {
        NtClose(hSec);
    }

    //
    //  Check for error from NtMapViewOfSection.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Map View of Section - %lx.\n", rc));
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  UnMapSection
//
//  This routine unmaps a view of the given section to the current process.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG UnMapSection(
    PVOID pBaseAddr)
{
    ULONG rc = 0L;                     // return code


    //
    //  Make sure we're in the critical section when entering this call.
    //
    ASSERT(NtCurrentTeb()->ClientId.UniqueThread == gcsTblPtrs.OwningThread);

    //
    //  UnMap a View of the Section.
    //
    rc = NtUnmapViewOfSection( NtCurrentProcess(),
                               pBaseAddr );

    //
    //  Check for error from NtUnmapViewOfSection.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Unmap View of Section - %lx.\n", rc));
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetNlsSectionName
//
//  This routine returns a section name by concatenating the given
//  section prefix and the given integer value converted to a string.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG GetNlsSectionName(
    UINT Value,
    UINT Base,
    UINT Padding,
    LPWSTR pwszPrefix,
    LPWSTR pwszSecName)
{
    //
    //  Create section name string.
    //
    NlsStrCpyW(pwszSecName, pwszPrefix);
    NLS_INTEGER_TO_UNICODE_STR( Value,
                                Base,
                                Padding,
                                pwszSecName + NlsStrLenW(pwszSecName),
                                MAX_PATH_LEN );

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetScriptMemberWeights
//
//  Gets the script member sorting order from the registry.  If the order
//  is different from the default order, it then sets the SMWeight array
//  in the table pointers structure to the correct values and sets the
//  IfModify_SMWeight flag to TRUE.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG GetScriptMemberWeights()
{
    DWORD ctr, ctr2;                           // loop counters
    ULONG Index = 0;                           // index for enumeration
    PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to full info for enum
    BYTE pStatic[MAX_KEY_VALUE_FULLINFO];      // ptr to static buffer for enum
    PVOID pBuffer = NULL;                      // ptr to alloc buffer for enum
    ULONG BufSize;                             // size of buffer
    ULONG ResultLength;                        // # bytes written
    UNICODE_STRING ObUnicodeStr;               // unicode string
    ULONG SortOrder;                           // sort order from registry
    ULONG Script;                              // script from registry
    ULONG rc = 0L;                             // return code
    BYTE RegVal[NUM_SM];                       // registry value for script
    BYTE NewScript;                            // new script to store
    BYTE SM;                                   // script member value
    LPBYTE pSMWeight = pTblPtrs->SMWeight;     // ptr to script member weights
    PMULTI_WT pMulti;                          // ptr to multi weight
    HANDLE hKey = NULL;                        // handle to registry key


    //
    //  Enter table pointers critical section.
    //
    RtlEnterCriticalSection(&gcsTblPtrs);

    //
    //  Make sure the SMWeight structure still hasn't been initialized.
    //
    //  NOTE: Must leave the first value set to INVALID_SM_VALUE until
    //        this function is complete.
    //
    if (pSMWeight[0] != INVALID_SM_VALUE)
    {
        RtlLeaveCriticalSection(&gcsTblPtrs);
        return (NO_ERROR);
    }

    //
    //  Set the 0 to FIRST_SCRIPT of script structure to its default
    //  value and the RegVal structure to zero.
    //
    //  NOTE: The intermediate RegVal array is necessary because:
    //            (1) Enumeration may produce values out of order
    //            (2) There may be multiple weight entries in the list
    //        As a result, it is not correct to simply increment
    //        the sorting order by the value given in the registry.
    //
    RtlZeroMemory(RegVal, NUM_SM);
    RtlZeroMemory(pSMWeight + 1, NUM_SM - 1);

    for (ctr = 1; ctr < FIRST_SCRIPT; ctr++)
    {
        pSMWeight[ctr] = (BYTE)ctr;
    }

    //
    //  Enumerate through all values in the registry.  Store the
    //  data in the RegVal structure if it exists.
    //
    //  NOTE: Values range from (1) to (NUM_SM - FIRST_SCRIPT).
    //
    OPEN_CPANEL_SORTING_KEY(hKey, (ULONG)STATUS_REGISTRY_CORRUPT);

    pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
    BufSize = MAX_KEY_VALUE_FULLINFO;
    RtlZeroMemory(pKeyValueFull, BufSize);
    rc = NtEnumerateValueKey( hKey,
                              Index,
                              KeyValueFullInformation,
                              pKeyValueFull,
                              BufSize,
                              &ResultLength );

    while (rc != STATUS_NO_MORE_ENTRIES)
    {
        if (rc == STATUS_BUFFER_OVERFLOW)
        {
            //
            //  Free old buffer if it was allocated before allocating
            //  a new buffer.
            //
            NLS_REG_BUFFER_FREE(pBuffer);

            //
            //  Buffer is too small, so allocate a new one.
            //
            NLS_REG_BUFFER_ALLOC(pKeyValueFull, ResultLength, pBuffer, TRUE);
            BufSize = ResultLength;
            RtlZeroMemory(pKeyValueFull, BufSize);
            rc = NtEnumerateValueKey( hKey,
                                      Index,
                                      KeyValueFullInformation,
                                      pKeyValueFull,
                                      BufSize,
                                      &ResultLength );
        }

        if (rc != NO_ERROR)
        {
            NLS_REG_BUFFER_FREE(pBuffer);
            CLOSE_REG_KEY(hKey);
            KdPrint(("NLSAPI: Error in getting Script Member Weights - %lx.\n",
                     rc));
            RtlLeaveCriticalSection(&gcsTblPtrs);
            return (rc);
        }

        //
        //  Convert the string value to an integer if data exists for
        //  the value.
        //
        if (pKeyValueFull->DataLength > 2)
        {
            //
            //  Convert the value to an integer.
            //
            RtlInitUnicodeString(&ObUnicodeStr, pKeyValueFull->Name);
            if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &SortOrder)) ||
                (SortOrder > (NUM_SM - FIRST_SCRIPT)))
            {
                //
                //  Report that there was an error in the registry.
                //
                KdPrint(("NLSAPI: Sorting Order Registry Value Corrupt.\n"));
            }
            else
            {
                //
                //  Convert the data to an integer and save it in the
                //  RegVal structure.
                //
                RtlInitUnicodeString( &ObUnicodeStr,
                                      GET_VALUE_DATA_PTR(pKeyValueFull) );
                if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Script)) ||
                    (Script >= NUM_SM) || (Script < FIRST_SCRIPT))
                {
                    //
                    //  Report that there was an error in the registry.
                    //
                    KdPrint(("NLSAPI: Sorting Order Registry Data Corrupt.\n"));
                }
                else
                {
                    RegVal[SortOrder] = (BYTE)Script;
                    if (SortOrder != (ULONG)(Script - FIRST_SCRIPT + 1))
                    {
                        //
                        //  No longer default order.  Set the boolean to TRUE.
                        //
                        pTblPtrs->IfModify_SMWeight = TRUE;
                    }
                }
            }
        }

        //
        //  Increment enumeration index value and get the next enumeration.
        //
        Index++;
        RtlZeroMemory(pKeyValueFull, BufSize);
        rc = NtEnumerateValueKey( hKey,
                                  Index,
                                  KeyValueFullInformation,
                                  pKeyValueFull,
                                  BufSize,
                                  &ResultLength );
    }

    //
    //  Free the buffer used for the enumeration.
    //
    NLS_REG_BUFFER_FREE(pBuffer);

    //
    //  Store the values in the SMWeight array in the table
    //  pointers structure only if the IfModify_SMWeight boolean is set
    //  to TRUE.
    //
    if (pTblPtrs->IfModify_SMWeight)
    {
        //
        //  Not using default table, so set the SMWeight array to the
        //  correct order.
        //
        NewScript = FIRST_SCRIPT;
        for (ctr = 1; ctr < NUM_SM; ctr++)
        {
            //
            //  For each registry value, store the appropriate order in
            //  each of the script member fields.
            //
            if ((SM = RegVal[ctr]) != 0)
            {
                //
                //  Save the order in the SMWeight array.
                //
                pSMWeight[SM] = NewScript;
                NewScript++;

                //
                //  Make sure the script is not part of a multiple weights
                //  script.
                //
                pMulti = pTblPtrs->pMultiWeight;
                for (ctr2 = pTblPtrs->NumMultiWeight; ctr2 > 0; ctr2--, pMulti++)
                {
                    if (pMulti->FirstSM == SM)
                    {
                        //
                        //  Part of multiple weight, so must move entire range
                        //  by setting each value in range to NewScript and
                        //  then incrementing NewScript.
                        //
                        //  NOTE:  May use 'ctr2' here since it ALWAYS breaks
                        //         out of outer for loop.
                        //
                        for (ctr2 = 1; ctr2 < pMulti->NumSM; ctr2++)
                        {
                            pSMWeight[SM + ctr2] = NewScript;
                            NewScript++;
                        }
                        break;
                    }
                }
            }
        }

        //
        //  Must set each script member that has not yet been reset to its
        //  new order.
        //
        //  The default ordering is to assign:
        //       Order  =  Script Member Value
        //
        //  Therefore, can simply set each zero entry in order to the end
        //  of the array to the next 'NewScript' value.
        //
        for (ctr = FIRST_SCRIPT; ctr < NUM_SM; ctr++)
        {
            //
            //  If it's a zero value, set it to the next sorting order value.
            //
            if (pSMWeight[ctr] == 0)
            {
                pSMWeight[ctr] = NewScript;
                NewScript++;
            }
        }
    }

    //
    //  Close the sorting key handle.
    //
    CLOSE_REG_KEY(hKey);

    //
    //  Set the first value to be valid.
    //
    pSMWeight[0] = 0;
    RtlLeaveCriticalSection(&gcsTblPtrs);

    //
    //  Return success.
    //
    return (NO_ERROR);
}




//-------------------------------------------------------------------------//
//                           INTERNAL ROUTINES                             //
//-------------------------------------------------------------------------//


////////////////////////////////////////////////////////////////////////////
//
//  OpenDataFile
//
//  This routine opens the data file for the specified file name and
//  returns the handle to the file.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG OpenDataFile(
    HANDLE *phFile,
    LPWSTR pFile)
{
    UNICODE_STRING ObFileName;         // file name
    OBJECT_ATTRIBUTES ObjA;            // object attributes structure
    IO_STATUS_BLOCK iosb;              // IO status block
    ULONG rc = 0L;                     // return code


    //
    //  Get the NT file name.
    //
    if (rc = GetNTFileName( pFile,
                            &ObFileName ))
    {
        return (rc);
    }

    //
    //  Open the file.
    //
    InitializeObjectAttributes( &ObjA,
                                &ObFileName,
                                OBJ_CASE_INSENSITIVE,
                                NULL,
                                NULL );

    rc = NtOpenFile( phFile,
                     FILE_READ_DATA | SYNCHRONIZE,
                     &ObjA,
                     &iosb,
                     FILE_SHARE_READ,
                     FILE_SYNCHRONOUS_IO_NONALERT );

    //
    //  Free the buffer used for the file name.
    //
    RtlFreeHeap( RtlProcessHeap(),
                 0,
                 ObFileName.Buffer );

    //
    //  Check for error from NtOpenFile.
    //
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Open File %wZ - %lx.\n", &ObFileName, rc));
        return (rc);
    }
    if (!NT_SUCCESS(iosb.Status))
    {
        KdPrint(("NLSAPI: Could NOT Open File %wZ - Status = %lx.\n",
                 &ObFileName, iosb.Status));
        return ((ULONG)iosb.Status);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetNTFileName
//
//  This routine returns the full path name for the data file found in
//  the given registry information buffer.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG GetNTFileName(
    LPWSTR pFile,
    PUNICODE_STRING pFileName)
{
    WCHAR pwszFilePath[MAX_PATH_LEN];  // ptr to file path string
    UNICODE_STRING ObFileName;         // file name
    ULONG rc = 0L;                     // return code


    //
    //  Get the full path name for the file.
    //
    GetSystemDirectoryW(pwszFilePath, MAX_PATH_LEN);
    NlsStrCatW(pwszFilePath, L"\\");
    NlsStrCatW(pwszFilePath, pFile);

    //
    //  Make the file name an NT path name.
    //
    RtlInitUnicodeString(&ObFileName, pwszFilePath);
    if (!RtlDosPathNameToNtPathName_U( ObFileName.Buffer,
                                       pFileName,
                                       NULL,
                                       NULL ))
    {
        KdPrint(("NLSAPI: Could NOT convert %wZ to NT path name - %lx.\n",
                 &ObFileName, rc));
        return (ERROR_FILE_NOT_FOUND);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  CreateSecurityDescriptor
//
//  This routine creates the security descriptor needed to create the
//  memory mapped section for a data file and returns the world SID.
//
//  05-31-91    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG CreateSecurityDescriptor(
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    PSID *ppWorldSid,
    ACCESS_MASK AccessMask)
{
    ULONG rc = 0L;                     // return code
    PACL pAclBuffer;                   // ptr to ACL buffer
    ULONG SidLength;                   // length of SID - 1 sub authority
    PSID pWSid;                        // ptr to world SID
    SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_WORLD_SID_AUTHORITY;


    //
    //  Create World SID.
    //
    SidLength = RtlLengthRequiredSid(1);

    if ((pWSid = (PSID)NLS_ALLOC_MEM(SidLength)) == NULL)
    {
        *ppWorldSid = NULL;
        KdPrint(("NLSAPI: Could NOT Allocate SID Buffer.\n"));
        return (ERROR_OUTOFMEMORY);
    }
    *ppWorldSid = pWSid;

    RtlInitializeSid( pWSid,
                      &SidAuth,
                      1 );

    *(RtlSubAuthoritySid(pWSid, 0)) = SECURITY_WORLD_RID;

    //
    //  Initialize Security Descriptor.
    //
    rc = RtlCreateSecurityDescriptor( pSecurityDescriptor,
                                      SECURITY_DESCRIPTOR_REVISION );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Create Security Descriptor - %lx.\n", rc));
        return (rc);
    }

    //
    //  Initialize ACL.
    //
    pAclBuffer = (PACL)((PBYTE)pSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
    rc = RtlCreateAcl( (PACL)pAclBuffer,
                       MAX_PATH_LEN * sizeof(ULONG),
                       ACL_REVISION2 );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Create ACL - %lx.\n", rc));
        return (rc);
    }

    //
    //  Add an ACE to the ACL that allows World GENERIC_READ to the
    //  section object.
    //
    rc = RtlAddAccessAllowedAce( (PACL)pAclBuffer,
                                 ACL_REVISION2,
                                 AccessMask,
                                 pWSid );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Add Access Allowed ACE - %lx.\n", rc));
        return (rc);
    }

    //
    //  Assign the DACL to the security descriptor.
    //
    rc = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR)pSecurityDescriptor,
                                       (BOOLEAN)TRUE,
                                       (PACL)pAclBuffer,
                                       (BOOLEAN)FALSE );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Set DACL Security Descriptor - %lx.\n", rc));
        return (rc);
    }

    //
    //  Return success.
    //
    return (NO_ERROR);
}


////////////////////////////////////////////////////////////////////////////
//
//  AppendAccessAllowedACE
//
//  This routine adds an ACE to the ACL for administrators.
//
//  03-08-93    JulieB    Created.
////////////////////////////////////////////////////////////////////////////

ULONG AppendAccessAllowedACE(
    PSECURITY_DESCRIPTOR pSecurityDescriptor,
    ACCESS_MASK AccessMask)
{
    ULONG rc = 0L;                     // return code
    PACL pDaclBuffer;                  // ptr to DACL buffer
    ULONG SidLength;                   // length of SID - 2 sub authorities
    PSID pWSid;                        // ptr to world SID
    SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_NT_AUTHORITY;
    BOOLEAN DaclPresent;
    BOOLEAN DaclDefaulted;


    //
    //  Create World SID.
    //
    SidLength = RtlLengthRequiredSid(2);

    if ((pWSid = (PSID)NLS_ALLOC_MEM(SidLength)) == NULL)
    {
        KdPrint(("NLSAPI: Could NOT Allocate SID Buffer.\n"));
        return (ERROR_OUTOFMEMORY);
    }

    RtlInitializeSid( pWSid,
                      &SidAuth,
                      2 );

    *(RtlSubAuthoritySid(pWSid, 0)) = SECURITY_BUILTIN_DOMAIN_RID;
    *(RtlSubAuthoritySid(pWSid, 1)) = DOMAIN_ALIAS_RID_ADMINS;

    //
    //  Get DACL.
    //
    rc = RtlGetDaclSecurityDescriptor( pSecurityDescriptor,
                                       &DaclPresent,
                                       &pDaclBuffer,
                                       &DaclDefaulted );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Get DACL Security Descriptor - %lx.\n", rc));
        return (rc);
    }

    //
    //  Add an ACE to the ACL that allows Admin query access to the
    //  section object.
    //
    rc = RtlAddAccessAllowedAce( (PACL)pDaclBuffer,
                                 ACL_REVISION2,
                                 AccessMask,
                                 pWSid );
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI: Could NOT Add Access Allowed ACE - %lx.\n", rc));
        return (rc);
    }

    //
    //  Free SID.
    //
    NLS_FREE_MEM(pWSid);

    //
    //  Return success.
    //
    return (NO_ERROR);
}