/* $Id: object.c,v 1.63 2003/06/07 12:23:14 chorns Exp $
 * 
 * COPYRIGHT:     See COPYING in the top level directory
 * PROJECT:       ReactOS kernel
 * FILE:          ntoskrnl/ob/object.c
 * PURPOSE:       Implements generic object managment functions
 * PROGRAMMER:    David Welch (welch@cwcom.net)
 * UPDATE HISTORY:
 *               10/06/98: Created
 */

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

#define NTOS_MODE_KERNEL
#include <ntos.h>
#include <roscfg.h>
#include <internal/ob.h>
#include <internal/ps.h>
#include <internal/id.h>
#include <internal/ke.h>

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


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

PVOID HEADER_TO_BODY(POBJECT_HEADER obj)
{
   return(((void *)obj)+sizeof(OBJECT_HEADER)-sizeof(COMMON_BODY_HEADER));
}


POBJECT_HEADER BODY_TO_HEADER(PVOID body)
{
   PCOMMON_BODY_HEADER chdr = (PCOMMON_BODY_HEADER)body;
   return(CONTAINING_RECORD((&(chdr->Type)),OBJECT_HEADER,Type));
}


/**********************************************************************
 * NAME							PRIVATE
 * 	ObInitializeObject
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *
 * RETURN VALUE
 */
VOID ObInitializeObject(POBJECT_HEADER ObjectHeader,
			PHANDLE Handle,
			ACCESS_MASK DesiredAccess,
			POBJECT_TYPE Type,
			POBJECT_ATTRIBUTES ObjectAttributes)
{
   ObjectHeader->HandleCount = 0;
   ObjectHeader->RefCount = 1;
   ObjectHeader->ObjectType = Type;
   if (ObjectAttributes != NULL &&
       ObjectAttributes->Attributes & OBJ_PERMANENT)
     {
	ObjectHeader->Permanent = TRUE;
     }
   else
     {
	ObjectHeader->Permanent = FALSE;
     }
   RtlInitUnicodeString(&(ObjectHeader->Name),NULL);
   if (Handle != NULL)
     {
	ObCreateHandle(PsGetCurrentProcess(),
		       HEADER_TO_BODY(ObjectHeader),
		       DesiredAccess,
		       ObjectAttributes && (ObjectAttributes->Attributes & OBJ_INHERIT) ? TRUE : FALSE,
		       Handle);
     }
}


/**********************************************************************
 * NAME							PRIVATE
 * 	ObFindObject@16
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *	ObjectAttributes
 *
 *	ReturnedObject
 *
 *	RemainigPath
 *		Pointer to a unicode string that will contain the
 *		remaining path if the function returns successfully.
 *		The caller must free the buffer after use by calling
 *		RtlFreeUnicodeString ().
 *
 *	ObjectType
 *		Optional pointer to an object type. This is used to
 *		descide if a symbolic link object will be parsed or not.
 *
 * RETURN VALUE
 */
