/*
 * COPYRIGHT:        See COPYING in the top level directory
 * PROJECT:          ReactOS kernel
 * FILE:             ntoskrnl/cm/ntfunc.c
 * PURPOSE:          Ntxxx function for registry access
 * UPDATE HISTORY:
*/

/* INCLUDES *****************************************************************/

#include <ddk/ntddk.h>
#include <roscfg.h>
#include <internal/ob.h>
#include <limits.h>
#include <string.h>
#include <internal/pool.h>
#include <internal/registry.h>

#define NDEBUG
#include <internal/debug.h>

#include "cm.h"


/* GLOBALS ******************************************************************/

extern POBJECT_TYPE  CmiKeyType;
extern PREGISTRY_HIVE  CmiVolatileHive;

static BOOLEAN CmiRegistryInitialized = FALSE;


/* FUNCTIONS ****************************************************************/

NTSTATUS STDCALL
NtCreateKey(OUT PHANDLE KeyHandle,
	    IN ACCESS_MASK DesiredAccess,
	    IN POBJECT_ATTRIBUTES ObjectAttributes,
	    IN ULONG TitleIndex,
	    IN PUNICODE_STRING Class,
	    IN ULONG CreateOptions,
	    OUT PULONG Disposition)
{
  UNICODE_STRING RemainingPath;
  PKEY_OBJECT KeyObject;
  NTSTATUS Status;
  PVOID Object;
  PWSTR End;

  DPRINT("NtCreateKey (Name %wZ  KeyHandle %x  Root %x)\n",
	 ObjectAttributes->ObjectName,
	 KeyHandle,
	 ObjectAttributes->RootDirectory);

  /* FIXME: check for standard handle prefix and adjust objectAttributes accordingly */

  Status = ObFindObject(ObjectAttributes,
			&Object,
			&RemainingPath,
			CmiKeyType);
  if (!NT_SUCCESS(Status))
    {
      return(Status);
    }

  DPRINT("RemainingPath %wZ\n", &RemainingPath);

  if ((RemainingPath.Buffer == NULL) || (RemainingPath.Buffer[0] == 0))
    {
      /* Fail if the key has been deleted */
      if (((PKEY_OBJECT) Object)->Flags & KO_MARKED_FOR_DELETE)
	{
	  ObDereferenceObject(Object);
	  return(STATUS_UNSUCCESSFUL);
	}

      if (Disposition)
	*Disposition = REG_OPENED_EXISTING_KEY;

      Status = ObCreateHandle(PsGetCurrentProcess(),
			      Object,
			      DesiredAccess,
			      FALSE,
			      KeyHandle);

      DPRINT("Status %x\n", Status);
      ObDereferenceObject(Object);
      return Status;
    }

  /* If RemainingPath contains \ we must return error
     because NtCreateKey don't create trees */
  if (RemainingPath.Buffer[0] == '\\')
    End = wcschr(RemainingPath.Buffer + 1, '\\');
  else
    End = wcschr(RemainingPath.Buffer, '\\');

  if (End != NULL)
    {
      ObDereferenceObject(Object);
      return STATUS_OBJECT_NAME_NOT_FOUND;
    }

  DPRINT("RemainingPath %S  ParentObject %x\n", RemainingPath.Buffer, Object);

  Status = ObCreateObject(KeyHandle,
			  DesiredAccess,
			  NULL,
			  CmiKeyType,
			  (PVOID*)&KeyObject);
  if (!NT_SUCCESS(Status))
    {
      return(Status);
    }

  KeyObject->ParentKey = Object;

  if (CreateOptions & REG_OPTION_VOLATILE)
    KeyObject->RegistryHive = CmiVolatileHive;
  else
    KeyObject->RegistryHive = KeyObject->ParentKey->RegistryHive;

  KeyObject->Flags = 0;
  KeyObject->NumberOfSubKeys = 0;
  KeyObject->SizeOfSubKeys = 0;
  KeyObject->SubKeys = NULL;

  /* Acquire hive lock */
  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  /* add key to subkeys of parent if needed */
  Status = CmiAddSubKey(KeyObject->RegistryHive,
			KeyObject->ParentKey,
			KeyObject,
			RemainingPath.Buffer,
			RemainingPath.Length,
			TitleIndex,
			Class,
			CreateOptions);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("CmiAddSubKey() failed (Status %lx)\n", Status);
      /* Release hive lock */
      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      ObDereferenceObject(Object);
      return STATUS_UNSUCCESSFUL;
    }

  KeyObject->Name = KeyObject->KeyCell->Name;
  KeyObject->NameSize = KeyObject->KeyCell->NameSize;

  if (KeyObject->RegistryHive == KeyObject->ParentKey->RegistryHive)
    {
      KeyObject->KeyCell->ParentKeyOffset = KeyObject->ParentKey->BlockOffset;
      KeyObject->KeyCell->SecurityKeyOffset = KeyObject->ParentKey->KeyCell->SecurityKeyOffset;
    }
  else
    {
      KeyObject->KeyCell->ParentKeyOffset = -1;
      KeyObject->KeyCell->SecurityKeyOffset = -1;
      /* This key must remain in memory unless it is deleted
	 or file is unloaded */
      ObReferenceObjectByPointer(KeyObject,
				 STANDARD_RIGHTS_REQUIRED,
				 NULL,
				 UserMode);
    }

  CmiAddKeyToList(KeyObject->ParentKey, KeyObject);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Release hive lock */
  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);

  ObDereferenceObject(KeyObject);
  ObDereferenceObject(Object);

  if (Disposition)
    *Disposition = REG_CREATED_NEW_KEY;

  CmiSyncHives();

  return Status;
}


