
/**
 * @file: object.c
 *
 * Tento soubor obsahuje rutiny frameworku pro praci s objekty jadra na nejvyssi urovni.
 * Tzto rutiny jsou nezavisle na veryi operacniho systemu a chovaji se k datovym strukturam
 * jadra podobnym zpusobem jako jadro samotne, tedy zajistuji zamykani objektu, je-li
 * potreba.
 */

#include <ntddk.h>
#include "driver.h"
#include "version.h"
#include "pushlock.h"
#include "object_type.h"
#include "object_header.h"
#include "object_directory.h"
#include "object.h"

// Pocet zamku na zamykani objektu v objektu typu. Pouziva se pri zamykani jednotlivych
// objektu na Windows XP, Windows Vista a prislusnych serverovych verzich. Nastavuje se
// pri inicializaci tohoto souboru.
static ULONG LockCount = 0;

// Cislo verze operacniho systemu. Nastavuje se pri inicializaci tohoto souboru.
static ULONG VersionCode = VERSION_UNKNOWN;

/** Zamkne objekt pro sdileny pristup.
 *
 * Rutina pouziva ruzne mechanismy vzhledem k verzi operacniho systemu, na ktere kod
 * bezi. Windows XP pouzivaji Executive Resources v objektu typu, Windows Vista pushlocky
 * v objektu typu a windows 7 pushlocky v hlavicce objekt.u. Windows 2000 nejsou reseny.
 *
 * @param Object Adresa tela objektu, ktery ma byt zamcen.
 *
 * @return Vrati TRUE v pripade uspesneho zamceni objektu. Tato metoda by nikdy nemela
 * selhat.
 */
BOOLEAN OBLockShared(PVOID Object)
{
  BOOLEAN Ret = FALSE;
  ULONG Index = 0;
  PVOID *PushLock = NULL;
  PVOID ObjectHeader = NULL;
  PVOID ObjectType = NULL;
  PERESOURCE EResources = NULL;
  PVOID *PushLocks = NULL;
  KdPrint(("object.c: OBLockShared(Object=0x%p)\n", Object));

  ObjectHeader = OHGetObjectHeader(Object);
  Ret = (ObjectHeader != NULL);
  if (Ret) {
    ObjectType = OHGetObjectType(ObjectHeader);
    switch (VersionCode) {
      case VERSION_WXP:
        EResources = (PERESOURCE)OTWXPObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		ExAcquireResourceSharedLite(&EResources[Index], TRUE);
		break;
      case VERSION_W2K3:
        EResources = (PERESOURCE)OTW2K3ObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		ExAcquireResourceSharedLite(&EResources[Index], TRUE);
		break;
      case VERSION_VISTA:
        PushLocks = (PVOID *)OTVistaObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		PushLockAcquireShared(&PushLocks[Index]);
		break;
	  case VERSION_W7:
        PushLock = OHW7GetLock(ObjectHeader);
        KeEnterCriticalRegion();
		PushLockAcquireShared(PushLock);
		break;
	}
  } else KdPrint(("object.c: OBLockShared(): Failed to get object header for 0x%p\n", Object));

  KdPrint(("object.c: OBLockShared(-):%u\n", Ret));
  return Ret;
}


/** Zamkne objekt pro exkluzivni pristup.
 *
 * Rutina pouziva ruzne mechanismy vzhledem k verzi operacniho systemu, na ktere kod
 * bezi. Windows XP pouzivaji Executive Resources v objektu typu, Windows Vista pushlocky
 * v objektu typu a windows 7 pushlocky v hlavicce objekt.u. Windows 2000 nejsou reseny.
 *
 * @param Object Adresa tela objektu, ktery ma byt zamcen.
 *
 * @return Vrati TRUE v pripade uspesneho zamceni objektu. Tato metoda by nikdy nemela
 * selhat.
 */
