/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    CONFIG.C

Abstract:

    This file contains the routines that walk the configuration registry.

Author:

    Rajen Shah  (rajens)    1-Jul-1991


Revision History:

    28-Mar-1994     Danl
        ReadRegistryInfo:  LogFileInfo->LogFileName wasn't getting updated
        when using the default (generated) LogFileName.
    16-Mar-1994     Danl
        Fixed Memory Leaks in ReadRegistryInfo().  Call to
        RtlDosPathNameToNtPathName allocates memory that wasn't being free'd.

--*/

//
// INCLUDES
//

#include <eventp.h>
#include <elfcfg.h>
#include <wcstr.h>
#include <malloc.h>
#include <memory.h>

//
// STRUCTURES
//

//
// This structure contains all the information used to setup and
// for listening to registry changes in the eventlog tree.
//
typedef struct _REG_MONITOR_INFO {
    HANDLE      NotifyEventHandle;
    DWORD       Timeout;
    HANDLE      WorkItemHandle;
} REG_MONITOR_INFO, *LPREG_MONITOR_INFO;

//
// GLOBALS
//
    REG_MONITOR_INFO    GlRegMonitorInfo;

//
// LOCAL FUNCTIONS
//
DWORD
ElfRegistryMonitor (
        LPVOID  pParms,
        DWORD   dwWaitStatus
        );

BOOL
ElfSetupMonitor(
    LPREG_MONITOR_INFO  pMonitorInfo
    );




VOID
ProcessChange (
        HANDLE hLogFile,
        PUNICODE_STRING ModuleName,
        PUNICODE_STRING LogFileName,
        ULONG MaxSize,
        ULONG Retention
        )