NTSTATUS STDCALL
NtDeleteKey(IN HANDLE KeyHandle)
{
  PKEY_OBJECT KeyObject;
  NTSTATUS Status;

  DPRINT("KeyHandle %x\n", KeyHandle);

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
				     KEY_WRITE,
				     CmiKeyType,
				     UserMode,
				     (PVOID *)&KeyObject,
				     NULL);
  if (!NT_SUCCESS(Status))
    {
      return(Status);
    }

  /* Acquire hive lock */
  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Check for subkeys */
  if (KeyObject->NumberOfSubKeys != 0)
    {
      Status = STATUS_CANNOT_DELETE;
    }
  else
    {
      /* Set the marked for delete bit in the key object */
      KeyObject->Flags |= KO_MARKED_FOR_DELETE;
      Status = STATUS_SUCCESS;
    }

  /* Release hive lock */
  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);

  DPRINT("PointerCount %lu\n", ObGetObjectPointerCount((PVOID)KeyObject));

  /* Dereference the object */
  ObDereferenceObject(KeyObject);
  if(KeyObject->RegistryHive != KeyObject->ParentKey->RegistryHive)
    ObDereferenceObject(KeyObject);

  DPRINT("PointerCount %lu\n", ObGetObjectPointerCount((PVOID)KeyObject));

  /*
   * Note:
   * Hive-Synchronization will not be triggered here. This is done in
   * CmiObjectDelete() (in regobj.c) after all key-related structures
   * have been released.
   */

  return(Status);
}


NTSTATUS STDCALL
NtEnumerateKey(
	IN	HANDLE			KeyHandle,
	IN	ULONG			  Index,
	IN	KEY_INFORMATION_CLASS	KeyInformationClass,
	OUT	PVOID			  KeyInformation,
	IN	ULONG			  Length,
	OUT	PULONG			ResultLength
	)
{
  NTSTATUS  Status;
  PKEY_OBJECT  KeyObject;
  PREGISTRY_HIVE  RegistryHive;
  PKEY_CELL  KeyCell, SubKeyCell;
  PHASH_TABLE_CELL  HashTableBlock;
  PKEY_BASIC_INFORMATION  BasicInformation;
  PKEY_NODE_INFORMATION  NodeInformation;
  PKEY_FULL_INFORMATION  FullInformation;
  PDATA_CELL pClassData;

  DPRINT("KH %x  I %d  KIC %x KI %x  L %d  RL %x\n",
	 KeyHandle,
	 Index,
	 KeyInformationClass,
	 KeyInformation,
	 Length,
	 ResultLength);

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
		KEY_ENUMERATE_SUB_KEYS,
		CmiKeyType,
		UserMode,
		(PVOID *) &KeyObject,
		NULL);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ObReferenceObjectByHandle() failed with status %x\n", Status);
      return(Status);
    }

  /* Acquire hive lock */
  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to KeyCell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;

  /* Get pointer to SubKey */
  if (Index >= KeyCell->NumberOfSubKeys)
    {
      if (RegistryHive == CmiVolatileHive)
	{
	  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
	  ObDereferenceObject(KeyObject);
	  DPRINT("No more volatile entries\n");
	  return(STATUS_NO_MORE_ENTRIES);
	}
      else
	{
	  ULONG i;
	  PKEY_OBJECT CurKey = NULL;

	  /* Search volatile keys */
	  for (i = 0; i < KeyObject->NumberOfSubKeys; i++)
	    {
	      CurKey = KeyObject->SubKeys[i];
	      if (CurKey->RegistryHive == CmiVolatileHive)
		{
		  if (Index-- == KeyObject->NumberOfSubKeys)
		    break;
		}
	    }
	  if (Index >= KeyCell->NumberOfSubKeys)
	    {
	      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
	      ObDereferenceObject(KeyObject);
	      DPRINT("No more non-volatile entries\n");
	      return(STATUS_NO_MORE_ENTRIES);
	    }
	  SubKeyCell = CurKey->KeyCell;
	}
    }
  else
    {
      if (KeyCell->HashTableOffset == (BLOCK_OFFSET)-1)
	{
	  return(STATUS_NO_MORE_ENTRIES);
	  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
	  ObDereferenceObject(KeyObject);
	}

      HashTableBlock = CmiGetBlock(RegistryHive, KeyCell->HashTableOffset, NULL);
      if (HashTableBlock == NULL)
	{
	  DPRINT("CmiGetBlock() failed\n");
	  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
	  ObDereferenceObject(KeyObject);
	  return STATUS_UNSUCCESSFUL;
	}
      SubKeyCell = CmiGetKeyFromHashByIndex(RegistryHive,
					    HashTableBlock,
					    Index);
    }

  if (SubKeyCell == NULL)
    {
      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      DPRINT("No more entries\n");
      return(STATUS_NO_MORE_ENTRIES);
    }

  Status = STATUS_SUCCESS;
  switch (KeyInformationClass)
    {
    case KeyBasicInformation:
      /* Check size of buffer */
      *ResultLength = sizeof(KEY_BASIC_INFORMATION) + 
        (SubKeyCell->NameSize ) * sizeof(WCHAR);
      if (Length < *ResultLength)
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* Fill buffer with requested info */
          BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
          BasicInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
          BasicInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
          BasicInformation->TitleIndex = Index;
          BasicInformation->NameLength = SubKeyCell->NameSize * sizeof(WCHAR);
          mbstowcs(BasicInformation->Name, 
            SubKeyCell->Name, 
            SubKeyCell->NameSize * 2);
//          BasicInformation->Name[SubKeyCell->NameSize] = 0;
        }
      break;
      
    case KeyNodeInformation:
      /* Check size of buffer */
      *ResultLength = sizeof(KEY_NODE_INFORMATION) +
        SubKeyCell->NameSize * sizeof(WCHAR) +
        SubKeyCell->ClassSize;
      if (Length < *ResultLength)
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* Fill buffer with requested info */
          NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
          NodeInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
          NodeInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
          NodeInformation->TitleIndex = Index;
          NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) + 
            SubKeyCell->NameSize * sizeof(WCHAR);
          NodeInformation->ClassLength = SubKeyCell->ClassSize;
          NodeInformation->NameLength = SubKeyCell->NameSize * sizeof(WCHAR);
          mbstowcs(NodeInformation->Name, 
            SubKeyCell->Name,
            SubKeyCell->NameSize * 2);