BOOLEAN OBLockExclusive(PVOID Object)
{
  BOOLEAN Ret = FALSE;
  ULONG Index = 0;
  PVOID *PushLock = NULL;
  PVOID ObjectHeader = NULL;
  PVOID ObjectType = NULL;
  PERESOURCE EResources = NULL;
  PVOID *PushLocks = NULL;
  KdPrint(("object.c: OBLockExclusive(Object=0x%p)\n", Object));

  ObjectHeader = OHGetObjectHeader(Object);
  Ret = (ObjectHeader != NULL);
  if (Ret) {
    ObjectType = OHGetObjectType(ObjectHeader);
    switch (VersionCode) {
      case VERSION_WXP:
        EResources = (PERESOURCE)OTWXPObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		ExAcquireResourceExclusiveLite(&EResources[Index], TRUE);
		break;
      case VERSION_W2K3:
        EResources = (PERESOURCE)OTW2K3ObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		ExAcquireResourceExclusiveLite(&EResources[Index], TRUE);
		break;
      case VERSION_VISTA:
        PushLocks = (PVOID *)OTVistaObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		KeEnterCriticalRegion();
		PushLockAcquireExclusive(&PushLocks[Index]);
		break;
	  case VERSION_W7:
        PushLock = OHW7GetLock(ObjectHeader);
        KeEnterCriticalRegion();
		PushLockAcquireExclusive(PushLock);
		break;
	}
  } else KdPrint(("object.c: OBLockExclusive(): Failed to get object header for 0x%p\n", Object));

  KdPrint(("object.c: OBLockExclusive(-):%u\n", Ret));
  return Ret;
}


/** Odemkne objekt.
 *
 * Rutina pouziva ruzne mechanismy vzhledem k verzi operacniho systemu, na ktere kod
 * bezi. Windows XP pouzivaji Executive Resources v objektu typu, Windows Vista pushlocky
 * v objektu typu a windows 7 pushlocky v hlavicce objekt.u. Windows 2000 nejsou reseny.
 *
 * @param Object Adresa tela objektu, ktery ma byt odemcen.
 *
 * @return Vrati TRUE v pripade uspesneho odemceni objektu. Tato metoda by nikdy nemela
 * selhat.
 */
BOOLEAN OBUnlock(PVOID Object)
{
  BOOLEAN Ret = FALSE;
  ULONG Index = 0;
  PVOID *PushLock = NULL;
  PVOID ObjectHeader = NULL;
  PVOID ObjectType = NULL;
  PERESOURCE EResources = NULL;
  PVOID *PushLocks = NULL;
  KdPrint(("object.c: OBUnlock(Object=0x%p)\n", Object));

  ObjectHeader = OHGetObjectHeader(Object);
  Ret = (ObjectHeader != NULL);
  if (Ret) {
    ObjectType = OHGetObjectType(ObjectHeader);
    switch (VersionCode) {
      case VERSION_WXP:
        EResources = (PERESOURCE)OTWXPObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
        ExReleaseResourceLite(&EResources[Index]);
        KeLeaveCriticalRegion();
		break;
      case VERSION_W2K3:
        EResources = (PERESOURCE)OTW2K3ObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		ExReleaseResourceLite(&EResources[Index]);
		KeLeaveCriticalRegion();
		break;
      case VERSION_VISTA:
        PushLocks = (PVOID *)OTVistaObjectLocks(ObjectType);
        Index = (((ULONG_PTR)ObjectHeader) >> 8) & (LockCount - 1);
		PushLockRelease(&PushLocks[Index]);
        KeLeaveCriticalRegion();
		break;
	  case VERSION_W7:
        PushLock = OHW7GetLock(ObjectHeader);
		PushLockRelease(PushLock);
        KeLeaveCriticalRegion();
		break;
	}
  } else KdPrint(("object.c: OBUnlock(): Failed to get object header for 0x%p\n", Object));

  KdPrint(("object.c: OBUnlock(-):%u\n", Ret));
  return Ret;
}


/** Zjisti jmeno zadaneho objektu.
 *
 * Nejedna se o plne jmeno, pouze o cast jmena v hlavicce objektu. 
 *
 * Rutina pri praci s objektme provadi zamykani.
 *
 * @param Object Adresa tela objektu, jehoz jmeno si volajici preje zjistit.
 * @param Name Struktura UNICODE_STRING, ktera pri uspesnem vykonani funkce bude obsahovat
 * jmeno objektu. Pokud objekt zadne jmeno nema, cela struktura je vyplnena nulami. Pamet
 * pro ulozeni jmena je alokovana ze strankovaneho fondu a volajici ji musi uvolnit, jakmile
 * jiz jmeno nepotrebuje.
 *
 * @return Vrati hodnotu NTSTATUS indikujici uspech ci neuspech funkce. 
 */