/*++

Routine Description:

    This routine is called by ProcessRegistryChanges for each module.

Arguments:



Return Value:

    None

Note:


--*/
{

    NTSTATUS        Status = STATUS_SUCCESS;
    PLOGMODULE      pModule;
    PLOGFILE        pLogFile;
    ULONG           Size;
    PVOID           BaseAddress;
    PUNICODE_STRING pFileNameString;
    LPWSTR          FileName;


    pModule = GetModuleStruc (ModuleName);

    //
    // If this module didn't exist, this was a brand new log file and
    // we need to create all the structures
    //

    if (pModule == ElfDefaultLogModule &&
        wcscmp(ModuleName->Buffer, ELF_DEFAULT_MODULE_NAME)) {
            Status = SetUpDataStruct(LogFileName, MaxSize, Retention,
                ModuleName, hLogFile, ElfNormalLog);
        return;
    }

    //
    // Update values
    //

    pLogFile = pModule->LogFile;
    pLogFile->Retention = Retention;

    //
    // Check to see if the name has changed.  If it has, and the log
    // hasn't been used yet, then use the new name.  Be sure to free
    // memory that was used for the old name.
    //

    if ((wcscmp(pLogFile->LogFileName->Buffer, LogFileName->Buffer) != 0) &&
        (pLogFile->BeginRecord == pLogFile->EndRecord)) {

        pFileNameString = ElfpAllocateBuffer(
                        sizeof(UNICODE_STRING) +
                        LogFileName->MaximumLength);

        if (pFileNameString != NULL) {
            FileName = (LPWSTR)(pFileNameString+1);
            wcscpy(FileName, LogFileName->Buffer);
            RtlInitUnicodeString(pFileNameString, FileName);

            ElfpFreeBuffer(pLogFile->LogFileName);
            pLogFile->LogFileName = pFileNameString;
        }
    }

    //
    // The log file can only be grown dynamically.  To shrink it,
    // it has to be cleared.
    //


    if (pLogFile->ConfigMaxFileSize < ELFFILESIZE(MaxSize)) {

        //
        // Save away how much we're growing so we can make sure we
        // can allocate this much more VM space
        //

        Size = ELFFILESIZE(MaxSize) - pLogFile->ConfigMaxFileSize;

        //
        // Reserve the increase in size just to make sure we can
        //

        BaseAddress = NULL;
        Status = NtAllocateVirtualMemory(
                    NtCurrentProcess(),
                    &BaseAddress,
                    0,  // Hi order bits in address that must be 0
                    &Size,
                    MEM_RESERVE,
                    PAGE_READWRITE);

        if (!NT_SUCCESS(Status)) {
            //
            // We must have hit our quota trying to grow the page tables.
            // All we can do is send an alert and leave things the way
            // they are.
            // BUGBUG - send an alert here
            //

            return;
        }
        else {

            Size = 0;
            Status = NtFreeVirtualMemory(
                NtCurrentProcess(),
                &BaseAddress,
                &Size,
                MEM_RELEASE);

            ASSERT(NT_SUCCESS(Status));

            //
            // Now unmap the view
            //

            Status = NtUnmapViewOfSection(
                NtCurrentProcess(),
                pLogFile->BaseAddress);

            ASSERT(NT_SUCCESS(Status));

            //
            //     Free up the extra address range we had reserved (if any)
            //

            if (pLogFile->ViewSize < pLogFile->ConfigMaxFileSize) {

                Size = 0;
                BaseAddress =
                    (PBYTE) pLogFile->BaseAddress + pLogFile->ViewSize,
                Status = NtFreeVirtualMemory(
                    NtCurrentProcess(),
                    &BaseAddress,
                    &Size,
                    MEM_RELEASE);

                ASSERT(NT_SUCCESS(Status));
            }

            //
            //
            //           And allocate/map it all fresh
            //

            pLogFile->ConfigMaxFileSize = ELFFILESIZE(MaxSize);
            pLogFile->BaseAddress = NULL;
            Status = NtMapViewOfSection(
                pLogFile->SectionHandle,
                NtCurrentProcess(),
                &pLogFile->BaseAddress,
                0,
                0,
                NULL,
                &pLogFile->ViewSize,
                ViewUnmap,
                0,
                PAGE_READWRITE);

            ASSERT(NT_SUCCESS(Status));

            BaseAddress = (PBYTE) pLogFile->BaseAddress +
                pLogFile->ViewSize;
            Size = pLogFile->ConfigMaxFileSize - pLogFile->ViewSize;
            Status = NtAllocateVirtualMemory(
                NtCurrentProcess(),
                &BaseAddress,
                0,
                &Size,
                MEM_RESERVE,
                PAGE_READWRITE);

            ASSERT(NT_SUCCESS(Status));

        }

    }
    else if (pLogFile->ConfigMaxFileSize > ELFFILESIZE(MaxSize)) {

        //
        // They're shrinking the size of the log file.
        // Next time we clear the log file, we'll use the new size
        // and new retention.
        //

        pLogFile->NextClearMaxFileSize = ELFFILESIZE(MaxSize);

    }

    //
    // Now see if they've added any new modules for this log file
    //

    SetUpModules(hLogFile, pLogFile, TRUE);

    return;
}



VOID
ProcessRegistryChanges (
        VOID
        )