NTSTATUS ObFindObject(POBJECT_ATTRIBUTES ObjectAttributes,
		      PVOID* ReturnedObject,
		      PUNICODE_STRING RemainingPath,
		      POBJECT_TYPE ObjectType)
{
   PVOID NextObject;
   PVOID CurrentObject;
   PVOID RootObject;
   POBJECT_HEADER CurrentHeader;
   NTSTATUS Status;
   PWSTR Path;
   PWSTR current;
   UNICODE_STRING PathString;
   ULONG Attributes;
   
   DPRINT("ObFindObject(ObjectAttributes %x, ReturnedObject %x, "
	  "RemainingPath %x)\n",ObjectAttributes,ReturnedObject,RemainingPath);
   DPRINT("ObjectAttributes->ObjectName %wZ\n",
	  ObjectAttributes->ObjectName);

   RtlInitUnicodeString (RemainingPath, NULL);

   if (ObjectAttributes->RootDirectory == NULL)
     {
	ObReferenceObjectByPointer(NameSpaceRoot,
				   DIRECTORY_TRAVERSE,
				   NULL,
				   UserMode);
	CurrentObject = NameSpaceRoot;
     }
   else
     {
	Status = ObReferenceObjectByHandle(ObjectAttributes->RootDirectory,
					   DIRECTORY_TRAVERSE,
					   NULL,
					   UserMode,
					   &CurrentObject,
					   NULL);
	if (!NT_SUCCESS(Status))
	  {
	     return(Status);
	  }
     }
   
   Path = ObjectAttributes->ObjectName->Buffer;
   
   if (Path[0] == 0)
     {
	*ReturnedObject = CurrentObject;
	return(STATUS_SUCCESS);
     }
   
   if ((ObjectAttributes->RootDirectory == NULL) && (Path[0] != '\\'))
     {
	ObDereferenceObject(CurrentObject);
	return(STATUS_UNSUCCESSFUL);
     }
   
   if (Path)
     {
	RtlCreateUnicodeString (&PathString, Path);
	current = PathString.Buffer;
     }
   else
     {
	RtlInitUnicodeString (&PathString, NULL);
	current = NULL;
     }

   RootObject = CurrentObject;
   Attributes = ObjectAttributes->Attributes;
   if (ObjectType == ObSymbolicLinkType)
     Attributes |= OBJ_OPENLINK;

   while (TRUE)
     {
	DPRINT("current %S\n",current);
	CurrentHeader = BODY_TO_HEADER(CurrentObject);

	DPRINT("Current ObjectType %wZ\n",
	       &CurrentHeader->ObjectType->TypeName);

	if (CurrentHeader->ObjectType->Parse == NULL)
	  {
	     DPRINT("Current object can't parse\n");
	     break;
	  }
	Status = CurrentHeader->ObjectType->Parse(CurrentObject,
						  &NextObject,
						  &PathString,
						  &current,
						  Attributes);
	if (Status == STATUS_REPARSE)
	  {
	     /* reparse the object path */
	     NextObject = RootObject;
	     current = PathString.Buffer;
	     
	     ObReferenceObjectByPointer(NextObject,
					DIRECTORY_TRAVERSE,
					NULL,
					UserMode);
	  }

	if (NextObject == NULL)
	  {
	     break;
	  }
	ObDereferenceObject(CurrentObject);
	CurrentObject = NextObject;
     }
   
   if (current)
      RtlCreateUnicodeString (RemainingPath, current);
   RtlFreeUnicodeString (&PathString);
   *ReturnedObject = CurrentObject;
   
   return(STATUS_SUCCESS);
}


/**********************************************************************
 * NAME							EXPORTED
 * 	ObQueryNameString@16
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *
 * RETURN VALUE
 */
NTSTATUS STDCALL
ObQueryNameString (IN PVOID Object,
		   OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
		   IN ULONG Length,
		   OUT PULONG ReturnLength)
{
  POBJECT_NAME_INFORMATION LocalInfo;
  POBJECT_HEADER ObjectHeader;
  ULONG LocalReturnLength;
  NTSTATUS Status;

  *ReturnLength = 0;

  if (Length < sizeof(OBJECT_NAME_INFORMATION) + sizeof(WCHAR))
    return STATUS_INVALID_BUFFER_SIZE;

  ObjectNameInfo->Name.MaximumLength = Length - sizeof(OBJECT_NAME_INFORMATION);
  ObjectNameInfo->Name.Length = 0;
  ObjectNameInfo->Name.Buffer =
    (PWCHAR)((ULONG_PTR)ObjectNameInfo + sizeof(OBJECT_NAME_INFORMATION));
  ObjectNameInfo->Name.Buffer[0] = 0;

  ObjectHeader = BODY_TO_HEADER(Object);

  if (ObjectHeader->ObjectType != NULL &&
      ObjectHeader->ObjectType->QueryName != NULL)
    {
      DPRINT ("Calling %x\n", ObjectHeader->ObjectType->QueryName);
      Status = ObjectHeader->ObjectType->QueryName (Object,
						    ObjectNameInfo,
						    Length,
						    ReturnLength);
    }
  else if (ObjectHeader->Name.Length > 0 && ObjectHeader->Name.Buffer != NULL)
    {
      DPRINT ("Object does not have a 'QueryName' function\n");

      if (ObjectHeader->Parent == NameSpaceRoot)
	{
	  DPRINT ("Reached the root directory\n");
	  ObjectNameInfo->Name.Length = 0;
	  ObjectNameInfo->Name.Buffer[0] = 0;
	  Status = STATUS_SUCCESS;
	}
      else if (ObjectHeader->Parent != NULL)
	{
	  LocalInfo = ExAllocatePool (NonPagedPool,
				      sizeof(OBJECT_NAME_INFORMATION) +
				      MAX_PATH * sizeof(WCHAR));
	  if (LocalInfo == NULL)
	    return STATUS_INSUFFICIENT_RESOURCES;

	  Status = ObQueryNameString (ObjectHeader->Parent,
				      LocalInfo,
				      MAX_PATH * sizeof(WCHAR),
				      &LocalReturnLength);
	  if (!NT_SUCCESS (Status))
	    {
	      ExFreePool (LocalInfo);
	      return Status;
	    }

	  Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
						   &LocalInfo->Name);

	  ExFreePool (LocalInfo);

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

      DPRINT ("Object path %wZ\n", &ObjectHeader->Name);
      Status = RtlAppendUnicodeToString (&ObjectNameInfo->Name,
					 L"\\");
      if (!NT_SUCCESS (Status))
	return Status;

      Status = RtlAppendUnicodeStringToString (&ObjectNameInfo->Name,
					       &ObjectHeader->Name);
    }
  else
    {
      DPRINT ("Object is unnamed\n");

      ObjectNameInfo->Name.MaximumLength = 0;
      ObjectNameInfo->Name.Length = 0;
      ObjectNameInfo->Name.Buffer = NULL;

      Status = STATUS_SUCCESS;
    }

  if (NT_SUCCESS (Status))
    {
      ObjectNameInfo->Name.MaximumLength =
	(ObjectNameInfo->Name.Length) ? ObjectNameInfo->Name.Length + sizeof(WCHAR) : 0;
      *ReturnLength =
	sizeof(OBJECT_NAME_INFORMATION) + ObjectNameInfo->Name.MaximumLength;
      DPRINT ("Returned object path: %wZ\n", &ObjectNameInfo->Name);
    }

  return Status;
}