NTSTATUS OBGetObjectName(PVOID Object, PUNICODE_STRING Name)
{
  PVOID NameInfo = NULL;
  PVOID Header = NULL;
  PUNICODE_STRING Tmp = NULL;
  PWCHAR TmpBuf = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("object.c: OBGetObjectName(Object=0x%p; Name=0x%p)\n", Object, Name));

  RtlZeroMemory(Name, sizeof(UNICODE_STRING));
  OBLockShared(Object);
  Header = OHGetObjectHeader(Object);
  if (Header != NULL) {
    NameInfo = OHGetInfoHeader(Header, OH_HEADER_NAME_INFO);
	if (NameInfo != NULL) {
	  Tmp = OHNameInfoGetName(NameInfo);
	} else Status = STATUS_SUCCESS;
  }

  if (Tmp != NULL) {
    RtlCopyMemory(Name, Tmp, sizeof(UNICODE_STRING));

    TmpBuf = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, Name->Length + sizeof(WCHAR), POOL_TAG);
    if (TmpBuf != NULL) {
      RtlZeroMemory(TmpBuf, Name->Length + sizeof(WCHAR));
      RtlCopyMemory(TmpBuf, Name->Buffer, Name->Length);
      Name->MaximumLength = Name->Length;
      Name->Buffer = TmpBuf;
      Status = STATUS_SUCCESS;
    } else {
      RtlZeroMemory(Name, sizeof(UNICODE_STRING));
      Status = STATUS_INSUFFICIENT_RESOURCES;
    }
  }

  OBUnlock(Object);

  KdPrint(("object.c: OBGetObjectName(-):0x%x\n", Status));
  return Status;
}


/** Vrati dulezite informace z hlavicky OBJECT_HEADER_NAME_INFO.
 *
 * Rutina vraci informace o jmenu objektu a adrese objektu adresare, do ktereho 
 * objekt patri. Jmeno je zkopirovano do nove alokovane pameti ze strankovaneho fondu.
 * Pocet referenci na objekt adresare je zvysen o jednicku. Cely objekt, jehoz udaje se
 * funkce snazi ziskat, je na nutnou dobu uzamcen.
 *
 * @param Object Adresa tela objektu, jehoz jmeno a adrwsar chce volajici znat.
 * @param NameInfo Tato struktura v pripade uspechu rutiny obsahuje jmeno a adresar. V
 * pripade neuspechu je vyplnena nulami.
 *
 * @return Vraci hodnotu NTSTATUS, ktera indikuje uspech operace.
 */
NTSTATUS OBGetNameInfo(PVOID Object, POBJECT_NAME_INFO NameInfo)
{
  PWCHAR TmpBuf = NULL;
  PVOID ObjectHeader = NULL;
  PVOID ObjectNameInfo = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("object.c: OBGetnameInfo(Object=0x%p; NameInfo=0x%p)\n", Object, NameInfo));

  RtlZeroMemory(NameInfo, sizeof(OBJECT_NAME_INFO));
  OBLockShared(Object);
  ObjectHeader = OHGetObjectHeader(Object);
  if (ObjectHeader != NULL) {
    Status = STATUS_SUCCESS;
    ObjectNameInfo = OHGetInfoHeader(ObjectHeader, OH_HEADER_NAME_INFO);
	if (ObjectNameInfo != NULL) {
      NameInfo->Directory = OHNameInfoGetDirectory(ObjectNameInfo);
	  if (NameInfo->Directory != NULL)
	    ObReferenceObject(NameInfo->Directory);

	  NameInfo->Name = *OHNameInfoGetName(ObjectNameInfo);
	  NameInfo->Name.MaximumLength = NameInfo->Name.Length;
	  if (NameInfo->Name.Length > 0) {
	    TmpBuf = (PWCHAR)ExAllocatePoolWithTag(PagedPool, NameInfo->Name.Length + sizeof(WCHAR), POOL_TAG);
	    if (TmpBuf != NULL) {
          RtlCopyMemory(TmpBuf, NameInfo->Name.Buffer, NameInfo->Name.Length);
		  NameInfo->Name.Buffer = TmpBuf;
	    } else {
          Status = STATUS_INSUFFICIENT_RESOURCES;
		  if (NameInfo->Directory != NULL)
		    ObDereferenceObject(NameInfo->Directory);

          RtlZeroMemory(NameInfo, sizeof(OBJECT_NAME_INFO));
		}
	  } else NameInfo->Name.Buffer = NULL;
	}
  }

  OBUnlock(Object);

  KdPrint(("object.c: OBGetNameInfo(-):0x%x\n", Status));
  return Status;
}