//          NodeInformation->Name[SubKeyCell->NameSize] = 0;
          if (SubKeyCell->ClassSize != 0)
            {
              pClassData=CmiGetBlock(KeyObject->RegistryHive,
                SubKeyCell->ClassNameOffset,
                NULL);
              wcsncpy(NodeInformation->Name + SubKeyCell->NameSize ,
                      (PWCHAR) pClassData->Data,
                      SubKeyCell->ClassSize);
            }
        }
      break;
      
    case KeyFullInformation:
      /*  check size of buffer  */
      *ResultLength = sizeof(KEY_FULL_INFORMATION) +
          SubKeyCell->ClassSize;
      if (Length < *ResultLength)
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* fill buffer with requested info  */
          FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
          FullInformation->LastWriteTime.u.LowPart = SubKeyCell->LastWriteTime.dwLowDateTime;
          FullInformation->LastWriteTime.u.HighPart = SubKeyCell->LastWriteTime.dwHighDateTime;
          FullInformation->TitleIndex = Index;
          FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) - 
            sizeof(WCHAR);
          FullInformation->ClassLength = SubKeyCell->ClassSize;
          FullInformation->SubKeys = SubKeyCell->NumberOfSubKeys;
          FullInformation->MaxNameLen = 
            CmiGetMaxNameLength(RegistryHive, SubKeyCell);
          FullInformation->MaxClassLen = 
            CmiGetMaxClassLength(RegistryHive, SubKeyCell);
          FullInformation->Values = SubKeyCell->NumberOfValues;
          FullInformation->MaxValueNameLen = 
            CmiGetMaxValueNameLength(RegistryHive, SubKeyCell);
          FullInformation->MaxValueDataLen = 
            CmiGetMaxValueDataLength(RegistryHive, SubKeyCell);
          if (SubKeyCell->ClassSize != 0)
            {
              pClassData = CmiGetBlock(KeyObject->RegistryHive,
                SubKeyCell->ClassNameOffset,
                NULL);
              wcsncpy(FullInformation->Class,
                (PWCHAR) pClassData->Data,
                SubKeyCell->ClassSize);
            }
        }
      break;
    }

  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
  ObDereferenceObject(KeyObject);

  DPRINT("Returning status %x\n", Status);

  return(Status);
}


NTSTATUS STDCALL
NtEnumerateValueKey(IN HANDLE KeyHandle,
	IN ULONG Index,
	IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
	OUT PVOID KeyValueInformation,
	IN ULONG Length,
	OUT PULONG ResultLength)
{
  NTSTATUS  Status;
  PKEY_OBJECT  KeyObject;
  PREGISTRY_HIVE  RegistryHive;
  PKEY_CELL  KeyCell;
  PVALUE_CELL  ValueCell;
  PDATA_CELL  DataCell;
  PKEY_VALUE_BASIC_INFORMATION  ValueBasicInformation;
  PKEY_VALUE_PARTIAL_INFORMATION  ValuePartialInformation;
  PKEY_VALUE_FULL_INFORMATION  ValueFullInformation;

  DPRINT("KH %x  I %d  KVIC %x  KVI %x  L %d  RL %x\n",
	 KeyHandle,
	 Index,
	 KeyValueInformationClass,
	 KeyValueInformation,
	 Length,
	 ResultLength);

  /*  Verify that the handle is valid and is a registry key  */
  Status = ObReferenceObjectByHandle(KeyHandle,
		KEY_QUERY_VALUE,
		CmiKeyType,
		UserMode,
		(PVOID *) &KeyObject,
		NULL);

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

  /* Acquire hive lock */
  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to KeyCell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;

  /* Get Value block of interest */
  Status = CmiGetValueFromKeyByIndex(RegistryHive,
		KeyCell,
		Index,
		&ValueCell);

  if (!NT_SUCCESS(Status))
    {
      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      return Status;
    }

  if (ValueCell != NULL)
    {
      switch (KeyValueInformationClass)
        {
        case KeyValueBasicInformation:
          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
            {
              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
                (ValueCell->NameSize + 1) * sizeof(WCHAR);
            }
          else
            {
              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
                ValueCell->NameSize + sizeof(WCHAR);
            }
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_OVERFLOW;
            }
          else
            {
              ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION) 
                KeyValueInformation;
              ValueBasicInformation->TitleIndex = 0;
              ValueBasicInformation->Type = ValueCell->DataType;
              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                {
                  ValueBasicInformation->NameLength =
                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
                  CmiCopyPackedName(ValueBasicInformation->Name,
                                    ValueCell->Name,
                                    ValueCell->NameSize);
                  ValueBasicInformation->Name[ValueCell->NameSize] = 0;
                }
              else
                {
                  ValueBasicInformation->NameLength =
                    ValueCell->NameSize + sizeof(WCHAR);
                  RtlCopyMemory(ValueBasicInformation->Name,
                                ValueCell->Name,
                                ValueCell->NameSize * sizeof(WCHAR));
                  ValueBasicInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
                }
            }
          break;

        case KeyValuePartialInformation:
          *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 
            (ValueCell->DataSize & LONG_MAX);
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_OVERFLOW;
            }
          else
            {
              ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
                KeyValueInformation;
              ValuePartialInformation->TitleIndex = 0;
              ValuePartialInformation->Type = ValueCell->DataType;
              ValuePartialInformation->DataLength = ValueCell->DataSize & LONG_MAX;
              if(ValueCell->DataSize >0)
              {
                DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
                RtlCopyMemory(ValuePartialInformation->Data, 
                  DataCell->Data,
                  ValueCell->DataSize & LONG_MAX);
              }
              else
              {
                RtlCopyMemory(ValuePartialInformation->Data, 
                  &ValueCell->DataOffset, 
                  ValueCell->DataSize & LONG_MAX);
              }
              DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
            }
          break;

        case KeyValueFullInformation:
          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
            {
              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
                (ValueCell->NameSize + 1) * sizeof(WCHAR) +
                (ValueCell->DataSize & LONG_MAX);
            }
          else
            {
              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
                ValueCell->NameSize + sizeof(WCHAR) +
                (ValueCell->DataSize & LONG_MAX);
            }
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_OVERFLOW;
            }
          else
            {
              ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION) 
                KeyValueInformation;
              ValueFullInformation->TitleIndex = 0;
              ValueFullInformation->Type = ValueCell->DataType;
              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                {
                  ValueFullInformation->NameLength =
                    (ValueCell->NameSize + 1) * sizeof(WCHAR);

                  CmiCopyPackedName(ValueFullInformation->Name,
				    ValueCell->Name,
				    ValueCell->NameSize);
                  ValueFullInformation->Name[ValueCell->NameSize] = 0;
                }
              else
                {
                  ValueFullInformation->NameLength =
                    ValueCell->NameSize + sizeof(WCHAR);
                  RtlCopyMemory(ValueFullInformation->Name,
				ValueCell->Name,
				ValueCell->NameSize);
                  ValueFullInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
                }
              ValueFullInformation->DataOffset = 
                (ULONG)ValueFullInformation->Name - (ULONG)ValueFullInformation +
                ValueFullInformation->NameLength;
              ValueFullInformation->DataOffset =
                (ValueFullInformation->DataOffset + 3) & 0xfffffffc;
              ValueFullInformation->DataLength = ValueCell->DataSize & LONG_MAX;
              if (ValueCell->DataSize > 0)
                {
                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
                  RtlCopyMemory((PCHAR) ValueFullInformation
                    + ValueFullInformation->DataOffset,
                    DataCell->Data,
                    ValueCell->DataSize & LONG_MAX);
                }
              else
                {
                  RtlCopyMemory((PCHAR) ValueFullInformation
                    + ValueFullInformation->DataOffset,
                    &ValueCell->DataOffset,
                    ValueCell->DataSize & LONG_MAX);
                }
            }
          break;
        }
    }
  else
    {
      Status = STATUS_UNSUCCESSFUL;
    }

  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
  ObDereferenceObject(KeyObject);

  return Status;
}