/*++

Routine Description:

    This routine processes that changes that have occurred in the
    eventlog node. It does this by rescanning the whole Eventlog node
    and then comparing with what it has as the current configuration.

Arguments:

    NONE.

Return Value:

    NONE

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    HANDLE hLogFile;
    UNICODE_STRING SubKeyName;
    ULONG Index = 0;
    BYTE Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
    PKEY_NODE_INFORMATION KeyBuffer = (PKEY_NODE_INFORMATION) Buffer;
    ULONG ActualSize;
    LOG_FILE_INFO LogFileInfo;
    PWCHAR SubKeyString;
    OBJECT_ATTRIBUTES ObjectAttributes;

    ElfDbgPrint(("[ELF] ProcessRegistryChanges\n"));

    //
    // Take the global resource so that nobody is making changes or
    // using the existing configured information.
    //

    GetGlobalResource (ELF_GLOBAL_SHARED);

    //
    // See if the Debug flag changed
    //

    RtlInitUnicodeString(&SubKeyName, VALUE_DEBUG);

    NtQueryValueKey(hEventLogNode, &SubKeyName,
        KeyValueFullInformation, KeyBuffer,
        ELF_MAX_REG_KEY_INFO_SIZE, & ElfDebug);


    //
    // Loop thru the subkeys under Eventlog and set up each logfile
    //

    while (NT_SUCCESS(Status)) {

        Status = NtEnumerateKey(hEventLogNode, Index++, KeyNodeInformation,
            KeyBuffer, ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);

        if (NT_SUCCESS(Status)) {

            //
            // It turns out the Name isn't null terminated, so we need
            // to copy it somewhere and null terminate it before we use it
            //

            SubKeyString = ElfpAllocateBuffer(KeyBuffer->NameLength +
                sizeof (WCHAR));
            if (!SubKeyString) {

                //
                // No one to notify, just give up till next time.
                //

                ReleaseGlobalResource();
                return;
            }

            memcpy(SubKeyString, KeyBuffer->Name, KeyBuffer->NameLength);
            SubKeyString[KeyBuffer->NameLength / sizeof(WCHAR)] = L'\0' ;

            //
            // Open the node for this logfile and extract the information
            // required by SetupDataStruct, and then call it.
            //

            RtlInitUnicodeString(&SubKeyName, SubKeyString);

            InitializeObjectAttributes(&ObjectAttributes,
                                      &SubKeyName,
                                      OBJ_CASE_INSENSITIVE,
                                      hEventLogNode,
                                      NULL
                                      );

            Status = NtOpenKey(&hLogFile, KEY_READ | KEY_SET_VALUE,
                &ObjectAttributes);

            //
            // Should always succeed since I just enum'ed it, but if it
            // doesn't, just skip it
            //

            if (!NT_SUCCESS(Status)) {

                ElfpFreeBuffer(SubKeyString);
                Status = STATUS_SUCCESS; // to keep the enum going
                continue;

            }

            Status = ReadRegistryInfo(hLogFile, &SubKeyName, & LogFileInfo);

            if (NT_SUCCESS(Status)) {
                //
                // Now process any changes.  Any errors are dealt with
                // in ProcessChange
                //

                ProcessChange (
                    hLogFile,
                    &SubKeyName,
                    LogFileInfo.LogFileName,
                    LogFileInfo.MaxFileSize,
                    LogFileInfo.Retention
                    );

                //
                // Free the buffer that was allocated in ReadRegistryInfo.
                //
                ElfpFreeBuffer(LogFileInfo.LogFileName);
            }

            ElfpFreeBuffer(SubKeyString);
            NtClose(hLogFile);
        }
    }

    //
    // Release the global resource.
    //

    ReleaseGlobalResource();


} // ProcessRegistryChanges




DWORD
ElfRegistryMonitor (
        LPVOID  pParms,
        DWORD   dwWaitStatus
        )

/*++

Routine Description:

    This is the entry point for the thread that will monitor changes in
    the registry. If anything changes, it will have to scan the change
    and then make the appropriate changes to the data structures in the
    service to reflect the new information.

Arguments:

    NONE

Return Value:

    NONE

Note:


--*/
{
    LPREG_MONITOR_INFO  pMonitorInfo=(LPREG_MONITOR_INFO)pParms;

    ElfDbgPrint(("[ELF] Inside registry monitor thread\n"));


    if (GetElState() == STOPPING) {
        //
        // If the eventlog is shutting down, then we need
        // to terminate this thread.
        //
        ElfDbgPrint(("[ELF] ElfRegistryMonitor - Shutdown\n"));

        //
        // Close the registry handle and registry event handle.
        //
        NtClose( hEventLogNode);
        CloseHandle(pMonitorInfo->NotifyEventHandle);

        //===============================================================
        // When we return from this call, the service controller will
        // decrement our reference count to 0 and unload the DLL.
        // So we can't exit until all the rest of the eventlog has shutdown.
        // This thread will perform the final cleanup for the eventlog.
        //
        ElfpCleanUp(EventFlags);

        //
        // We should actually return here so that the DLL gets unloaded.
        // However, RPC has a problem in that it might still call our
        // context rundown routine even though we unregistered our interface.
        // So we exit thread instead.  This keeps our Dll loaded.
        //
        ExitThread(0);
    }
    if (dwWaitStatus == STATUS_TIMEOUT) {

       ElfDbgPrint(("[ELF] Timer popped, running queued list\n"));

       //
       // Timer popped, try running the list
       //

       if (!IsListEmpty(&QueuedEventListHead)) {

           //
           // There are things queued up to write, do it
           //

           WriteQueuedEvents();

       }

       //
       // Don't wait again
       //

       pMonitorInfo->Timeout = INFINITE;

    }
    else if (dwWaitStatus == WAIT_OBJECT_0) {
        ElfDbgPrint(("[ELF] ElfRegistryMonitor - Notification\n"));
        ProcessRegistryChanges ();
    }
    else {
       ElfDbgPrint(("[ELF] WorkItemCallback returned %d\n",
        dwWaitStatus));
    }

    if (!ElfSetupMonitor(pMonitorInfo)) {
        ElfDbgPrint(("[ELF] ElfSetupMonitor Failed! Can't listen for Reg Changes\n"));
    }

    ElfDbgPrint(("[ELF] ElfRegistryMonitor - returning\n"));

    return(0);

} // ElfRegistryMonitor



