/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   digifile.c

Abstract:

   Kernelmode file access routines.

Author:

   Michael Zarns

--*/

#include "digilib.h"

//
// Describes an open file
//
typedef struct _DIGI_FILE_DESCRIPTOR
{
    HANDLE NtFileHandle;
    PVOID Data;
    KSPIN_LOCK Lock;
    BOOLEAN Mapped;
} DIGI_FILE_DESCRIPTOR, *PDIGI_FILE_DESCRIPTOR;


VOID
DigiOpenFile(
	OUT PNTSTATUS       Status,
	OUT PHANDLE         FileHandle,
	OUT PULONG          FileLength,
	IN PUNICODE_STRING  FileName,
	IN PHYSICAL_ADDRESS HighestAcceptableAddress )
/*++

Routine Description:

    This routine opens a file for future mapping and reads its contents
    into allocated memory.

Arguments:

    Status - The status of the operation

    FileHandle - A handle to be associated with this open

    FileLength - Returns the length of the file

    FileName - The name of the file

    HighestAcceptableAddress - The highest physical address at which
      the memory for the file can be allocated.

Return Value:

    None.

--*/
{
   NTSTATUS NtStatus;
   IO_STATUS_BLOCK IoStatus;
   HANDLE NtFileHandle;
   OBJECT_ATTRIBUTES ObjectAttributes;
   ULONG LengthOfFile;
   WCHAR PathPrefix[] = L"\\SystemRoot\\system32\\drivers\\";
   ULONG PathPrefixLength;
   UNICODE_STRING FullFileName = {0};
   ULONG FullFileNameLength;
   PDIGI_FILE_DESCRIPTOR FileDescriptor;
   PVOID FileImage;

   //
   // This structure represents the data from the
   // NtQueryInformationFile API with an information
   // class of FileStandardInformation.
   //
   FILE_STANDARD_INFORMATION StandardInfo;

   //
   // Insert the correct path prefix.
   //
   PathPrefixLength = sizeof(PathPrefix);

   FullFileNameLength = PathPrefixLength + FileName->Length + sizeof(WCHAR);
   if (FullFileNameLength > MAXUSHORT)
   {
       *Status = STATUS_INSUFFICIENT_RESOURCES;
       return;
   }

   FullFileName.Buffer = DigiAllocMem( NonPagedPool, FullFileNameLength );
   if (FullFileName.Buffer == NULL)
   {
       *Status = STATUS_INSUFFICIENT_RESOURCES;
       return;
   }

   FullFileName.MaximumLength = (USHORT)FullFileNameLength;
   RtlAppendUnicodeToString(&FullFileName, PathPrefix);
   RtlAppendUnicodeStringToString (&FullFileName, FileName);

   DigiDump(DEBUG_DIGILIB, ( "DIGIFILE: Attempting to open %wZ\n", &FullFileName ) );

   InitializeObjectAttributes( &ObjectAttributes, &FullFileName,
                               OBJ_CASE_INSENSITIVE, NULL, NULL );

   NtStatus = ZwCreateFile( &NtFileHandle, SYNCHRONIZE | FILE_READ_DATA,
                            &ObjectAttributes, &IoStatus, NULL,
                            FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
                            FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );

   if( !NT_SUCCESS(NtStatus) )
   {
      DigiDump( DEBUG_ERROR, ( "Error opening file %x\n", NtStatus ) );
      DigiFreeMem( FullFileName.Buffer );
      *Status = NtStatus;
      return;
   }

   DigiFreeMem( FullFileName.Buffer );

   //
   // Query the object to determine its length.
   //

   NtStatus = ZwQueryInformationFile( NtFileHandle, &IoStatus, &StandardInfo,
                                      sizeof(FILE_STANDARD_INFORMATION),
                                      FileStandardInformation );

   if ( !NT_SUCCESS(NtStatus) )
   {
      DigiDump( DEBUG_ERROR, ( "Error querying info on file %x\n", NtStatus ) );
      ZwClose( NtFileHandle );
      *Status = NtStatus;
      return;
   }

   LengthOfFile = StandardInfo.EndOfFile.LowPart;

   DigiDump( DEBUG_DIGILIB, ( "File length is %d\n", LengthOfFile ) );

   //
   // Might be corrupted.
   //

   if( LengthOfFile < 1 )
   {
      DigiDump( DEBUG_ERROR, ( "Bad file length %d\n", LengthOfFile ) );
      ZwClose( NtFileHandle );
      *Status = STATUS_FILE_INVALID;
      return;
   }

   //
   // Allocate buffer for this file
   //

   FileImage = DigiAllocMem( NonPagedPool,
                             LengthOfFile );

   if( FileImage == NULL )
   {
     DigiDump( DEBUG_ERROR, ( "Could not allocate buffer\n" ) );
     ZwClose( NtFileHandle );
     *Status = STATUS_INSUFFICIENT_RESOURCES;
     return;
   }

   //
   // Read the file into our buffer.
   //

   NtStatus = ZwReadFile( NtFileHandle, NULL, NULL, NULL, &IoStatus,
                          FileImage, LengthOfFile, NULL, NULL );

   ZwClose( NtFileHandle );

   if( (!NT_SUCCESS(NtStatus)) || (IoStatus.Information != LengthOfFile) )
   {
      DigiDump( DEBUG_ERROR, ( "error reading file %x\n", NtStatus ) );
      *Status = STATUS_UNSUCCESSFUL;
      DigiFreeMem( FileImage );
      return;
   }

   //
   // Allocate a structure to describe the file.
   //

   FileDescriptor = DigiAllocMem( NonPagedPool, sizeof(DIGI_FILE_DESCRIPTOR) );

   if( FileDescriptor == NULL )
   {
      *Status = STATUS_INSUFFICIENT_RESOURCES;
      DigiFreeMem( FileImage );
      return;
   }

   FileDescriptor->NtFileHandle = NtFileHandle;
   FileDescriptor->Data = FileImage;
   KeInitializeSpinLock( &FileDescriptor->Lock );
   FileDescriptor->Mapped = FALSE;

   *FileHandle = (HANDLE)FileDescriptor;
   *FileLength = LengthOfFile;
   *Status = STATUS_SUCCESS;
}