NTSTATUS STDCALL
NtFlushKey(IN HANDLE KeyHandle)
{
  NTSTATUS Status;
  PKEY_OBJECT  KeyObject;
  PREGISTRY_HIVE  RegistryHive;

  DPRINT("NtFlushKey (KeyHandle %lx) called\n", KeyHandle);

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
				     KEY_QUERY_VALUE,
				     CmiKeyType,
				     UserMode,
				     (PVOID *)&KeyObject,
				     NULL);
  if (!NT_SUCCESS(Status))
    {
      return(Status);
    }

  VERIFY_KEY_OBJECT(KeyObject);

  RegistryHive = KeyObject->RegistryHive;

  /* Acquire hive lock */
  ExAcquireResourceExclusiveLite(&RegistryHive->HiveResource,
				 TRUE);

  if (IsVolatileHive(RegistryHive))
    {
      Status = STATUS_SUCCESS;
    }
  else
    {
      /* Flush non-volatile hive */
      Status = CmiFlushRegistryHive(RegistryHive);
    }

  ExReleaseResourceLite(&RegistryHive->HiveResource);

  ObDereferenceObject(KeyObject);

  return STATUS_SUCCESS;
}


NTSTATUS STDCALL
NtOpenKey(OUT PHANDLE KeyHandle,
	  IN ACCESS_MASK DesiredAccess,
	  IN POBJECT_ATTRIBUTES ObjectAttributes)
{
  UNICODE_STRING RemainingPath;
  NTSTATUS Status;
  PVOID Object;

  DPRINT("NtOpenFile(KH %x  DA %x  OA %x  OA->ON '%wZ'\n",
	 KeyHandle,
	 DesiredAccess,
	 ObjectAttributes,
	 ObjectAttributes ? ObjectAttributes->ObjectName : NULL);

  RemainingPath.Buffer = NULL;
  Status = ObFindObject(ObjectAttributes,
			&Object,
			&RemainingPath,
			CmiKeyType);
  if (!NT_SUCCESS(Status))
    {
      return(Status);
    }

  VERIFY_KEY_OBJECT((PKEY_OBJECT) Object);

  DPRINT("RemainingPath '%wZ'\n", &RemainingPath);

  if ((RemainingPath.Buffer != NULL) && (RemainingPath.Buffer[0] != 0))
    {
      ObDereferenceObject(Object);
      return(STATUS_UNSUCCESSFUL);
    }

  /* Fail if the key has been deleted */
  if (((PKEY_OBJECT)Object)->Flags & KO_MARKED_FOR_DELETE)
    {
      ObDereferenceObject(Object);
      return(STATUS_UNSUCCESSFUL);
    }

  Status = ObCreateHandle(PsGetCurrentProcess(),
			  Object,
			  DesiredAccess,
			  FALSE,
			  KeyHandle);
  ObDereferenceObject(Object);

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

  return(STATUS_SUCCESS);
}