DWORD
InitNotify(
    PVOID   pData,
    DWORD   dwWaitStatus
    )

/*++

Routine Description:


Arguments:


Return Value:


--*/
{
    NTSTATUS        NtStatus = STATUS_SUCCESS;
    DWORD           status = NO_ERROR;
    DWORD           Buffer;
    PVOID           pBuffer = & Buffer;
    LPREG_MONITOR_INFO  pMonitorInfo;

    static IO_STATUS_BLOCK IoStatusBlock;

    UNREFERENCED_PARAMETER(dwWaitStatus);

    ElfDbgPrint(("[ELF]In InitNotify Routine\n"));

    pMonitorInfo = (LPREG_MONITOR_INFO)pData;

    NtStatus = NtNotifyChangeKey (
                    hEventLogNode,
                    pMonitorInfo->NotifyEventHandle,
                    NULL,
                    NULL,
                    &IoStatusBlock,
                    REG_NOTIFY_CHANGE_LAST_SET |
                    REG_NOTIFY_CHANGE_NAME,
                    TRUE,
                    pBuffer,
                    1,
                    TRUE      // return and wait on event
                    );

    if (!NT_SUCCESS(NtStatus)) {
        status = RtlNtStatusToDosError(NtStatus);
    }
    ElfDbgPrint(("[ELF]NtNotifyChangeKey Status =  0x%lx\n",NtStatus));
    return(status);

} // InitNotify


BOOL
ElfSetupMonitor(
    LPREG_MONITOR_INFO  pMonitorInfo
    )