VOID
DigiCloseFile(
	IN HANDLE FileHandle )
/*++

Routine Description:

    This routine closes a file previously opened with DigiOpenFile.
    The file is unmapped if needed and the memory is freed.

Arguments:

    FileHandle - The handle returned by DigiOpenFile

Return Value:

    None.

--*/
{
    PDIGI_FILE_DESCRIPTOR FileDescriptor = (PDIGI_FILE_DESCRIPTOR)FileHandle;

    DigiFreeMem( FileDescriptor->Data );
    DigiFreeMem( FileDescriptor );
}



VOID
DigiMapFile(
	OUT PNTSTATUS Status,
	OUT PVOID     *MappedBuffer,
	IN HANDLE     FileHandle )
/*++

Routine Description:

    This routine maps an open file, so that the contents can be accessed.
    Files can only have one active mapping at any time.

Arguments:

    Status - The status of the operation

    MappedBuffer - Returns the virtual address of the mapping.

    FileHandle - The handle returned by DigiOpenFile.

Return Value:

    None.

--*/
{
    PDIGI_FILE_DESCRIPTOR FileDescriptor = (PDIGI_FILE_DESCRIPTOR)FileHandle;
    KIRQL oldirql;

    KeAcquireSpinLock (&FileDescriptor->Lock, &oldirql);

    if (FileDescriptor->Mapped == TRUE)
    {
        *Status = STATUS_UNSUCCESSFUL;
        KeReleaseSpinLock (&FileDescriptor->Lock, oldirql);
        return;
    }

    FileDescriptor->Mapped = TRUE;
    KeReleaseSpinLock (&FileDescriptor->Lock, oldirql);

    *MappedBuffer = FileDescriptor->Data;
    *Status = STATUS_SUCCESS;
}


VOID
DigiUnmapFile(
	IN HANDLE FileHandle )
/*++

Routine Description:

    This routine unmaps a file previously mapped with DigiOpenFile.
    The file is unmapped if needed and the memory is freed.

Arguments:

    FileHandle - The handle returned by DigiOpenFile

Return Value:

    None.

--*/
{
   PDIGI_FILE_DESCRIPTOR FileDescriptor = (PDIGI_FILE_DESCRIPTOR)FileHandle;
   KIRQL oldirql;

   KeAcquireSpinLock (&FileDescriptor->Lock, &oldirql);
   FileDescriptor->Mapped = FALSE;
   KeReleaseSpinLock (&FileDescriptor->Lock, oldirql);
}