NTSTATUS STDCALL
NtQueryKey(IN HANDLE KeyHandle,
	   IN KEY_INFORMATION_CLASS KeyInformationClass,
	   OUT PVOID KeyInformation,
	   IN ULONG Length,
	   OUT PULONG ResultLength)
{
  PKEY_BASIC_INFORMATION BasicInformation;
  PKEY_NODE_INFORMATION NodeInformation;
  PKEY_FULL_INFORMATION FullInformation;
  PREGISTRY_HIVE RegistryHive;
  PDATA_CELL pClassData;
  PKEY_OBJECT KeyObject;
  PKEY_CELL KeyCell;
  NTSTATUS Status;

  DPRINT("KH %x  KIC %x  KI %x  L %d  RL %x\n",
	 KeyHandle,
	 KeyInformationClass,
	 KeyInformation,
	 Length,
	 ResultLength);

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
		KEY_READ,
		CmiKeyType,
		UserMode,
		(PVOID *) &KeyObject,
		NULL);
  if (!NT_SUCCESS(Status))
    {
      return Status;
    }

  /* Acquire hive lock */
  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to KeyCell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;

  Status = STATUS_SUCCESS;
  switch (KeyInformationClass)
    {
    case KeyBasicInformation:
      /* Check size of buffer */
      if (Length < sizeof(KEY_BASIC_INFORMATION) + 
          KeyObject->NameSize * sizeof(WCHAR))
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* Fill buffer with requested info */
          BasicInformation = (PKEY_BASIC_INFORMATION) KeyInformation;
          BasicInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
          BasicInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
          BasicInformation->TitleIndex = 0;
          BasicInformation->NameLength = (KeyObject->NameSize) * sizeof(WCHAR);
          mbstowcs(BasicInformation->Name,
            KeyObject->Name, 
            KeyObject->NameSize*sizeof(WCHAR));
          *ResultLength = sizeof(KEY_BASIC_INFORMATION) + 
            KeyObject->NameSize * sizeof(WCHAR);
        }
      break;

    case KeyNodeInformation:
      /* Check size of buffer */
      if (Length < sizeof(KEY_NODE_INFORMATION)
         + KeyObject->NameSize * sizeof(WCHAR)
         + KeyCell->ClassSize)
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* Fill buffer with requested info */
          NodeInformation = (PKEY_NODE_INFORMATION) KeyInformation;
          NodeInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
          NodeInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
          NodeInformation->TitleIndex = 0;
          NodeInformation->ClassOffset = sizeof(KEY_NODE_INFORMATION) + 
            KeyObject->NameSize * sizeof(WCHAR);
          NodeInformation->ClassLength = KeyCell->ClassSize;
          NodeInformation->NameLength = KeyObject->NameSize * sizeof(WCHAR);
          mbstowcs(NodeInformation->Name,
            KeyObject->Name,
            KeyObject->NameSize * sizeof(WCHAR));

          if (KeyCell->ClassSize != 0)
            {
              pClassData = CmiGetBlock(KeyObject->RegistryHive,
                KeyCell->ClassNameOffset,
                NULL);
              wcsncpy(NodeInformation->Name + KeyObject->NameSize * sizeof(WCHAR),
                (PWCHAR)pClassData->Data,
                KeyCell->ClassSize);
            }
          *ResultLength = sizeof(KEY_NODE_INFORMATION)
            + KeyObject->NameSize * sizeof(WCHAR)
            + KeyCell->ClassSize;
        }
      break;

    case KeyFullInformation:
      /* Check size of buffer */
      if (Length < sizeof(KEY_FULL_INFORMATION) + KeyCell->ClassSize)
        {
          Status = STATUS_BUFFER_OVERFLOW;
        }
      else
        {
          /* Fill buffer with requested info */
          FullInformation = (PKEY_FULL_INFORMATION) KeyInformation;
          FullInformation->LastWriteTime.u.LowPart = KeyCell->LastWriteTime.dwLowDateTime;
          FullInformation->LastWriteTime.u.HighPart = KeyCell->LastWriteTime.dwHighDateTime;
          FullInformation->TitleIndex = 0;
          FullInformation->ClassOffset = sizeof(KEY_FULL_INFORMATION) - sizeof(WCHAR);
          FullInformation->ClassLength = KeyCell->ClassSize;
          FullInformation->SubKeys = KeyCell->NumberOfSubKeys;
          FullInformation->MaxNameLen =
            CmiGetMaxNameLength(RegistryHive, KeyCell);
          FullInformation->MaxClassLen =
            CmiGetMaxClassLength(RegistryHive, KeyCell);
          FullInformation->Values = KeyCell->NumberOfValues;
          FullInformation->MaxValueNameLen =
            CmiGetMaxValueNameLength(RegistryHive, KeyCell);
          FullInformation->MaxValueDataLen = 
            CmiGetMaxValueDataLength(RegistryHive, KeyCell);
          if (KeyCell->ClassSize != 0)
            {
              pClassData=CmiGetBlock(KeyObject->RegistryHive,
                KeyCell->ClassNameOffset,
                NULL);
              wcsncpy(FullInformation->Class,
                (PWCHAR)pClassData->Data,
                KeyCell->ClassSize);
            }
          *ResultLength = sizeof(KEY_FULL_INFORMATION) + KeyCell->ClassSize;
        }
      break;
    }

  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
  ObDereferenceObject(KeyObject);

  return(Status);
}