/** Uvolni struktur OBJECT_NAME_INFO uspesne vyplnenou volanim funkce OBGetNameInfo.
 *
 * Rutina snizi pocet referenci na objekt adresare (je-li jeho adresa platna) o jednicku
 * a uvolni pamet potrebnou na ulozeni jmena objektu. Dale celou strukturu vynuluje.
 *
 * @param NameInfo Struktura OBJECT_NAME_INFO, kterou si volajici preje uvolnit.
 */
VOID OBReleaseNameInfo(POBJECT_NAME_INFO NameInfo)
{
  KdPrint(("object.c: OBReleaseNameInfo(NameInfo=0x%p)\n", NameInfo));

  if (NameInfo->Directory != NULL)
    ObDereferenceObject(NameInfo->Directory);

  if (NameInfo->Name.Length > 0)
    ExFreePoolWithTag(NameInfo->Name.Buffer, POOL_TAG);

  RtlZeroMemory(NameInfo, sizeof(OBJECT_NAME_INFO));

  KdPrint(("object.c: OBReleaseNameInfo(-)\n"));
  return;
}



/** Zjisti adresu objektu typu pro dany objekt.
 *
 * Funkce zamyka cilovy objekt pro sdileny pristup. Pocet referenci nalezeneho objektu
 * typu je zvysen o jednicku. Rutina by nemela nikdy selhat.
 *
 * @param Object Adresa tela objektu, jehoz typ si volajici preje zjistit.
 * 
 * @return Adresa tela prislusneho objektu typu. V pripade neuspechu funkce vraci NULL.
 */
POBJECT_TYPE OBGetObjectType(PVOID Object)
{
  PVOID Header = NULL;
  POBJECT_TYPE Ret = NULL;
  KdPrint(("object.c: OBGetObjectType(Object=0x%p)\n", Object));

  OBLockShared(Object);
  Header = OHGetObjectHeader(Object);
  if (Header != NULL) {
    Ret = OHGetObjectType(Header);
    if (Ret != NULL)
      ObReferenceObject(Ret);
  }

  OBUnlock(Object);

  KdPrint(("object.c: OBGetObjectType(-):0x%p", Ret));
  return Ret;
}



/** Zjisti, zda v zadanem adresari existuje objekt daneho jmena. Pokud ano, vrati adresu
 *  jeho tela.
 *
 * Rutina na dobu sve prace zamyka objekt adresare pro sdileny pristup. Jmena jednotlivych
 * polozek jsou zjistovana pres funkci OBGetObjectName, ktera se k danym objektum tez chova
 * hezky z hlediska zamykani. Pokud objekt daneho jmena v adresari existuje, rutina zvysi
 * jeho pocet referenci o jednicku a vrati adresu jeho tela.
 *
 * @param Directory Adresa tela adresare, ktery si volajici preje prohledat.
 * @param Name Struktura UNICODE_STRING se jmenem hledaneho objektu.
 *
 * @return Vraci adresu tela hledaneho objektu. Pokud takovy objekt neexistuje, funkce vraci
 * NULL.
 */
PVOID OBLocalNameToPointer(PVOID Directory, PUNICODE_STRING Name)
{
  LONG i = 0;
  LONG CompareResult = 0;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  UNICODE_STRING LocalName;
  PVOID Ret = NULL;
  PDIRECTORY_ENTRY Tmp = NULL;
  PDIRECTORY_ENTRY *Buckets = NULL;
  KdPrint(("object.c: OBLocalNameToPointer(Directory=0x%p; Name=%S)\n", Directory, Name->Buffer));

  DODirObjectLockShared(Directory);
  Buckets = DOObjectHashBuckets(Directory);
  for (i = 0; i < 37; ++i) {
    Tmp = Buckets[i];
	while (Tmp != NULL) {
      Status = OBGetObjectName(Tmp->Object, &LocalName);
	  if (NT_SUCCESS(Status)) {
	    CompareResult = RtlCompareUnicodeString(Name, &LocalName, TRUE);
		ExFreePoolWithTag(LocalName.Buffer, POOL_TAG);
		if (CompareResult == 0) {
          Ret = Tmp->Object;
		  ObReferenceObject(Ret);
		  break;
		}
	  }

      Tmp = Tmp->Next;
	}

	if (Ret != NULL)
      break;
  }

  DODirObjectUnlockShared(Directory);

  KdPrint(("object.c: OBLocalNameToPointer(-):0x%p\n", Ret));
  return Ret;
}