/**********************************************************************
 * NAME							EXPORTED
 * 	ObRosCreateObject@20
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *
 * NOTE
 *   Internal ReactOS function
 * RETURN VALUE
 */
NTSTATUS STDCALL
ObRosCreateObject (OUT PHANDLE Handle,
		IN ACCESS_MASK DesiredAccess,
		IN POBJECT_ATTRIBUTES ObjectAttributes,
		IN POBJECT_TYPE Type,
		OUT PVOID *Object)
{
  PVOID Parent = NULL;
  UNICODE_STRING RemainingPath;
  POBJECT_HEADER Header;
  POBJECT_HEADER ParentHeader = NULL;
  NTSTATUS Status;
  BOOLEAN ObjectAttached = FALSE;
  PWCHAR NamePtr;

  assert_irql(APC_LEVEL);

  DPRINT("ObRosCreateObject(Handle %x, ObjectAttributes %x, Type %x)\n",
	 Handle, ObjectAttributes, Type);

  if (ObjectAttributes != NULL &&
      ObjectAttributes->ObjectName != NULL &&
      ObjectAttributes->ObjectName->Buffer != NULL)
    {
      Status = ObFindObject(ObjectAttributes,
			    &Parent,
			    &RemainingPath,
			    NULL);
      if (!NT_SUCCESS(Status))
	{
	  DPRINT("ObFindObject() failed! (Status 0x%x)\n", Status);
	  return(Status);
	}
    }
  else
    {
      RtlInitUnicodeString(&RemainingPath, NULL);
    }

  RtlMapGenericMask(&DesiredAccess,
		    Type->Mapping);

  Header = (POBJECT_HEADER)ExAllocatePoolWithTag(NonPagedPool,
						 OBJECT_ALLOC_SIZE(Type),
						 Type->Tag);
  ObInitializeObject(Header,
		     NULL,
		     DesiredAccess,
		     Type,
		     ObjectAttributes);

  if (Parent != NULL)
    {
      ParentHeader = BODY_TO_HEADER(Parent);
    }

  if (ParentHeader != NULL &&
      ParentHeader->ObjectType == ObDirectoryType &&
      RemainingPath.Buffer != NULL)
    {
      NamePtr = RemainingPath.Buffer;
      if (*NamePtr == L'\\')
	NamePtr++;

      ObpAddEntryDirectory(Parent,
			   Header,
			   NamePtr);

      ObjectAttached = TRUE;
    }

  if ((Header->ObjectType != NULL) &&
      (Header->ObjectType->Create != NULL))
    {
      DPRINT("Calling %x\n", Header->ObjectType->Create);
      Status = Header->ObjectType->Create(HEADER_TO_BODY(Header),
					  Parent,
					  RemainingPath.Buffer,
					  ObjectAttributes);
      if (!NT_SUCCESS(Status))
	{
	  if (ObjectAttached == TRUE)
	    {
	      ObpRemoveEntryDirectory(Header);
	    }
	  if (Parent)
	    {
	      ObDereferenceObject(Parent);
	    }
	  RtlFreeUnicodeString(&Header->Name);
	  RtlFreeUnicodeString(&RemainingPath);
	  ExFreePool(Header);
	  return(Status);
	}
    }
  RtlFreeUnicodeString( &RemainingPath );

  if (Object != NULL)
    {
      *Object = HEADER_TO_BODY(Header);
    }

  if (Handle != NULL)
  {
     ObCreateHandle(PsGetCurrentProcess(),
		    *Object,
		    DesiredAccess,
		    ObjectAttributes && (ObjectAttributes->Attributes & OBJ_INHERIT) ? TRUE : FALSE,
		    Handle);
  }

  return(STATUS_SUCCESS);
}