/*++

Routine Description:

    This function submits a request for a registry NotifyChangeKey
    and then submits a work item to the service controller thread
    management system to wait for the Notification handle to become
    signaled.

Arguments:

    pMonitorInfo - This is a pointer to a MONITOR_INFO structure.  This
        function fills in the WorkItemHandle member of that structure
        if successfully adds a new work item.

Return Value:

    TRUE - if successful in setting up.
    FALSE - if unsuccessful.  A work item hasn't been submitted, and
        we won't be listening for registry changes.

--*/
{
    NTSTATUS        Status = STATUS_SUCCESS;

    //
    // Call NtNotifyChange Key indirectly via the service controller
    // Watcher thread.  This way the thread that created the I/O
    // request will always be around.
    //
    pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
                    NULL,
                    InitNotify,
                    (PVOID)pMonitorInfo,
                    SVC_IMMEDIATE_CALLBACK,
                    INFINITE,
                    NULL);

    if (pMonitorInfo->WorkItemHandle == NULL) {
        ElfDbgPrint(("[ELF]Couldn't Initialize Registry Notify %d\n",GetLastError()));
        return(FALSE);
    }
    //
    // Add the work item that is to be called when the
    // NotifyEventHandle is signalled.
    //
    pMonitorInfo->WorkItemHandle = ElfGlobalData->SvcsAddWorkItem(
                    pMonitorInfo->NotifyEventHandle,
                    ElfRegistryMonitor,
                    (PVOID)pMonitorInfo,
                    SVC_QUEUE_WORK_ITEM,
                    pMonitorInfo->Timeout,
                    ElfGlobalSvcRefHandle);

    if (pMonitorInfo->WorkItemHandle == NULL) {
        ElfDbgPrint(("[ELF]Couldn't add Reg Monitor work item\n"));
        return(FALSE);
    }
    return(TRUE);

}  // ElfSetupMonitor


BOOL
ElfStartRegistryMonitor()

/*++

Routine Description:

    This routine starts up the thread that monitors changes in the registry.

    This function calls ElfSetupMonitor() to register for the change
    notification and to submit a work item to wait for the registry
    change event to get signaled.  When signalled, the ElfRegistryMonitor()
    callback function is called by a thread from the services thread pool.
    This callback function services the notification.

Arguments:

    NONE

Return Value:

    TRUE if thread creation succeeded, FALSE otherwise.

Note:


--*/
{
    NTSTATUS        Status;
    HANDLE          hNotifyEvent=NULL;

    ElfDbgPrint(("[ELF] Starting registry monitor\n"));

    if (hEventLogNode == NULL) {
        ElfDbgPrint(("[ELF]No EventLog node in registry - Exit Monitor"));
        return(FALSE);
    }

    //
    // Create an event to wait on
    //

    Status = NtCreateEvent (&hNotifyEvent, EVENT_ALL_ACCESS,
        NULL, NotificationEvent, FALSE);

    if (!NT_SUCCESS(Status)) {
        ElfDbgPrint(("[ELF]Couldn't create event for registry monitor"));
        return(FALSE);
    }

    //
    // Fill in the Monitor info structure with the event handle
    // and a 5 minute timeout.
    //

    GlRegMonitorInfo.NotifyEventHandle = hNotifyEvent;
    GlRegMonitorInfo.Timeout = 5 * 60 * 1000;
    GlRegMonitorInfo.WorkItemHandle = NULL;

    //
    // Setup for the change notify and
    // submit the work item to the service controller.
    //

    if (!ElfSetupMonitor(&GlRegMonitorInfo)) {
        return(FALSE);
    }
    return(TRUE);

} // ElfStartRegistryMonitor


VOID
StopRegistryMonitor ()

/*++

Routine Description:

    This routine wakes up the work item that has been submitted for the
    purpose of monitoring registry eventlog changes.  The thread created
    to service that work item will actually do the clean-up of the monitor
    thread.


Arguments:

    NONE

Return Value:

    NONE

--*/

{
    ElfDbgPrint (("[ELF] Stopping registry monitor\n"));

    //
    // Wake up the RegistryMonitorThread.
    //
    if (GlRegMonitorInfo.NotifyEventHandle != NULL ) {
        SetEvent(GlRegMonitorInfo.NotifyEventHandle);
    }

    return;

} // StopRegistryMonitor


NTSTATUS
ReadRegistryInfo (
    HANDLE hLogFile,
    PUNICODE_STRING SubKeyName,
    PLOG_FILE_INFO LogFileInfo
    )