/** Prelozi plne jmeno objektu (jmeno vcetne cesty od korenoveho adresare) na adresu tela.
 *
 * Rutina pri sve cinnosti postupne zamyka jednotlive adresare, jak nimi prochazi, a zvysuje
 * prislusne pocet jejich referenci tak, aby je system nemohl behem jejiho vykonavani odstranit.
 * Pokuc je objekt zadaneho plneho jmena nalezen, rutina pocet jeho referenci zvysi o jednicku a
 * vrati adresu jeho tela.
 *
 * @param ObjectName Plne jmeno (vcetne cesty) objektu, jehoz adresu chce volajici ziskat.
 *
 * @return Vraci adresu tela objektu, pokud objekt daneho jmena existuje. V opacnem pripade
 * a v pripade neuspechu vraci NULL.
 */
PVOID OBObjectNameToPointer(PUNICODE_STRING ObjectName)
{
  POBJECT_TYPE TypeObject = NULL;
  PVOID Ret = NULL;
  PWCHAR AktPos = NULL;
  PVOID AktDir = NULL;
  WCHAR Tmp [256];
  LONG i = 0;
  PVOID LocalObject = NULL;
  UNICODE_STRING LocalName;
  KdPrint(("object.c: OBObjectNameToPointer(ObjectName=%S)\n", ObjectName->Buffer));

  if (ObjectName->Buffer[0] == L'\\') {
    AktDir = DORootDirectory();
	ObReferenceObject(AktDir);
    AktPos = &ObjectName->Buffer[1];
	if (ObjectName->Length == sizeof(WCHAR))
      Ret = AktDir;

	while (*AktPos != L'\0') {
      i = 0;
      RtlZeroMemory(&Tmp, sizeof(Tmp));
      while (*AktPos != L'\0' && *AktPos != L'\\') {
	    Tmp[i] = *AktPos;
		++i;
		++AktPos;
	  }

      RtlInitUnicodeString(&LocalName, (PWCHAR)&Tmp);
      LocalObject = OBLocalNameToPointer(AktDir, &LocalName);
      if (LocalObject != NULL) {
	    if (*AktPos == L'\\') {
          ObDereferenceObject(AktDir);
		  if (!DOIsDirectory(LocalObject)) {
            OBReleaseObjectPointer(LocalObject);
			break;
		  } else AktDir = LocalObject;
	    } else {
          ASSERT(*AktPos == L'\0');
		  Ret = LocalObject;
		  break;
		}
	  }

	  ++AktPos;
	}
  }

  KdPrint(("object.c: OBObjectNameToPointer(-):0x%p\n", Ret));
  return Ret;
}


/** Snizi pocet referenci na dany objekt o jednicku.
 * 
 * Rutina pouze vola funkci ObDereferenceObject, neprovani zadnou jinou praci.
 *
 * @param Object Adresa tela objektu, jehoz pocet referenci chce volajici snizit.
 */
VOID OBReleaseObjectPointer(PVOID Object)
{
  ObDereferenceObject(Object);

  return;
}



/** Vrati veskere informace, ktere lze z hlavicky objektu ziskat.
 *
 * Behem ziskavani informaci z jeho hlavicek je objekt zamcen pro sdileny pristup. Dalsi
 * objekty, na ktere jeho hlavicky pripadne ukazuji (objekt typu) nejsou nijak modifikovany
 * ani co se tyce manipulace s poctem referenci. Pokud ma cilovy objekt jmeno, toto jmeno je
 * prekopirovano do strankovane pameti, tudiz po skonceni prace s udaji vracenymi touto 
 * funkci je volajici povinen tuto pamet uvolnit.
 *
 * @param Object Adresa tela objektu, jehoz udaje chce volajici zjistit.
 * @param ObjectInfo Adresa struktury OBJECT_HEADER_INFO, kam rutina v pripade uspechu
 * zapise vsechny nasbirane udaje.
 *
 * @return Funkce vraci hodnotu NTSTATUS indikujici uspech operace.
 */