/**********************************************************************
 * NAME							EXPORTED
 * 	ObCreateObject@36
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *
 * RETURN VALUE
 */
NTSTATUS STDCALL
ObCreateObject (IN KPROCESSOR_MODE      ObjectAttributesAccessMode OPTIONAL,
  IN POBJECT_TYPE         ObjectType,
  IN POBJECT_ATTRIBUTES   ObjectAttributes OPTIONAL,
  IN KPROCESSOR_MODE      AccessMode,
  IN OUT PVOID            ParseContext OPTIONAL,
  IN ULONG                ObjectSize,
  IN ULONG                PagedPoolCharge OPTIONAL,
  IN ULONG                NonPagedPoolCharge OPTIONAL,
  OUT PVOID               *Object)
{
  UNIMPLEMENTED
  return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS STDCALL
ObReferenceObjectByPointer(IN PVOID Object,
			   IN ACCESS_MASK DesiredAccess,
			   IN POBJECT_TYPE ObjectType,
			   IN KPROCESSOR_MODE AccessMode)
/*
 * FUNCTION: Increments the pointer reference count for a given object
 * ARGUMENTS:
 *         ObjectBody = Object's body
 *         DesiredAccess = Desired access to the object
 *         ObjectType = Points to the object type structure
 *         AccessMode = Type of access check to perform
 * RETURNS: Status
 */
{
   POBJECT_HEADER Header;

//   DPRINT("ObReferenceObjectByPointer(Object %x, ObjectType %x)\n",
//	  Object,ObjectType);
   
   Header = BODY_TO_HEADER(Object);
   
   if (ObjectType != NULL && Header->ObjectType != ObjectType)
     {
	DPRINT("Failed %x (type was %x %S) should be %x %S\n",
		Header,
		Header->ObjectType,
		Header->ObjectType->TypeName.Buffer,
		ObjectType,
    ObjectType->TypeName.Buffer);
	return(STATUS_UNSUCCESSFUL);
     }
   if (Header->ObjectType == PsProcessType)
     {
	DPRINT("Ref p 0x%x refcount %d type %x ",
		Object, Header->RefCount, PsProcessType);
	DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
     }
   if (Header->ObjectType == PsThreadType)
     {
	DPRINT("Deref t 0x%x with refcount %d type %x ",
		Object, Header->RefCount, PsThreadType);
	DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
     }
 
   if (Header->CloseInProcess)
   {
      return(STATUS_UNSUCCESSFUL);
   }

   InterlockedIncrement(&Header->RefCount);
   
   return(STATUS_SUCCESS);
}


NTSTATUS STDCALL
ObOpenObjectByPointer(IN POBJECT Object,
		      IN ULONG HandleAttributes,
		      IN PACCESS_STATE PassedAccessState,
		      IN ACCESS_MASK DesiredAccess,
		      IN POBJECT_TYPE ObjectType,
		      IN KPROCESSOR_MODE AccessMode,
		      OUT PHANDLE Handle)
{
   NTSTATUS Status;
   
   DPRINT("ObOpenObjectByPointer()\n");
   
   Status = ObReferenceObjectByPointer(Object,
				       0,
				       ObjectType,
				       AccessMode);
   if (!NT_SUCCESS(Status))
     {
	return Status;
     }
   
   Status = ObCreateHandle(PsGetCurrentProcess(),
			   Object,
			   DesiredAccess,
			   HandleAttributes & OBJ_INHERIT,
			   Handle);
   
   ObDereferenceObject(Object);
   
   return STATUS_SUCCESS;
}


static NTSTATUS
ObpPerformRetentionChecks(POBJECT_HEADER Header)
{
//  DPRINT("ObPerformRetentionChecks(Header %x), RefCount %d, HandleCount %d\n",
//	  Header,Header->RefCount,Header->HandleCount);
  
  if (Header->RefCount < 0)
    {
      CPRINT("Object %x/%x has invalid reference count (%d)\n",
	     Header, HEADER_TO_BODY(Header), Header->RefCount);
      KeBugCheck(0);
    }
  if (Header->HandleCount < 0)
    {
      CPRINT("Object %x/%x has invalid handle count (%d)\n",
	     Header, HEADER_TO_BODY(Header), Header->HandleCount);
      KeBugCheck(0);
    }
  
  if (Header->RefCount == 0 &&
      Header->HandleCount == 0 &&
      Header->Permanent == FALSE)
    {
      if (Header->CloseInProcess)
      {
	 KeBugCheck(0);
	 return STATUS_UNSUCCESSFUL;
      }
      Header->CloseInProcess = TRUE;
      if (Header->ObjectType != NULL &&
	  Header->ObjectType->Delete != NULL)
	{
	  Header->ObjectType->Delete(HEADER_TO_BODY(Header));
	}
      if (Header->Name.Buffer != NULL)
	{
	  ObpRemoveEntryDirectory(Header);
	  RtlFreeUnicodeString(&Header->Name);
	}
      DPRINT("ObPerformRetentionChecks() = Freeing object\n");
      ExFreePool(Header);
    }
  return(STATUS_SUCCESS);
}


/**********************************************************************
 * NAME							EXPORTED
 * 	ObfReferenceObject@4
 *
 * DESCRIPTION
 *	Increments a given object's reference count and performs
 *	retention checks.
 *
 * ARGUMENTS
 *  ObjectBody = Body of the object.
 *
 * RETURN VALUE
 * 	None.
 */
VOID FASTCALL
ObfReferenceObject(IN PVOID Object)
{
  POBJECT_HEADER Header;

  assert(Object);

  Header = BODY_TO_HEADER(Object);

  if (Header->CloseInProcess)
  {
      KeBugCheck(0);
  }
  InterlockedIncrement(&Header->RefCount);

  ObpPerformRetentionChecks(Header);
}


/**********************************************************************
 * NAME							EXPORTED
 *	ObfDereferenceObject@4
 *
 * DESCRIPTION
 *	Decrements a given object's reference count and performs
 *	retention checks.
 *
 * ARGUMENTS
 *	ObjectBody = Body of the object.
 *
 * RETURN VALUE
 * 	None.
 */
VOID FASTCALL
ObfDereferenceObject(IN PVOID Object)
{
  POBJECT_HEADER Header;
  extern POBJECT_TYPE PsProcessType;

  assert(Object);

  Header = BODY_TO_HEADER(Object);

  if (Header->ObjectType == PsProcessType)
    {
      DPRINT("Deref p 0x%x with refcount %d type %x ",
	     Object, Header->RefCount, PsProcessType);
      DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
    }
  if (Header->ObjectType == PsThreadType)
    {
      DPRINT("Deref t 0x%x with refcount %d type %x ",
	     Object, Header->RefCount, PsThreadType);
      DPRINT("eip %x\n", ((PULONG)&Object)[-1]);
    }
  
  InterlockedDecrement(&Header->RefCount);
  
  ObpPerformRetentionChecks(Header);
}


/**********************************************************************
 * NAME							EXPORTED
 *	ObGetObjectPointerCount@4
 *
 * DESCRIPTION
 *	Retrieves the pointer(reference) count of the given object.
 *
 * ARGUMENTS
 *	ObjectBody = Body of the object.
 *
 * RETURN VALUE
 * 	Reference count.
 */
ULONG STDCALL
ObGetObjectPointerCount(PVOID Object)
{
  POBJECT_HEADER Header;

  assert(Object);
  Header = BODY_TO_HEADER(Object);

  return(Header->RefCount);
}


/**********************************************************************
 * NAME							INTERNAL
 *	ObGetObjectHandleCount@4
 *
 * DESCRIPTION
 *	Retrieves the handle count of the given object.
 *
 * ARGUMENTS
 *	ObjectBody = Body of the object.
 *
 * RETURN VALUE
 * 	Reference count.
 */
ULONG
ObGetObjectHandleCount(PVOID Object)
{
  POBJECT_HEADER Header;

  assert(Object);
  Header = BODY_TO_HEADER(Object);

  return(Header->HandleCount);
}

/* EOF */
