/* $Id: file.c,v 1.23 2003/03/23 14:46:09 ekohl Exp $
 *
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS kernel
 * FILE:            ntoskrnl/io/file.c
 * PURPOSE:         Graceful system shutdown if a bug is detected
 * PROGRAMMER:      David Welch (welch@mcmail.com)
 * UPDATE HISTORY:
 *                  Created 22/05/98
 */

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

#include <ddk/ntddk.h>
#include <internal/io.h>
#include <internal/mm.h>

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


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

#define TAG_SYSB   TAG('S', 'Y', 'S', 'B')


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

NTSTATUS STDCALL
NtQueryInformationFile(HANDLE FileHandle,
		       PIO_STATUS_BLOCK IoStatusBlock,
		       PVOID FileInformation,
		       ULONG Length,
		       FILE_INFORMATION_CLASS FileInformationClass)
{
   PFILE_OBJECT FileObject;
   NTSTATUS Status;
   PIRP Irp;
   PDEVICE_OBJECT DeviceObject;
   PIO_STACK_LOCATION StackPtr;
   PVOID SystemBuffer;
   IO_STATUS_BLOCK IoSB;
   
   assert(IoStatusBlock != NULL);
   assert(FileInformation != NULL);
   
   DPRINT("NtQueryInformationFile(Handle %x StatBlk %x FileInfo %x Length %d "
	  "Class %d)\n", FileHandle, IoStatusBlock, FileInformation,
	  Length, FileInformationClass);
   
   Status = ObReferenceObjectByHandle(FileHandle,
				      FILE_READ_ATTRIBUTES,
				      IoFileObjectType,
				      UserMode,
				      (PVOID *)&FileObject,
				      NULL);
   if (!NT_SUCCESS(Status))
     {
	return(Status);
     }
   DPRINT("FileObject %x\n", FileObject);
   
   DeviceObject = FileObject->DeviceObject;
   
   Irp = IoAllocateIrp(DeviceObject->StackSize,
		       TRUE);
   if (Irp == NULL)
     {
	ObDereferenceObject(FileObject);
	return STATUS_INSUFFICIENT_RESOURCES;
     }
   
   SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,
					Length,
					TAG_SYSB);
   if (SystemBuffer == NULL)
     {
	IoFreeIrp(Irp);
	ObDereferenceObject(FileObject);
	return(STATUS_INSUFFICIENT_RESOURCES);
     }
   
   Irp->AssociatedIrp.SystemBuffer = SystemBuffer;
   Irp->UserIosb = &IoSB;
   Irp->UserEvent = &FileObject->Event;
   KeResetEvent( &FileObject->Event );
   
   StackPtr = IoGetNextIrpStackLocation(Irp);
   StackPtr->MajorFunction = IRP_MJ_QUERY_INFORMATION;
   StackPtr->MinorFunction = 0;
   StackPtr->Flags = 0;
   StackPtr->Control = 0;
   StackPtr->DeviceObject = DeviceObject;
   StackPtr->FileObject = FileObject;
   
   StackPtr->Parameters.QueryFile.FileInformationClass =
     FileInformationClass;
   StackPtr->Parameters.QueryFile.Length = Length;
   
   Status = IoCallDriver(FileObject->DeviceObject,
			 Irp);
   if (Status==STATUS_PENDING && !(FileObject->Flags & FO_SYNCHRONOUS_IO))
     {
	KeWaitForSingleObject(&FileObject->Event,
			      Executive,
			      KernelMode,
			      FALSE,
			      NULL);
	Status = IoSB.Status;
     }
  if (IoStatusBlock)
    {
      *IoStatusBlock = IoSB;
    }

  if (NT_SUCCESS(Status))
    {
      DPRINT("Information %lu\n", IoStatusBlock->Information);
      MmSafeCopyToUser(FileInformation,
		       SystemBuffer,
		       IoStatusBlock->Information);
    }
   
   ExFreePool(SystemBuffer);   
   return(Status);
}