NTSTATUS OBGetObjectHeaderInfo(PVOID Object, POBJECT_HEADER_INFO ObjectInfo)
{
  PWCHAR Tmp = NULL;
  PVOID InfoHeader = 0;
  PVOID Header = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("object.c: OBGetObjectHeaderInfo(Object=0x%p; ObjectInfo=0x%p)\n", Object, ObjectInfo));

  Header = OHGetObjectHeader(Object);
  if (Header != NULL) {
    if (OBLockShared(Object)) {
      Status = STATUS_SUCCESS;
      RtlZeroMemory(ObjectInfo, sizeof(OBJECT_HEADER_INFO));
	  ObjectInfo->InfoMask = OHGetInfoMask(Header);
	  ObjectInfo->HandleInfoOffset = OHGetHandleInfoOffset(Header);
	  ObjectInfo->NameInfoOffset = OHGetNameInfoOffset(Header);
	  ObjectInfo->QuotaInfoOffset = OHGetQuotaInfoOffset(Header);
	  ObjectInfo->CreatorInfoOffset = OHGetCreatorInfoOffset(Header);
	  ObjectInfo->ProcessInfoOffset = OHGetProcessInfoOffset(Header);
	  ObjectInfo->PointerCount = OHGetPointerCount(Header);
      ObjectInfo->HandleCount = OHGetHandleCount(Header);
      ObjectInfo->Flags = OHGetFlags(Header);
      ObjectInfo->Type = OHGetObjectType(Header);
      ObjectInfo->SecurityDescriptor = OHGetSecurityDescriptor(Header);

      InfoHeader = OHGetInfoHeader(Header, OH_HEADER_NAME_INFO);
	  if (InfoHeader != NULL) {
        ObjectInfo->Directory = OHNameInfoGetDirectory(InfoHeader);
        ObjectInfo->Name = *OHNameInfoGetName(InfoHeader);
        Tmp = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, ObjectInfo->Name.Length, POOL_TAG);
        if (Tmp != NULL) {
          RtlCopyMemory(Tmp, ObjectInfo->Name.Buffer, ObjectInfo->Name.Length);
          ObjectInfo->Name.Buffer = Tmp;
        } else {
          Status = STATUS_INSUFFICIENT_RESOURCES;
		  RtlZeroMemory(&ObjectInfo->Name, sizeof(UNICODE_STRING));
		}
      }

      InfoHeader = OHGetInfoHeader(Header, OH_HEADER_QUOTA_INFO);
      if (InfoHeader != NULL) {
        ObjectInfo->PagedPoolCharge = OHQuotaInfoGetPagedPoolCharge(InfoHeader);
        ObjectInfo->NonPagedPoolCharge = OHQuotaInfoGetNonPagedPoolCharge(InfoHeader);
        ObjectInfo->SecurityDescriptorCharge = OHQuotaInfoGetSecurityDescriptorCharge(InfoHeader);
      }

      InfoHeader = OHGetInfoHeader(Header, OH_HEADER_CREATOR_INFO);
      if (InfoHeader != NULL)
        ObjectInfo->CreatorUniqueProcessId = OHCreatorInfoGetCreatorUniqueProcessId(InfoHeader);

      ObjectInfo->ExclusiveProcess = OHGetEcxlusiveProcess(Header);
	  
	  OBUnlock(Object);
	} else Status = STATUS_UNSUCCESSFUL;
  }

  KdPrint(("object.c: OBGetObjectHeaderInfo(-):0x%x\n", Status));
  return Status;
}



/** Zjisti, zda je cilovy objekt adresarem.
 *
 * Pri zjistovani teto informace neni provadeno zadne zamykani. Ani to neni treba, protoze
 * se predpoklada (jako ostatne ve vsech funkcich frameworku), ze volajici drzi na cilovy 
 * objekt referenci. A informace o typu objektu se za jeho zivota nemeni.
 *
 * @param Object Adresa tela objektu, ktery si preje volajici testovat.
 *
 * @return Vraci TRUE, pokud je cilovy objekt adresarem a FALSE v opacnem pripade.
 */