NTSTATUS STDCALL
NtQueryValueKey(IN HANDLE KeyHandle,
	IN PUNICODE_STRING ValueName,
	IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
	OUT PVOID KeyValueInformation,
	IN ULONG Length,
	OUT PULONG ResultLength)
{
  NTSTATUS  Status;
  PKEY_OBJECT  KeyObject;
  PREGISTRY_HIVE  RegistryHive;
  PKEY_CELL  KeyCell;
  PVALUE_CELL  ValueCell;
  PDATA_CELL  DataCell;
  PKEY_VALUE_BASIC_INFORMATION  ValueBasicInformation;
  PKEY_VALUE_PARTIAL_INFORMATION  ValuePartialInformation;
  PKEY_VALUE_FULL_INFORMATION  ValueFullInformation;

  DPRINT("NtQueryValueKey(KeyHandle %x  ValueName %S  Length %x)\n",
    KeyHandle, ValueName->Buffer, Length);

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
		KEY_QUERY_VALUE,
		CmiKeyType,
		UserMode,
		(PVOID *)&KeyObject,
		NULL);

  if (!NT_SUCCESS(Status))
    {
      DPRINT("ObReferenceObjectByHandle() failed with status %x\n", Status);
      return Status;
    }

  /* Acquire hive lock */
  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to KeyCell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;

  /* Get Value block of interest */
  Status = CmiScanKeyForValue(RegistryHive,
			      KeyCell,
			      ValueName,
			      &ValueCell,
			      NULL);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("CmiScanKeyForValue() failed with status %x\n", Status);
      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      return(Status);
    }
  else if (ValueCell != NULL)
    {
      switch (KeyValueInformationClass)
        {
        case KeyValueBasicInformation:
          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
            {
              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
                (ValueCell->NameSize + 1) * sizeof(WCHAR);
            }
          else
            {
              *ResultLength = sizeof(KEY_VALUE_BASIC_INFORMATION) + 
                ValueCell->NameSize + sizeof(WCHAR);
            }
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_TOO_SMALL;
            }
          else
            {
              ValueBasicInformation = (PKEY_VALUE_BASIC_INFORMATION) 
                KeyValueInformation;
              ValueBasicInformation->TitleIndex = 0;
              ValueBasicInformation->Type = ValueCell->DataType;
              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                {
                  ValueBasicInformation->NameLength =
                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
                  CmiCopyPackedName(ValueBasicInformation->Name,
                                    ValueCell->Name,
                                    ValueCell->NameSize);
                  ValueBasicInformation->Name[ValueCell->NameSize] = 0;
                }
              else
                {
                  ValueBasicInformation->NameLength =
                    ValueCell->NameSize + sizeof(WCHAR);
                  RtlCopyMemory(ValueBasicInformation->Name,
                                ValueCell->Name,
                                ValueCell->NameSize * sizeof(WCHAR));
                  ValueBasicInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
                }
            }
          break;

        case KeyValuePartialInformation:
          *ResultLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION)
            + (ValueCell->DataSize & LONG_MAX);
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_TOO_SMALL;
            }
          else
            {
              ValuePartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)
                KeyValueInformation;
              ValuePartialInformation->TitleIndex = 0;
              ValuePartialInformation->Type = ValueCell->DataType;
              ValuePartialInformation->DataLength = ValueCell->DataSize & LONG_MAX;
              if (ValueCell->DataSize > 0)
                {
                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
                  RtlCopyMemory(ValuePartialInformation->Data,
                    DataCell->Data,
                    ValueCell->DataSize & LONG_MAX);
                }
              else
                {
                  RtlCopyMemory(ValuePartialInformation->Data,
                    &ValueCell->DataOffset,
                    ValueCell->DataSize & LONG_MAX);
                }
            }
          break;

        case KeyValueFullInformation:
          if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
            {
              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
                (ValueCell->NameSize + 1) * sizeof(WCHAR) +
                (ValueCell->DataSize & LONG_MAX);
            }
          else
            {
              *ResultLength = sizeof(KEY_VALUE_FULL_INFORMATION) + 
                ValueCell->NameSize + sizeof(WCHAR) +
                (ValueCell->DataSize & LONG_MAX);
            }
          if (Length < *ResultLength)
            {
              Status = STATUS_BUFFER_TOO_SMALL;
            }
          else
            {
              ValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)
                KeyValueInformation;
              ValueFullInformation->TitleIndex = 0;
              ValueFullInformation->Type = ValueCell->DataType;
              if (ValueCell->Flags & REG_VALUE_NAME_PACKED)
                {
                  ValueFullInformation->NameLength =
                    (ValueCell->NameSize + 1) * sizeof(WCHAR);
                  CmiCopyPackedName(ValueFullInformation->Name,
                                    ValueCell->Name,
                                    ValueCell->NameSize);
                  ValueFullInformation->Name[ValueCell->NameSize] = 0;
                }
              else
                {
                  ValueFullInformation->NameLength =
                    ValueCell->NameSize + sizeof(WCHAR);
                  RtlCopyMemory(ValueFullInformation->Name,
                                ValueCell->Name,
                                ValueCell->NameSize);
                  ValueFullInformation->Name[ValueCell->NameSize / sizeof(WCHAR)] = 0;
                }
              ValueFullInformation->DataOffset = 
                (ULONG)ValueFullInformation->Name - (ULONG)ValueFullInformation +
                ValueFullInformation->NameLength;
              ValueFullInformation->DataOffset =
                (ValueFullInformation->DataOffset + 3) & 0xfffffffc;
              ValueFullInformation->DataLength = ValueCell->DataSize & LONG_MAX;
              if (ValueCell->DataSize > 0)
                {
                  DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL);
                  RtlCopyMemory((PCHAR) ValueFullInformation
                                + ValueFullInformation->DataOffset,
                                DataCell->Data,
                                ValueCell->DataSize & LONG_MAX);
                }
              else
                {
                  RtlCopyMemory((PCHAR) ValueFullInformation
                                + ValueFullInformation->DataOffset,
                                &ValueCell->DataOffset,
                  ValueCell->DataSize & LONG_MAX);
                }
            }
          break;
        }
    }
  else
    {
      Status = STATUS_OBJECT_NAME_NOT_FOUND;
    }

  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
  ObDereferenceObject(KeyObject);

  return Status;
}