NTSTATUS STDCALL
IoQueryFileInformation(IN PFILE_OBJECT FileObject,
		       IN FILE_INFORMATION_CLASS FileInformationClass,
		       IN ULONG Length,
		       OUT PVOID FileInformation,
		       OUT PULONG ReturnedLength)
{
   IO_STATUS_BLOCK IoStatusBlock;
   PIRP Irp;
   PDEVICE_OBJECT DeviceObject;
   PIO_STACK_LOCATION StackPtr;
   NTSTATUS Status;
   
   assert(FileInformation != NULL)
   
   Status = ObReferenceObjectByPointer(FileObject,
				       FILE_READ_ATTRIBUTES,
				       IoFileObjectType,
				       KernelMode);
   if (!NT_SUCCESS(Status))
     {
	return(Status);
     }
   
   DPRINT("FileObject %x\n", FileObject);
   
   DeviceObject = FileObject->DeviceObject;
   
   Irp = IoAllocateIrp(DeviceObject->StackSize,
		       TRUE);
   if (Irp == NULL)
     {
	ObDereferenceObject(FileObject);
	return STATUS_INSUFFICIENT_RESOURCES;
     }
   
   Irp->AssociatedIrp.SystemBuffer = FileInformation;
   Irp->UserIosb = &IoStatusBlock;
   Irp->UserEvent = &FileObject->Event;
   KeResetEvent( &FileObject->Event );
   
   StackPtr = IoGetNextIrpStackLocation(Irp);
   StackPtr->MajorFunction = IRP_MJ_QUERY_INFORMATION;
   StackPtr->MinorFunction = 0;
   StackPtr->Flags = 0;
   StackPtr->Control = 0;
   StackPtr->DeviceObject = DeviceObject;
   StackPtr->FileObject = FileObject;
   
   StackPtr->Parameters.QueryFile.FileInformationClass =
     FileInformationClass;
   StackPtr->Parameters.QueryFile.Length = Length;
   
   Status = IoCallDriver(FileObject->DeviceObject,
			 Irp);
   if (Status==STATUS_PENDING && !(FileObject->Flags & FO_SYNCHRONOUS_IO))
     {
	KeWaitForSingleObject(&FileObject->Event,
			      Executive,
			      KernelMode,
			      FALSE,
			      NULL);
	Status = IoStatusBlock.Status;
     }
   
   if (ReturnedLength != NULL)
     {
	*ReturnedLength = IoStatusBlock.Information;
     }
   
   
   return Status;
}


NTSTATUS STDCALL
NtSetInformationFile(HANDLE FileHandle,
		     PIO_STATUS_BLOCK IoStatusBlock,
		     PVOID FileInformation,
		     ULONG Length,
		     FILE_INFORMATION_CLASS FileInformationClass)
{
   PIO_STACK_LOCATION StackPtr;
   PFILE_OBJECT FileObject;
   PDEVICE_OBJECT DeviceObject;
   PIRP Irp;
   NTSTATUS Status;
   PVOID SystemBuffer;
   IO_STATUS_BLOCK IoSB;
   
   assert(IoStatusBlock != NULL)
   assert(FileInformation != NULL)
   
   DPRINT("NtSetInformationFile(Handle %x StatBlk %x FileInfo %x Length %d "
	  "Class %d)\n", FileHandle, IoStatusBlock, FileInformation,
	  Length, FileInformationClass);
   
   /*  Get the file object from the file handle  */
   Status = ObReferenceObjectByHandle(FileHandle,
				      FILE_WRITE_ATTRIBUTES,
				      IoFileObjectType,
				      UserMode,
				      (PVOID *)&FileObject,
				      NULL);
   if (!NT_SUCCESS(Status))
     {
	return Status;
     }
   
   DPRINT("FileObject %x\n", FileObject);

   //io completion port?
   if (FileInformationClass == FileCompletionInformation)
   {
      PKQUEUE Queue;

      if (Length < sizeof(FILE_COMPLETION_INFORMATION))
      {
         Status = STATUS_INFO_LENGTH_MISMATCH;
      }
      else
      {
         Status = ObReferenceObjectByHandle(((PFILE_COMPLETION_INFORMATION)FileInformation)->IoCompletionHandle,
                                            IO_COMPLETION_MODIFY_STATE,//???
                                            ExIoCompletionType,
                                            UserMode,
                                            (PVOID*)&Queue,
                                            NULL);
         if (NT_SUCCESS(Status))
         {   
            //FIXME: maybe use lookaside list
            FileObject->CompletionContext = ExAllocatePool(NonPagedPool, sizeof(IO_COMPLETION_CONTEXT));
            FileObject->CompletionContext->Key = ((PFILE_COMPLETION_INFORMATION)FileInformation)->CompletionKey;
            FileObject->CompletionContext->Port = Queue;

            ObDereferenceObject(Queue);
         }
      }

      ObDereferenceObject(FileObject);
      return Status;
   }

   DeviceObject = FileObject->DeviceObject;
   
   Irp = IoAllocateIrp(DeviceObject->StackSize,
		       TRUE);
   if (Irp == NULL)
     {
	ObDereferenceObject(FileObject);
	return STATUS_INSUFFICIENT_RESOURCES;
     }
   
   SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,
					Length,
					TAG_SYSB);
   if (SystemBuffer == NULL)
     {
	IoFreeIrp(Irp);
	ObDereferenceObject(FileObject);
	return(STATUS_INSUFFICIENT_RESOURCES);
     }
   
   MmSafeCopyFromUser(SystemBuffer,
		      FileInformation,
		      Length);
   
   Irp->AssociatedIrp.SystemBuffer = SystemBuffer;
   Irp->UserIosb = &IoSB;
   Irp->UserEvent = &FileObject->Event;
   KeResetEvent( &FileObject->Event );
   
   StackPtr = IoGetNextIrpStackLocation(Irp);
   StackPtr->MajorFunction = IRP_MJ_SET_INFORMATION;
   StackPtr->MinorFunction = 0;
   StackPtr->Flags = 0;
   StackPtr->Control = 0;
   StackPtr->DeviceObject = DeviceObject;
   StackPtr->FileObject = FileObject;
   
   StackPtr->Parameters.SetFile.FileInformationClass =
     FileInformationClass;
   StackPtr->Parameters.SetFile.Length = Length;
   
   /*
    * Pass the IRP to the FSD (and wait for
    * it if required)
    */
   DPRINT("FileObject->DeviceObject %x\n", FileObject->DeviceObject);
   Status = IoCallDriver(FileObject->DeviceObject,
			 Irp);
   if (Status == STATUS_PENDING && !(FileObject->Flags & FO_SYNCHRONOUS_IO))
     {
	KeWaitForSingleObject(&FileObject->Event,
			      Executive,
			      KernelMode,
			      FALSE,
			      NULL);
	Status = IoSB.Status;
     }
   if (IoStatusBlock)
     {
       *IoStatusBlock = IoSB;
     }
   ExFreePool(SystemBuffer);
 
   return Status;
}