BOOLEAN OBIsObjectDirectory(PVOID Object)
{
  BOOLEAN Ret = FALSE;
  KdPrint(("object.c: OBIsObjectDirectory(Object=0x%p)\n", Object));

  Ret = DOIsDirectory(Object);

  KdPrint(("object.c: OBIsObjectDirectory(-):%u\n", Ret));
  return Ret;
}



/** Predradi obsah retezce Source pred obsah retezce Dest. Vysledek zapise do retezce Dest.
 *
 * Funkce predpoklada pouziti pro skladani plne cesty k urcitemu objektu, takze mezi fragmenty
 * ulozene v parametrech Dest a Source vklada zpetne lomitko. Reteyec v parametru Dest je prekopirovan
 * do nove alokovane oblasti strankovane pameti, ktera je dost velka na to, aby pojala cely vysledny retezec.
 * Nasledne je pred retezec Dest predrazen retezec Source. Pamet zabirana puvodnim retezcem Dest je
 * uvolnena. Promenna Dest na konci tedy obsahuje retezec tvaru:
 *
 * * Source\\Dest, pokud Source neni L'\\',
 * * \\Dest, pokud Source je L'\\'.
 *
 * @param Dest Struktura UNICODE_STRING reprezentujici retezec, pred nejz ma byt predrazen
 * element cesty. Tato struktura po uspesnem volani funkce obsahuje vysledny retezec.
 * @param Source Struktura UNICODE_STRING reptezentujici retezec, ktery ma byt predrazen.
 *
 * @return Vraci kod NTSTATUS odpovidajici uspechu operace. V pripade neuspechu nejsou hodnoty
 * parametru nijak meneny.
 */
static NTSTATUS PreAppendUnicodeString(PUNICODE_STRING Dest, PUNICODE_STRING Source)
{
  ULONG32 BufferLength = 0;
  PWCHAR Buffer = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("services.c: PreAppendUnicodeString(Dest=%S; Source=%S)\n", Dest->Buffer, Source->Buffer));

  BufferLength = Dest->Length + Source->Length + 2 * sizeof(WCHAR);
  Buffer = (PWCHAR)ExAllocatePoolWithTag(PagedPool, BufferLength, POOL_TAG);
  if (Buffer != NULL) {
    RtlZeroMemory(Buffer, BufferLength);
    RtlCopyMemory(Buffer, Source->Buffer, Source->Length);
    if (Source->Buffer[0] != L'\\') {
      Buffer[Source->Length / sizeof(WCHAR)] = L'\\';
      RtlCopyMemory(&Buffer[Source->Length / sizeof(WCHAR) + 1], Dest->Buffer, Dest->Length);
    } else {
      RtlCopyMemory(&Buffer[Source->Length / sizeof(WCHAR)], Dest->Buffer, Dest->Length);
      BufferLength -= sizeof(WCHAR);
    }

    ExFreePoolWithTag(Dest->Buffer, POOL_TAG);
    Dest->Buffer = Buffer;
    Dest->Length = BufferLength - sizeof(WCHAR);
    Dest->MaximumLength = Dest->Length;
    Status = STATUS_SUCCESS;
  } else Status = STATUS_INSUFFICIENT_RESOURCES;

  KdPrint(("services.c: PreAppendUnicodeString(-):0x%x, Dest=%S\n", Status, Dest->Buffer));
  return Status;
}


/** Vrati cele jmeno ciloveho objektu.
 *
 * Cele jmeno objektu je poskladano postupnym pruchodem od objektu ke korenovemu adresari.
 * Jednotlive adresare jsou postupne zamykany behem prochazeni a dochazi i k manipulaci s jejich poctem
 * referenci. Pro ulozeni celeho jmena objektu je alokovana strankovana pamet, kterou je potreba
 * po skonceni pouzivani jmena explicitne uvolnit.
 *
 * @param Object Adresa tela objektu, jehoz jmeno chce volajici zjistit.
 * @param FullName Adresa struktury UNICODE_STRING, kam funkce v pripade uspechu ulozi cele
 * jmeno objektu.
 * @param NameLength Adresa promenne, kam funkce v pripade uspechu zapise delku posledni casti
 * jmena objektu (tedy jmena ulozeneho v hlavicce samotneho objektu). Tento parametr muze mit
 * hodnotu NULL.
 *
 * @param Vrati hodnotu NTSTATUS odpovidajici uspechu ci neuspechu operace.
 */