NTSTATUS STDCALL
NtSetValueKey(IN HANDLE KeyHandle,
	      IN PUNICODE_STRING ValueName,
	      IN ULONG TitleIndex,
	      IN ULONG Type,
	      IN PVOID Data,
	      IN ULONG DataSize)
{
  NTSTATUS  Status;
  PKEY_OBJECT  KeyObject;
  PREGISTRY_HIVE  RegistryHive;
  PKEY_CELL  KeyCell;
  PVALUE_CELL  ValueCell;
  BLOCK_OFFSET VBOffset;
  PDATA_CELL DataCell;
  PDATA_CELL NewDataCell;
  PHBIN pBin;
  ULONG DesiredAccess;

  DPRINT("NtSetValueKey(KeyHandle %x  ValueName '%wZ'  Type %d)\n",
	 KeyHandle, ValueName, Type);

  DesiredAccess = KEY_SET_VALUE;
  if (Type == REG_LINK)
    DesiredAccess |= KEY_CREATE_LINK;

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
				     DesiredAccess,
				     CmiKeyType,
				     UserMode,
				     (PVOID *)&KeyObject,
				     NULL);
  if (!NT_SUCCESS(Status))
    return(Status);

  /* Acquire hive lock exclucively */
  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to key cell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;
  Status = CmiScanKeyForValue(RegistryHive,
			      KeyCell,
			      ValueName,
			      &ValueCell,
			      &VBOffset);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("Value not found. Status 0x%X\n", Status);

      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      return(Status);
    }

  if (ValueCell == NULL)
    {
      DPRINT("Allocate new value cell\n");
      Status = CmiAddValueToKey(RegistryHive,
				KeyCell,
				ValueName,
				&ValueCell,
				&VBOffset);
      if (NT_SUCCESS(Status))
	{
	  CmiMarkBlockDirty(RegistryHive, VBOffset);
	}
    }

  if (!NT_SUCCESS(Status))
    {
      DPRINT("Cannot add value. Status 0x%X\n", Status);

      ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
      ObDereferenceObject(KeyObject);
      return(Status);
    }

  DPRINT("DataSize %lu\n", DataSize);
  DPRINT("ValueCell %p\n", ValueCell);
  DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);

  if (DataSize <= 4)
    {
      /* If datasize <= 4 then write in valueblock directly */
      DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);
      if ((ValueCell->DataSize >= 0) &&
	  (DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL)))
	{
	  CmiDestroyBlock(RegistryHive, DataCell, ValueCell->DataOffset);
	}

      RtlCopyMemory(&ValueCell->DataOffset, Data, DataSize);
      ValueCell->DataSize = DataSize | 0x80000000;
      ValueCell->DataType = Type;
      RtlMoveMemory(&ValueCell->DataOffset, Data, DataSize);
      CmiMarkBlockDirty(RegistryHive, VBOffset);
    }
  else if (DataSize <= (ULONG) (ValueCell->DataSize & 0x7fffffff))
    {
      /* If new data size is <= current then overwrite current data */
      DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset,&pBin);
      RtlZeroMemory(DataCell->Data, ValueCell->DataSize);
      RtlCopyMemory(DataCell->Data, Data, DataSize);
      ValueCell->DataSize = DataSize;
      ValueCell->DataType = Type;

      /* Update time of heap */
      if (!IsVolatileHive(RegistryHive))
	{
	  NtQuerySystemTime((PTIME) &pBin->DateModified);
	}
      CmiMarkBlockDirty(RegistryHive, ValueCell->DataOffset);
    }
  else
    {
      /*
       * New data size is larger than the current, destroy current
       * data block and allocate a new one.
       */
      BLOCK_OFFSET NewOffset;

      DPRINT("ValueCell->DataSize %lu\n", ValueCell->DataSize);

      if ((ValueCell->DataSize >= 0) &&
	  (DataCell = CmiGetBlock(RegistryHive, ValueCell->DataOffset, NULL)))
	{
	  CmiDestroyBlock(RegistryHive, DataCell, ValueCell->DataOffset);
	  ValueCell->DataSize = 0;
	  ValueCell->DataType = 0;
	  ValueCell->DataOffset = 0xffffffff;
	}

      Status = CmiAllocateBlock(RegistryHive,
				(PVOID *)&NewDataCell,
				DataSize,
				&NewOffset);
      if (!NT_SUCCESS(Status))
	{
	  DPRINT("CmiAllocateBlock() failed (Status %lx)\n", Status);

	  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
	  ObDereferenceObject(KeyObject);

	  return(Status);
	}

      RtlCopyMemory(&NewDataCell->Data[0], Data, DataSize);
      ValueCell->DataSize = DataSize;
      ValueCell->DataType = Type;
      ValueCell->DataOffset = NewOffset;
      CmiMarkBlockDirty(RegistryHive, ValueCell->DataOffset);
    }

  /* Mark link key */
  if ((_wcsicmp(ValueName->Buffer, L"SymbolicLinkValue") == 0) &&
      (Type == REG_LINK))
    {
      KeyCell->Type = REG_LINK_KEY_CELL_TYPE;
      CmiMarkBlockDirty(RegistryHive, KeyObject->BlockOffset);
    }

  /* Update time of heap */
  if (!IsVolatileHive(RegistryHive) && CmiGetBlock(RegistryHive, VBOffset, &pBin))
    {
      NtQuerySystemTime((PTIME) &pBin->DateModified);
    }

  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);
  ObDereferenceObject(KeyObject);

  CmiSyncHives();

  DPRINT("Return Status 0x%X\n", Status);

  return(Status);
}


NTSTATUS STDCALL
NtDeleteValueKey(IN HANDLE KeyHandle,
		 IN PUNICODE_STRING ValueName)
{
  PKEY_OBJECT KeyObject;
  NTSTATUS Status;

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
		KEY_QUERY_VALUE,
		CmiKeyType,
		UserMode,
		(PVOID *)&KeyObject,
		NULL);
  if (!NT_SUCCESS(Status))
    {
      return Status;
    }

  /* Acquire hive lock */
  ExAcquireResourceExclusiveLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  Status = CmiDeleteValueFromKey(KeyObject->RegistryHive,
				 KeyObject->KeyCell,
				 KeyObject->BlockOffset,
				 ValueName);

  /* Release hive lock */
  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);

  ObDereferenceObject(KeyObject);

  CmiSyncHives();

  return Status;
}

/*
 * NOTE:
 * KeyObjectAttributes->RootDirectory specifies the handle to the parent key and
 * KeyObjectAttributes->Name specifies the name of the key to load.
 */
NTSTATUS STDCALL
NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
	  IN POBJECT_ATTRIBUTES FileObjectAttributes)
{
  return NtLoadKey2(KeyObjectAttributes, FileObjectAttributes, 0);
}


/*
 * NOTE:
 * KeyObjectAttributes->RootDirectory specifies the handle to the parent key and
 * KeyObjectAttributes->Name specifies the name of the key to load.
 */
NTSTATUS STDCALL
NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
	   IN POBJECT_ATTRIBUTES FileObjectAttributes,
	   IN ULONG Flags)
{
  UNIMPLEMENTED;
  return STATUS_NOT_IMPLEMENTED;
}