NTSTATUS STDCALL
NtQueryAttributesFile(IN POBJECT_ATTRIBUTES ObjectAttributes,
		      OUT PFILE_BASIC_INFORMATION FileInformation)
{
  IO_STATUS_BLOCK IoStatusBlock;
  HANDLE FileHandle;
  NTSTATUS Status;

  /* Open the file */
  Status = NtOpenFile (&FileHandle,
		       SYNCHRONIZE | FILE_READ_ATTRIBUTES,
		       ObjectAttributes,
		       &IoStatusBlock,
		       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		       FILE_SYNCHRONOUS_IO_NONALERT);
  if (!NT_SUCCESS (Status))
    {
      DPRINT ("NtOpenFile() failed (Status %lx)\n", Status);
      return Status;
    }

  /* Get file attributes */
  Status = NtQueryInformationFile (FileHandle,
				   &IoStatusBlock,
				   FileInformation,
				   sizeof(FILE_BASIC_INFORMATION),
				   FileBasicInformation);
  NtClose (FileHandle);
  if (!NT_SUCCESS (Status))
    {
      DPRINT ("NtQueryInformationFile() failed (Status %lx)\n", Status);
    }

  return Status;
}


NTSTATUS STDCALL
NtQueryFullAttributesFile(IN POBJECT_ATTRIBUTES ObjectAttributes,
			  OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation)
{
  IO_STATUS_BLOCK IoStatusBlock;
  HANDLE FileHandle;
  NTSTATUS Status;

  /* Open the file */
  Status = NtOpenFile (&FileHandle,
		       SYNCHRONIZE | FILE_READ_ATTRIBUTES,
		       ObjectAttributes,
		       &IoStatusBlock,
		       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		       FILE_SYNCHRONOUS_IO_NONALERT);
  if (!NT_SUCCESS (Status))
    {
      DPRINT ("NtOpenFile() failed (Status %lx)\n", Status);
      return Status;
    }

  /* Get file attributes */
  Status = NtQueryInformationFile (FileHandle,
				   &IoStatusBlock,
				   FileInformation,
				   sizeof(FILE_NETWORK_OPEN_INFORMATION),
				   FileNetworkOpenInformation);
  NtClose (FileHandle);
  if (!NT_SUCCESS (Status))
    {
      DPRINT ("NtQueryInformationFile() failed (Status %lx)\n", Status);
    }

  return Status;
}


NTSTATUS STDCALL
NtQueryEaFile(IN HANDLE FileHandle,
	      OUT PIO_STATUS_BLOCK IoStatusBlock,
	      OUT PVOID Buffer,
	      IN ULONG Length,
	      IN BOOLEAN ReturnSingleEntry,
	      IN PVOID EaList OPTIONAL,
	      IN ULONG EaListLength,
	      IN PULONG EaIndex OPTIONAL,
	      IN BOOLEAN RestartScan)
{
   UNIMPLEMENTED;
   return STATUS_NOT_IMPLEMENTED;
}


NTSTATUS STDCALL
NtSetEaFile(IN HANDLE FileHandle,
	    IN PIO_STATUS_BLOCK	IoStatusBlock,
	    IN PVOID EaBuffer,
	    IN ULONG EaBufferSize)
{
   UNIMPLEMENTED;
   return STATUS_NOT_IMPLEMENTED;
}

/* EOF */