NTSTATUS OBGetFullName(PVOID Object, PUNICODE_STRING FullName, PULONG NameLength)
{
  BOOLEAN FirstPassed = FALSE;
  ULONG LastElementLength = 0;
  OBJECT_NAME_INFO Tmp;
  OBJECT_NAME_INFO ObjectNameInfo;
  PVOID Directory = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("object.c: OBGetFullName(Object=0x%p; FullName=0x%p)\n", Object, FullName));

  RtlZeroMemory(FullName, sizeof(UNICODE_STRING));
  Status = OBGetNameInfo(Object, &ObjectNameInfo);
  if (NT_SUCCESS(Status) && ObjectNameInfo.Name.Length > 0) {
    LastElementLength = ObjectNameInfo.Name.Length;
	if (NameLength != NULL)
      *NameLength = LastElementLength;

    Directory = ObjectNameInfo.Directory;
    while (Directory != NULL) {
      Status = OBGetNameInfo(Directory, &Tmp);
      if (NT_SUCCESS(Status)) {
        if (Tmp.Name.Buffer != NULL) {
          Status = PreAppendUnicodeString(&ObjectNameInfo.Name, &Tmp.Name);
          if (!NT_SUCCESS(Status)) {
            OBReleaseNameInfo(&Tmp);
            break;
		  }
		}

		if (FirstPassed)
          ObDereferenceObject(Directory);

        Directory = Tmp.Directory;
		if (Directory != NULL)
          ObReferenceObject(Directory);

        OBReleaseNameInfo(&Tmp);
        FirstPassed = TRUE;
      } else {
        if (FirstPassed)
          ObDereferenceObject(Directory);

        Directory = NULL;
	  }
    }

	if (NT_SUCCESS(Status)) {
	  *FullName = ObjectNameInfo.Name;
	  if (ObjectNameInfo.Directory != NULL)
	    ObDereferenceObject(ObjectNameInfo.Directory);
	} else OBReleaseNameInfo(&ObjectNameInfo);
  } else {
    if (NT_SUCCESS(Status))
      OBReleaseNameInfo(&ObjectNameInfo);
  }

  KdPrint(("object.c: OBGetFullName(-):0x%x\n", Status));
  return Status;
}



/** Inicializuje cely modul. 
 *
 * Funkce predpoklada, ze ostatni moduly frameworku na nizsich urovnich jiz jsou 
 * inicializovany (object_type, object_header, object_directory, object_initializer).
 *
 * Rutina pouze zapise cislo verze do lokalni promenne VersionCode a podle jeji hodnoty
 * nastavi pocet zamku v objektu typu (4 pro XP/2003, 32 pro Vista/2008, 0 jinak / 2K/7
 * pouzivaji pro zamykani objektu jine mechanismy).
 *
 * @return Vraci hodnotu NTSTATUS indikujici uspech ci neuspech inicializace.
 */
NTSTATUS OBInit(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("object.c: OBInit()\n"));

  VersionCode = GetVersionCode();
  if (VersionCode != VERSION_UNKNOWN) {
    switch (VersionCode) {
      case VERSION_WXP: LockCount = OTWXPObjectLockCount; break;
      case VERSION_W2K3: LockCount = OTW2K3ObjectLockCount; break;
      case VERSION_VISTA: LockCount = OTVistaObjectLockCount; break;
      default: LockCount = 0; break;
    }

    Status = STATUS_SUCCESS;
  } else Status = STATUS_NOT_SUPPORTED;

  KdPrint(("object.c: OBInit():0x%x\n", Status));
  return Status;
}


/** Provede uklid po modulu.
 *
 * Jelikoz po tomto modulu neni co uklizet, jedna se vlastne o NO-OP.
 */
VOID OBFinit(VOID)
{
  KdPrint(("object.c: OBFinit()\n"));

  KdPrint(("object.c: OBFinit(-)\n"));
  return;
}