NTSTATUS STDCALL
NtNotifyChangeKey(
	IN	HANDLE  KeyHandle,
	IN	HANDLE  Event,
	IN	PIO_APC_ROUTINE	 ApcRoutine  OPTIONAL,
	IN	PVOID  ApcContext  OPTIONAL,
	OUT	PIO_STATUS_BLOCK  IoStatusBlock,
	IN	ULONG  CompletionFilter,
	IN	BOOLEAN  Asynchroneous,
	OUT	PVOID  ChangeBuffer,
	IN	ULONG  Length,
	IN	BOOLEAN  WatchSubtree)
{
	UNIMPLEMENTED;
}


NTSTATUS STDCALL
NtQueryMultipleValueKey(IN HANDLE KeyHandle,
			IN OUT PKEY_VALUE_ENTRY ValueList,
			IN ULONG NumberOfValues,
			OUT PVOID Buffer,
			IN OUT PULONG Length,
			OUT PULONG ReturnLength)
{
  PREGISTRY_HIVE RegistryHive;
  PVALUE_CELL ValueCell;
  PKEY_OBJECT KeyObject;
  PDATA_CELL DataCell;
  ULONG BufferLength = 0;
  PKEY_CELL KeyCell;
  NTSTATUS Status;
  PUCHAR DataPtr;
  ULONG i;

  /* Verify that the handle is valid and is a registry key */
  Status = ObReferenceObjectByHandle(KeyHandle,
				     KEY_QUERY_VALUE,
				     CmiKeyType,
				     UserMode,
				     (PVOID *) &KeyObject,
				     NULL);
  if (!NT_SUCCESS(Status))
    {
      DPRINT("ObReferenceObjectByHandle() failed with status %x\n", Status);
      return(Status);
    }

  /* Acquire hive lock */
  ExAcquireResourceSharedLite(&KeyObject->RegistryHive->HiveResource, TRUE);

  VERIFY_KEY_OBJECT(KeyObject);

  /* Get pointer to KeyCell */
  KeyCell = KeyObject->KeyCell;
  RegistryHive = KeyObject->RegistryHive;

  DataPtr = (PUCHAR) Buffer;

  for (i = 0; i < NumberOfValues; i++)
    {
      DPRINT("ValueName: '%wZ'\n", ValueList[i].ValueName);

      /* Get Value block of interest */
      Status = CmiScanKeyForValue(RegistryHive,
			  KeyCell,
			  ValueList[i].ValueName,
			  &ValueCell,
			  NULL);

      if (!NT_SUCCESS(Status))
	{
	  DPRINT("CmiScanKeyForValue() failed with status %x\n", Status);
	  break;
	}
      else if (ValueCell == NULL)
	{
	  Status = STATUS_OBJECT_NAME_NOT_FOUND;
	  break;
	}

      BufferLength = (BufferLength + 3) & 0xfffffffc;

      if (BufferLength + (ValueCell->DataSize & LONG_MAX) <= *Length)
	{
	  DataPtr = (PUCHAR)(((ULONG)DataPtr + 3) & 0xfffffffc);

	  ValueList[i].Type = ValueCell->DataType;
	  ValueList[i].DataLength = ValueCell->DataSize & LONG_MAX;
	  ValueList[i].DataOffset = (ULONG) DataPtr - (ULONG) Buffer;

	  if (ValueCell->DataSize > 0)
	    {
	      DataCell = CmiGetBlock(RegistryHive,
				     ValueCell->DataOffset,
				     NULL);
	      RtlCopyMemory(DataPtr,
			    DataCell->Data,
			    ValueCell->DataSize & LONG_MAX);
	    }
	  else
	    {
	      RtlCopyMemory(DataPtr,
			    &ValueCell->DataOffset,
			    ValueCell->DataSize & LONG_MAX);
	    }

	  DataPtr += ValueCell->DataSize & LONG_MAX;
	}
      else
	{
	  Status = STATUS_BUFFER_TOO_SMALL;
	}

      BufferLength +=  ValueCell->DataSize & LONG_MAX;
    }

  if (NT_SUCCESS(Status))
    *Length = BufferLength;

  *ReturnLength = BufferLength;

  /* Release hive lock */
  ExReleaseResourceLite(&KeyObject->RegistryHive->HiveResource);

  ObDereferenceObject(KeyObject);

  DPRINT("Return Status 0x%X\n", Status);

  return(Status);
}


NTSTATUS STDCALL
NtReplaceKey(
	IN	POBJECT_ATTRIBUTES	ObjectAttributes,
	IN	HANDLE  Key,
	IN	POBJECT_ATTRIBUTES	ReplacedObjectAttributes
	)
{
	UNIMPLEMENTED;
}


NTSTATUS STDCALL
NtRestoreKey(
	IN	HANDLE	KeyHandle,
	IN	HANDLE	FileHandle,
	IN	ULONG	RestoreFlags
	)
{
	UNIMPLEMENTED;
}


NTSTATUS STDCALL
NtSaveKey(
	IN	HANDLE	KeyHandle,
	IN	HANDLE	FileHandle)
{
	UNIMPLEMENTED;
}


NTSTATUS STDCALL
NtSetInformationKey(
	IN	HANDLE	KeyHandle,
	IN	CINT	KeyInformationClass,
	IN	PVOID	 KeyInformation,
	IN	ULONG	 KeyInformationLength)
{
  UNIMPLEMENTED;
}


/*
 * NOTE:
 * KeyObjectAttributes->RootDirectory specifies the handle to the parent key and
 * KeyObjectAttributes->Name specifies the name of the key to unload.
 */
NTSTATUS STDCALL
NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
{
  UNIMPLEMENTED;
}


NTSTATUS STDCALL
NtInitializeRegistry(IN BOOLEAN SetUpBoot)
{
  NTSTATUS Status = STATUS_ACCESS_DENIED;

  if (CmiRegistryInitialized == FALSE)
    {
      /* FIXME: save boot log file */

      Status = CmiInitHives(SetUpBoot);

      CmiRegistryInitialized = TRUE;
    }

  return(Status);
}

/* EOF */