/*++

Routine Description:

    This routine reads in the information from the node pointed to by
    hLogFile and stores it in the a structure so that the
    necessary data structures can be set up for the service.

    ALLOCATIONS:  If successful, this function allocates memory for
        LogFileInfo->LogFileName.  It is the responsiblilty of the caller
        to free this memory.

Arguments:

    hLogFile - A handle to the Eventlog\<somelogfile> node in the registry
    KeyName  - The subkey for this logfile to open
    LogFileInfo - The structure to fill in with the data

Return Value:

    NTSTATUS

--*/
{

#define EXPAND_BUFFER_SIZE 64

    NTSTATUS        Status;
    BOOLEAN         RegistryCorrupt = FALSE;
    BYTE            Buffer[ELF_MAX_REG_KEY_INFO_SIZE];
    ULONG           ActualSize;
    UNICODE_STRING  ValueName;
    UNICODE_STRING  UnexpandedName;
    UNICODE_STRING  ExpandedName;
    ULONG           NumberOfBytes = 0;
    BYTE            ExpandNameBuffer[EXPAND_BUFFER_SIZE];
    PUNICODE_STRING FileNameString;
    LPWSTR          FileName;
    BOOL            ExpandedBufferWasAllocated=FALSE;
    PKEY_VALUE_FULL_INFORMATION ValueBuffer =
        (PKEY_VALUE_FULL_INFORMATION) Buffer;

    ASSERT(hLogFile);

    // MaxSize

    RtlInitUnicodeString(&ValueName, VALUE_MAXSIZE);

    Status = NtQueryValueKey(hLogFile, &ValueName,
        KeyValueFullInformation, ValueBuffer,
        ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
    if (!NT_SUCCESS(Status)) {
        ElfDbgPrint(("[ELF] - Logfile %ws Maxsize doesn't exist\n",
            SubKeyName->Buffer));
        LogFileInfo->MaxFileSize = ELF_DEFAULT_MAX_FILE_SIZE;
        RegistryCorrupt = TRUE;
    }
    else {
        LogFileInfo->MaxFileSize = *((PULONG)(Buffer +
            ValueBuffer->DataOffset));
    }


    // Retention period

    RtlInitUnicodeString(&ValueName, VALUE_RETENTION);

    Status = NtQueryValueKey(hLogFile, &ValueName,
        KeyValueFullInformation, ValueBuffer,
        ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);
    if (!NT_SUCCESS(Status)) {
        ElfDbgPrint(("[ELF] - Logfile %ws Retention doesn't exist\n",
            SubKeyName->Buffer));
        LogFileInfo->Retention = ELF_DEFAULT_RETENTION_PERIOD;
        RegistryCorrupt = TRUE;
    }
    else {
        LogFileInfo->Retention = *((PULONG)(Buffer +
            ValueBuffer->DataOffset));
    }


    // Filename

    RtlInitUnicodeString(&ValueName, VALUE_FILENAME);

    Status = NtQueryValueKey(hLogFile, &ValueName,
        KeyValueFullInformation, ValueBuffer,
        ELF_MAX_REG_KEY_INFO_SIZE, & ActualSize);

    if (!NT_SUCCESS(Status)) {

        //
        // Allocate the buffer for the UNICODE_STRING for the filename and
        // initialize it. (41 = \Systemroot\system32\config\xxxxxxxx.evt)
        //

        FileNameString = ElfpAllocateBuffer(41 * sizeof(WCHAR) +
            sizeof(UNICODE_STRING));
        if (!FileNameString) {
            return(STATUS_NO_MEMORY);
        }

        LogFileInfo->LogFileName = FileNameString;
        FileName = (LPWSTR)(FileNameString + 1);
        wcscpy(FileName, L"\\Systemroot\\System32\\Config\\");
        wcsncat(FileName, SubKeyName->Buffer, 8);
        wcscat(FileName, L".evt");
        RtlInitUnicodeString(FileNameString, FileName);

        RegistryCorrupt = TRUE;

    }
    else {

        //
        // If it's a REG_EXPAND_SZ expand it
        //

        if (ValueBuffer->Type == REG_EXPAND_SZ) {

            //
            // Initialize the UNICODE_STRING, when the string isn't null
            // terminated
            //

            UnexpandedName.MaximumLength = UnexpandedName.Length =
                (USHORT) ValueBuffer->DataLength;
            UnexpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
                ValueBuffer->DataOffset);

            //
            // Call the magic expand-o api
            //

            ExpandedName.Length = ExpandedName.MaximumLength =
                EXPAND_BUFFER_SIZE;
            ExpandedName.Buffer = (LPWSTR) ExpandNameBuffer;
            Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
                &ExpandedName, &NumberOfBytes);

            if (NumberOfBytes > EXPAND_BUFFER_SIZE) {

                //
                // The default buffer wasn't big enough.  Allocate a
                // bigger one and try again
                //

                ExpandedName.Length = ExpandedName.MaximumLength =
                    (USHORT) NumberOfBytes;
                ExpandedName.Buffer = ElfpAllocateBuffer(ExpandedName.Length);
                if (!ExpandedName.Buffer) {
                    return(STATUS_NO_MEMORY);
                }
                ExpandedBufferWasAllocated = TRUE;

                Status = RtlExpandEnvironmentStrings_U(NULL, &UnexpandedName,
                    &ExpandedName, &NumberOfBytes);
            }

            if (!NT_SUCCESS(Status)) {
                if (ExpandedBufferWasAllocated) {
                    ElfpFreeBuffer(ExpandedName.Buffer);
                }
                return(Status);
            }
        }
        else {

            //
            // It doesn't need to be expanded, just set up the UNICODE_STRING
            // for the conversion to an NT pathname
            //

            ExpandedName.MaximumLength = ExpandedName.Length =
                (USHORT) ValueBuffer->DataLength;
            ExpandedName.Buffer = (PWSTR) ((PBYTE) ValueBuffer +
                ValueBuffer->DataOffset);
        }

        //
        // Now convert from a DOS pathname to an NT pathname
        //
        // Need to allocate this since it needs to stay around
        //

        //
        // Translate to NtPathName.
        // NOTE:  this allocates a buffer for ValueName.Buffer.
        //
        if (!RtlDosPathNameToNtPathName_U(ExpandedName.Buffer,
                &ValueName, NULL, NULL)) {

            if (ExpandedBufferWasAllocated) {
                ElfpFreeBuffer(ExpandedName.Buffer);
            }
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Allocate memory for the unicode string structure and the buffer
        // so that it can be free'd with a single call.
        //
        FileNameString = ElfpAllocateBuffer(
                            sizeof(UNICODE_STRING) +
                            ((ValueName.Length + 1) * sizeof(WCHAR)));

        if (!FileNameString) {
            if (ExpandedBufferWasAllocated) {
                ElfpFreeBuffer(ExpandedName.Buffer);
            }
            RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);
            return(STATUS_NO_MEMORY);
        }

        //
        // Copy the NtPathName string into the new buffer, and initialize
        // the unicode string.
        //
        FileName = (LPWSTR)(FileNameString + 1);
        wcsncpy(FileName, ValueName.Buffer, ValueName.Length);
        *(FileName+ValueName.Length) = L'\0';
        RtlInitUnicodeString(FileNameString, FileName);

        //
        // Free memory allocated by RtlDosPathNAmeToNtPathName.
        //
        RtlFreeHeap(RtlProcessHeap(),0,ValueName.Buffer);

        //
        // Clean up if I had to allocate a bigger buffer than the default
        //

        if (ExpandedBufferWasAllocated) {
            ElfpFreeBuffer(ExpandedName.Buffer);
        }

    }

    //
    // Add the LogFileName to the LogFileInfo structure.
    //
    LogFileInfo->LogFileName = FileNameString;

    //
    // If we didn't find all the required values, tell someone
    //

    if (RegistryCorrupt) {
        ElfDbgPrintNC(("[ELF] Registry information for %ws invalid\n",
            SubKeyName->Buffer));
    }

    return(STATUS_SUCCESS);

}

