
/**
 * @file: driver.c
 *
 * Hlavni soubor ovladace objview.sys. Obsahuje kod pro inicializaci,
 * uklid a komunikaci s okolim pomoci objektu zarizeni.
 */

#include <ntddk.h>
#include "ioctls.h"
#include "hash_table.h"
#include "version.h"
#include "utils.h"
#include "pushlock.h"
#include "handles.h"
#include "object_directory.h"
#include "object_header.h"
#include "object_initializer.h"
#include "object_type.h"
#include "object.h"
#include "services.h"
#include "driver.h"


/** Reaguje na pozadavky IRP_MJ_CREATE a IRP_MJ_CLOSE.
 *
 *  Rutina vzdy vrati uspech.
 */
NTSTATUS DriverCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverCreateClose(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  Status = STATUS_SUCCESS;
  Irp->IoStatus.Status = Status;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

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


/** Obsluhuje pozadavky obecne zpravy, pomoci kterych ovladc predava
 *  data okoli.
 *
 * @return Vraci kod NTSTATUS odpovidajici uspesnemu ci neuspesnemu vyrizeni
 * pozadavku.
 */
NTSTATUS DriverDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
  PIO_STACK_LOCATION IrpStack = NULL;
  PVOID OutBuffer = NULL;
  ULONG OutBufferLength = 0;
  PVOID InBuffer = NULL;
  ULONG InBufferLength = 0;
  ULONG ControlCode = 0;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverDeviceControl(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  IrpStack = IoGetCurrentIrpStackLocation(Irp);
  OutBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  OutBuffer = Irp->AssociatedIrp.SystemBuffer;
  InBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
  InBuffer = Irp->AssociatedIrp.SystemBuffer;
  ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
  Irp->IoStatus.Information = 0;

  switch (ControlCode) {
    case IOCTL_OBJVIEW_OPEN:
      if (InBufferLength >= sizeof(OBJVIEW_OPEN_IN) &&
          OutBufferLength >= sizeof(OBJVIEW_OPEN_OUT)) {
        Status = OpenObjectService((POBJVIEW_OPEN_IN)InBuffer, InBufferLength, (POBJVIEW_OPEN_OUT)OutBuffer);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = sizeof(OBJVIEW_OPEN_OUT);
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_CLOSE:
      if (InBufferLength >= sizeof(OBJVIEW_CLOSE_IN))
        Status = CloseObjectService((POBJVIEW_CLOSE_IN)InBuffer);
      else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_HEADER:
      if (InBufferLength >= sizeof(OBJVIEW_GET_HEADER_IN) &&
          OutBufferLength >= sizeof(OBJVIEW_GET_HEADER_OUT)) {
        Status = GetObjectHeaderService((POBJVIEW_GET_HEADER_IN)InBuffer, (POBJVIEW_GET_HEADER_OUT)OutBuffer);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = sizeof(OBJVIEW_GET_HEADER_OUT);
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_NAME_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_NAME_INFO_IN)) {
        Status = GetObjectNameInfoService((POBJVIEW_GET_NAME_INFO_IN)InBuffer, (POBJVIEW_GET_NAME_INFO_OUT)OutBuffer, &OutBufferLength);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = OutBufferLength;
	  } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_HANDLE_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_HANDLE_INFO_IN)) {
        Status = GetObjectHandleInfoService((POBJVIEW_GET_HANDLE_INFO_IN)InBuffer, (POBJVIEW_GET_HANDLE_INFO_OUT)OutBuffer, &OutBufferLength);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = OutBufferLength;
	  } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_QUOTA_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_QUOTA_INFO_IN) &&
          OutBufferLength >= sizeof(OBJVIEW_GET_QUOTA_INFO_OUT)) {
        Status = GetObjectQuotaInfoService((POBJVIEW_GET_QUOTA_INFO_IN)InBuffer, (POBJVIEW_GET_QUOTA_INFO_OUT)OutBuffer);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = sizeof(OBJVIEW_GET_QUOTA_INFO_OUT);
	  } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_CREATOR_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_CREATOR_INFO_IN) &&
          OutBufferLength >= sizeof(OBJVIEW_GET_CREATOR_INFO_OUT)) {
	    Status = GetObjectCreatorInfoService((POBJVIEW_GET_CREATOR_INFO_IN)InBuffer, (POBJVIEW_GET_CREATOR_INFO_OUT)OutBuffer);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = sizeof(OBJVIEW_GET_CREATOR_INFO_OUT);
	  } else Status = STATUS_BUFFER_TOO_SMALL;
	  break;

    case IOCTL_OBJVIEW_GET_SPECIFIC_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_SPECIFIC_INFO_IN)) {
	    Status = GetObjectSpecificInfoService((POBJVIEW_GET_SPECIFIC_INFO_IN)InBuffer, OutBuffer, &OutBufferLength);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = OutBufferLength;
	  } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    case IOCTL_OBJVIEW_GET_TYPE_INFO:
      if (InBufferLength >= sizeof(OBJVIEW_GET_TYPE_INFO_IN) &&
          OutBufferLength >= sizeof(OBJVIEW_GET_TYPE_INFO_OUT) - sizeof(WCHAR)) {
        Status = GetObjectTypeInfoService((POBJVIEW_GET_TYPE_INFO_IN)InBuffer, (POBJVIEW_GET_TYPE_INFO_OUT)OutBuffer, &OutBufferLength);
        if (NT_SUCCESS(Status))
          Irp->IoStatus.Information = OutBufferLength;
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    default:
      KdPrint(("driver.c: DriverDeviceControl(): Invalid device control request\n"));
      Status = STATUS_NOT_IMPLEMENTED;
      break;
  }  

  Irp->IoStatus.Status = Status;
  IoCompleteRequest (Irp,IO_NO_INCREMENT);

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


/** Provede uklid casti ovladace zodpovedne za komunikaci.
 *
 *  Rutina odstrani symbolicky odkaz a samze objekt komunikacniho
 *  zarizeni.
 *
 *  @param Driverobject Adresa struktury DRIVER_OBJECT reprezentujici
 *  tento voladac.
 */
VOID DriverFinit(IN PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING usymlink;
  KdPrint(("driver.c: DriverFinit(Driverobject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&usymlink,DRIVER_SYMLINK);
  IoDeleteSymbolicLink(&usymlink);
  IoDeleteDevice(DriverObject->DeviceObject);

  KdPrint(("driver.c: DriverFinit(-)\n"));
  return;
}


/** Tuto rutinu system vola pri uvolnovani ovladace z jdara.
 *
 *  Rutina provede veskere nutne uklidove operace.
 */
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
  KdPrint(("driver.c: DriverUnload(DriverObject=0x%p)\n", DriverObject));

  DriverFinit(DriverObject);
  HandlesFinit();
  OBFinit();
  DirObjectFinit();
  OTModuleFinit();
  OTIFinit();
  OHFinit();
  PushLockModuleFinit();
  UtilsFinit();
  VersionFinit();

  KdPrint(("driver.c: DriverUnload(-)\n"));
  return;
}


/** Inicializuje cast ovladace zodpovednou za komunikaci s okolnim svetem.
 *
 * Rutina vytvori objekt zarizeni a nasmeruje na nej symbolicky odkaz tak,
 * aby s ovladacem mohly komunikovat i bezne aplikace bezici v uzivatelskem
 * rezimu. Rutina take nastavi funkci DriverUnload, takze ovladac lze dynamicky
 * odstranit z pameti jadra.
 *
 * @param DriverObject Adresa struktury DRIVER_OBJECT reprezentujici ovladac
 * v jadre.
 *
 * @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 * operace.
 */
NTSTATUS DriverInit(IN PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING udevice;
  UNICODE_STRING usymlink;
  NTSTATUS status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverInit(DriverObject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&udevice, DRIVER_DEVICE);
  status = IoCreateDevice(DriverObject, 0, &udevice, FILE_DEVICE_UNKNOWN, 0, FALSE, &DriverObject->DeviceObject);
  if (NT_SUCCESS(status)) {
    RtlInitUnicodeString(&usymlink, DRIVER_SYMLINK);
    status = IoCreateSymbolicLink(&usymlink, &udevice);
    if (NT_SUCCESS(status)) {
      DriverObject->DriverUnload = DriverUnload;
      DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
      DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverCreateClose;
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
      if (!NT_SUCCESS(status)) {
        IoDeleteSymbolicLink(&usymlink);
        IoDeleteDevice(DriverObject->DeviceObject);
      }
    } else IoDeleteDevice(DriverObject->DeviceObject);
  }

  KdPrint(("driver.c: DriverInit(-):0x%x\n", status));
  return status;
}


/** Tuto rutinu jadro vola tesne po nacteni ovladace do jadra.
 *
 *  Rutina inicializuje vsechny casti ovladace; mechanismus handle,
 *  framework pro praci s datovymi strukturami spravce objektu, pomocne
 *  moduly a komponentu zodpovednou za komunikaci s okolim.
 *
 *  @return Rutina vraci kod NTSTATUS odpovidajici uspechu ci neuspechu
 *  inicializace ovladace.
 */
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  NTSTATUS status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverEntry(DriverObject=%p;RegistryPath=\"%S\")\n", DriverObject, RegistryPath->Buffer));

  status = VersionInit();
  if (NT_SUCCESS(status)) {
    status = UtilsInit();
    if (NT_SUCCESS(status)) {
      status = PushLockModuleInit();
      if (NT_SUCCESS(status)) {
        status = OHInit();
        if (NT_SUCCESS(status)) {
          status = OTIInit(DriverObject);
          if (NT_SUCCESS(status)) {
            status = OTModuleInit();
            if (NT_SUCCESS(status)) {
              status = DirObjectInit(DriverObject);
              if (NT_SUCCESS(status)) {
                status = OBInit();
                if (NT_SUCCESS(status)) {
                  status = HandlesInit();
                  if (NT_SUCCESS(status)) {
                    status = DriverInit(DriverObject);
                    if (!NT_SUCCESS(status)) {
                      HandlesFinit();
                      OBFinit();
                      DirObjectFinit();
                      OTModuleFinit();
                      OTIFinit();
                      OHFinit();
                      PushLockModuleFinit();
                      UtilsFinit();
                      VersionFinit();
                    }
                  } else {
                    OBFinit();
                    DirObjectFinit();
                    OTModuleFinit();
                    OTIFinit();
                    OHFinit();
                    PushLockModuleFinit();
                    UtilsFinit();
		    VersionFinit();
                  }
                } else {
                  DirObjectFinit();
                  OTModuleFinit();
                  OTIFinit();
                  OHFinit();
                  PushLockModuleFinit();
                  UtilsFinit();
                  VersionFinit();
                }
              } else {
                OTModuleFinit();
                OTIFinit();
                OHFinit();
                PushLockModuleFinit();
                UtilsFinit();
                VersionFinit();
              }
            } else {
              OTIFinit();
              OHFinit();
              PushLockModuleFinit();
              UtilsFinit();
              VersionFinit();
            }
          } else {
            OHFinit();
            PushLockModuleFinit();
            UtilsFinit();
            VersionFinit();
          }
	} else {
          PushLockModuleFinit();
          UtilsFinit();
          VersionFinit();
	}
      } else {
        UtilsFinit();
        VersionFinit();
      }
    } else VersionFinit();
  }

  KdPrint(("driver.c: DriverEntry(-):0x%x\n", status));
  return status;
}
