text: /*
   * nt_io.c --- This is the Nt I/O interface to the I/O manager.
   *
   * Implements a one-block write-through cache.
   *
   * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
   * Copyright (C) 1998 Andrey Shedel (andreys@ns.cr.cyco.com)
   *
   * %Begin-Header%
   * This file may be redistributed under the terms of the GNU Library
   * General Public License, version 2.
   * %End-Header%
   */
  
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif
  
  
  //
  // I need some warnings to disable...
  //
  
  
  #pragma warning(disable:4514) // unreferenced inline function has been removed
  #pragma warning(push,4)
  
  #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union)
  #pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
  #pragma warning(disable:4115) // named type definition in parentheses
  
  #include <ntddk.h>
  #include <ntdddisk.h>
  #include <ntstatus.h>
  
  #pragma warning(pop)
  
  
  //
  // Some native APIs.
  //
  
  NTSYSAPI
  ULONG
  NTAPI
  RtlNtStatusToDosError(
      IN NTSTATUS Status
     );
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtClose(
      IN HANDLE Handle
     );
  
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtOpenFile(
      OUT PHANDLE FileHandle,
      IN ACCESS_MASK DesiredAccess,
      IN POBJECT_ATTRIBUTES ObjectAttributes,
      OUT PIO_STATUS_BLOCK IoStatusBlock,
      IN ULONG ShareAccess,
      IN ULONG OpenOptions
      );
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtFlushBuffersFile(
      IN HANDLE FileHandle,
      OUT PIO_STATUS_BLOCK IoStatusBlock
     );
  
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtReadFile(
      IN HANDLE FileHandle,
      IN HANDLE Event OPTIONAL,
      IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
      IN PVOID ApcContext OPTIONAL,
      OUT PIO_STATUS_BLOCK IoStatusBlock,
      OUT PVOID Buffer,
      IN ULONG Length,
      IN PLARGE_INTEGER ByteOffset OPTIONAL,
      IN PULONG Key OPTIONAL
      );
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtWriteFile(
      IN HANDLE FileHandle,
      IN HANDLE Event OPTIONAL,
      IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
      IN PVOID ApcContext OPTIONAL,
      OUT PIO_STATUS_BLOCK IoStatusBlock,
      IN PVOID Buffer,
      IN ULONG Length,
      IN PLARGE_INTEGER ByteOffset OPTIONAL,
      IN PULONG Key OPTIONAL
      );
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtDeviceIoControlFile(
      IN HANDLE FileHandle,
      IN HANDLE Event OPTIONAL,
      IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
      IN PVOID ApcContext OPTIONAL,
      OUT PIO_STATUS_BLOCK IoStatusBlock,
      IN ULONG IoControlCode,
      IN PVOID InputBuffer OPTIONAL,
      IN ULONG InputBufferLength,
      OUT PVOID OutputBuffer OPTIONAL,
      IN ULONG OutputBufferLength
      );
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtFsControlFile(
      IN HANDLE FileHandle,
      IN HANDLE Event OPTIONAL,
      IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
      IN PVOID ApcContext OPTIONAL,
      OUT PIO_STATUS_BLOCK IoStatusBlock,
      IN ULONG IoControlCode,
      IN PVOID InputBuffer OPTIONAL,
      IN ULONG InputBufferLength,
      OUT PVOID OutputBuffer OPTIONAL,
      IN ULONG OutputBufferLength
      );
  
  
  NTSYSAPI
  NTSTATUS
  NTAPI
  NtDelayExecution(
      IN BOOLEAN Alertable,
      IN PLARGE_INTEGER Interval
      );
  
  
  #define FSCTL_LOCK_VOLUME               CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
  #define FSCTL_UNLOCK_VOLUME             CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
  #define FSCTL_DISMOUNT_VOLUME           CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
  #define FSCTL_IS_VOLUME_MOUNTED         CTL_CODE(FILE_DEVICE_FILE_SYSTEM,10, METHOD_BUFFERED, FILE_ANY_ACCESS)
  
  
  //
  // useful macros
  //
  
  #define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0)))
  
  
  //
  // Include Win32 error codes.
  //
  
  #include <winerror.h>
  
  //
  // standard stuff
  //
  
  #include <assert.h>
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include <malloc.h>
  
  #include <linux/types.h>
  #include "ext2_fs.h"
  #include <errno.h>
  
  #include "et/com_err.h"
  #include "ext2fs/ext2fs.h"
  #include "ext2fs/ext2_err.h"
  
  
  
  
  //
  // For checking structure magic numbers...
  //
  
  
  #define EXT2_CHECK_MAGIC(struct, code) \
  	  if ((struct)->magic != (code)) return (code)
  
  #define EXT2_ET_MAGIC_NT_IO_CHANNEL  0x10ed
  
  
  //
  // Private data block
  //
  
  typedef struct _NT_PRIVATE_DATA {
  	int	   magic;
  	HANDLE Handle;
  	int	   Flags;
  	PCHAR  Buffer;
  	__u32  BufferBlockNumber;
  	ULONG  BufferSize;
  	BOOLEAN OpenedReadonly;
  	BOOLEAN Written;
  }NT_PRIVATE_DATA, *PNT_PRIVATE_DATA;
  
  
  
  //
  // Standard interface prototypes
  //
  
  static errcode_t nt_open(const char *name, int flags, io_channel *channel);
  static errcode_t nt_close(io_channel channel);
  static errcode_t nt_set_blksize(io_channel channel, int blksize);
  static errcode_t nt_read_blk(io_channel channel, unsigned long block,
  			       int count, void *data);
  static errcode_t nt_write_blk(io_channel channel, unsigned long block,
  				int count, const void *data);
  static errcode_t nt_flush(io_channel channel);
  
  static struct struct_io_manager struct_nt_manager = {
  	EXT2_ET_MAGIC_IO_MANAGER,
  	"NT I/O Manager",
  	nt_open,
  	nt_close,
  	nt_set_blksize,
  	nt_read_blk,
  	nt_write_blk,
  	nt_flush
  };
  
  
  
  //
  // function to get API
  //
  
  io_manager nt_io_manager()
  {
  	return &struct_nt_manager;
  }
  
  
  
  
  
  //
  // This is a code to convert Win32 errors to unix errno
  //
  
  typedef struct {
  	ULONG WinError;
  	int errnocode;
  }ERROR_ENTRY;
  
  static ERROR_ENTRY ErrorTable[] = {
          {  ERROR_INVALID_FUNCTION,       EINVAL    },
          {  ERROR_FILE_NOT_FOUND,         ENOENT    },
          {  ERROR_PATH_NOT_FOUND,         ENOENT    },
          {  ERROR_TOO_MANY_OPEN_FILES,    EMFILE    },
          {  ERROR_ACCESS_DENIED,          EACCES    },
          {  ERROR_INVALID_HANDLE,         EBADF     },
          {  ERROR_ARENA_TRASHED,          ENOMEM    },
          {  ERROR_NOT_ENOUGH_MEMORY,      ENOMEM    },
          {  ERROR_INVALID_BLOCK,          ENOMEM    },
          {  ERROR_BAD_ENVIRONMENT,        E2BIG     },
          {  ERROR_BAD_FORMAT,             ENOEXEC   },
          {  ERROR_INVALID_ACCESS,         EINVAL    },
          {  ERROR_INVALID_DATA,           EINVAL    },
          {  ERROR_INVALID_DRIVE,          ENOENT    },
          {  ERROR_CURRENT_DIRECTORY,      EACCES    },
          {  ERROR_NOT_SAME_DEVICE,        EXDEV     },
          {  ERROR_NO_MORE_FILES,          ENOENT    },
          {  ERROR_LOCK_VIOLATION,         EACCES    },
          {  ERROR_BAD_NETPATH,            ENOENT    },
          {  ERROR_NETWORK_ACCESS_DENIED,  EACCES    },
          {  ERROR_BAD_NET_NAME,           ENOENT    },
          {  ERROR_FILE_EXISTS,            EEXIST    },
          {  ERROR_CANNOT_MAKE,            EACCES    },
          {  ERROR_FAIL_I24,               EACCES    },
          {  ERROR_INVALID_PARAMETER,      EINVAL    },
          {  ERROR_NO_PROC_SLOTS,          EAGAIN    },
          {  ERROR_DRIVE_LOCKED,           EACCES    },
          {  ERROR_BROKEN_PIPE,            EPIPE     },
          {  ERROR_DISK_FULL,              ENOSPC    },
          {  ERROR_INVALID_TARGET_HANDLE,  EBADF     },
          {  ERROR_INVALID_HANDLE,         EINVAL    },
          {  ERROR_WAIT_NO_CHILDREN,       ECHILD    },
          {  ERROR_CHILD_NOT_COMPLETE,     ECHILD    },
          {  ERROR_DIRECT_ACCESS_HANDLE,   EBADF     },
          {  ERROR_NEGATIVE_SEEK,          EINVAL    },
          {  ERROR_SEEK_ON_DEVICE,         EACCES    },
          {  ERROR_DIR_NOT_EMPTY,          ENOTEMPTY },
          {  ERROR_NOT_LOCKED,             EACCES    },
          {  ERROR_BAD_PATHNAME,           ENOENT    },
          {  ERROR_MAX_THRDS_REACHED,      EAGAIN    },
          {  ERROR_LOCK_FAILED,            EACCES    },
          {  ERROR_ALREADY_EXISTS,         EEXIST    },
          {  ERROR_FILENAME_EXCED_RANGE,   ENOENT    },
          {  ERROR_NESTING_NOT_ALLOWED,    EAGAIN    },
          {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }
  };
  
  
  
  
  static
  unsigned
  _MapDosError (
      IN ULONG WinError
     )
  {
  	int i;
  
  	//
  	// Lookup
  	//
  
  	for (i = 0; i < (sizeof(ErrorTable)/sizeof(ErrorTable[0])); ++i)
  	{
  		if (WinError == ErrorTable[i].WinError)
  		{
  			return ErrorTable[i].errnocode;
  		}
  	}
  
  	//
  	// not in table. Check ranges
  	//
  
  	if ((WinError >= ERROR_WRITE_PROTECT) &&
  		(WinError <= ERROR_SHARING_BUFFER_EXCEEDED))
  	{
  		return EACCES;
  	}
  	else if ((WinError >= ERROR_INVALID_STARTING_CODESEG) &&
  			 (WinError <= ERROR_INFLOOP_IN_RELOC_CHAIN))
  	{
  		return ENOEXEC;
  	}
  	else
  	{
  		return EINVAL;
  	}
  }
  
  
  
  
  
  
  
  //
  // Function to map NT status to dos error.
  //
  
  static
  __inline
  unsigned
  _MapNtStatus(
      IN NTSTATUS Status
     )
  {
  	return _MapDosError(RtlNtStatusToDosError(Status));
  }
  
  
  
  
  
  //
  // Helper functions to make things easyer
  //
  
  static
  NTSTATUS
  _OpenNtName(
      IN PCSTR Name,
      IN BOOLEAN Readonly,
      OUT PHANDLE Handle,
      OUT PBOOLEAN OpenedReadonly OPTIONAL
     )
  {
  	UNICODE_STRING UnicodeString;
  	ANSI_STRING    AnsiString;
  	WCHAR Buffer[512];
  	NTSTATUS Status;
  	OBJECT_ATTRIBUTES ObjectAttributes;
  	IO_STATUS_BLOCK IoStatusBlock;
  
  	//
  	// Make Unicode name from inlut string
  	//
  
  	UnicodeString.Buffer = &Buffer[0];
  	UnicodeString.Length = 0;
  	UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!!
  
  	RtlInitAnsiString(&AnsiString, Name);
  
  	Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
  
  	if(!NT_SUCCESS(Status))
  	{
  		return Status; // Unpappable character?
  	}
  
  	//
  	// Initialize object
  	//
  
  	InitializeObjectAttributes(&ObjectAttributes,
  							   &UnicodeString,
  							   OBJ_CASE_INSENSITIVE,
  							   NULL,
  							   NULL );
  
  	//
  	// Try to open it in initial mode
  	//
  
  	if(ARGUMENT_PRESENT(OpenedReadonly))
  	{
  		*OpenedReadonly = Readonly;
  	}
  
  
  	Status = NtOpenFile(Handle,
  						SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
  						&ObjectAttributes,
  						&IoStatusBlock,
  						FILE_SHARE_WRITE | FILE_SHARE_READ,
  						FILE_SYNCHRONOUS_IO_NONALERT);
  
  	if(!NT_SUCCESS(Status))
  	{
  		//
  		// Maybe was just mounted? wait 0.5 sec and retry.
  		//
  
  		LARGE_INTEGER Interval;
  		Interval.QuadPart = -5000000; // 0.5 sec. from now
  
  		NtDelayExecution(FALSE, &Interval);
  
  		Status = NtOpenFile(Handle,
  							SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
  							&ObjectAttributes,
  							&IoStatusBlock,
  							FILE_SHARE_WRITE | FILE_SHARE_READ,
  							FILE_SYNCHRONOUS_IO_NONALERT);
  
  		//
  		// Try to satisfy mode
  		//
  
  		if((STATUS_ACCESS_DENIED == Status) && !Readonly)
  		{
  			if(ARGUMENT_PRESENT(OpenedReadonly))
  			{
  				*OpenedReadonly = TRUE;
  			}
  
  			Status = NtOpenFile(Handle,
  							SYNCHRONIZE | FILE_READ_DATA,
  							&ObjectAttributes,
  							&IoStatusBlock,
  							FILE_SHARE_WRITE | FILE_SHARE_READ,
  							FILE_SYNCHRONOUS_IO_NONALERT);
  		}
  	}
  
  
  
  	//
  	// done
  	//
  
  	return Status;
  }
  
  
  static
  NTSTATUS
  _OpenDriveLetter(
      IN CHAR Letter,
      IN BOOLEAN ReadOnly,
      OUT PHANDLE Handle,
      OUT PBOOLEAN OpenedReadonly OPTIONAL
     )
  {
  	CHAR Buffer[100];
  
  	sprintf(Buffer, "\\DosDevices\\%c:", Letter);
  
  	return _OpenNtName(Buffer, ReadOnly, Handle, OpenedReadonly);
  }
  
  
  //
  // Flush device
  //
  
  static
  __inline
  NTSTATUS
  _FlushDrive(
  		IN HANDLE Handle
  		)
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	return NtFlushBuffersFile(Handle, &IoStatusBlock);
  }
  
  
  //
  // lock drive
  //
  
  static
  __inline
  NTSTATUS
  _LockDrive(
  		IN HANDLE Handle
  		)
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_LOCK_VOLUME, 0, 0, 0, 0);
  }
  
  
  //
  // unlock drive
  //
  
  static
  __inline
  NTSTATUS
  _UnlockDrive(
  	IN HANDLE Handle
  	)
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0);
  }
  
  static
  __inline
  NTSTATUS
  _DismountDrive(
  	IN HANDLE Handle
  	)
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0);
  }
  
  
  //
  // is mounted
  //
  
  static
  __inline
  BOOLEAN
  _IsMounted(
  	IN HANDLE Handle
  	)
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	NTSTATUS Status;
  	Status = NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_IS_VOLUME_MOUNTED, 0, 0, 0, 0);
  	return (BOOLEAN)(STATUS_SUCCESS == Status);
  }
  
  
  static
  __inline
  NTSTATUS
  _CloseDisk(
  		IN HANDLE Handle
  		)
  {
  	return NtClose(Handle);
  }
  
  
  
  
  //
  // Make NT name from any recognized name
  //
  
  static
  PCSTR
  _NormalizeDeviceName(
      IN PCSTR Device,
      IN PSTR NormalizedDeviceNameBuffer
     )
  {
  	int PartitionNumber = -1;
  	UCHAR DiskNumber;
  	PSTR p;
  
  
  	//
  	// Do not try to parse NT name
  	//
  
  	if('\\' == *Device)
  		return Device;
  
  
  
  	//
  	// Strip leading '/dev/' if any
  	//
  
  	if(('/' == *(Device)) &&
  		('d' == *(Device + 1)) &&
  		('e' == *(Device + 2)) &&
  		('v' == *(Device + 3)) &&
  		('/' == *(Device + 4)))
  	{
  		Device += 5;
  	}
  
  	if('\0' == *Device)
  	{
  		return NULL;
  	}
  
  
  	//
  	// forms: hda[n], fd[n]
  	//
  
  	if('d' != *(Device + 1))
  	{
  		return NULL;
  	}
  
  	if('h' == *Device)
  	{
  		if((*(Device + 2) < 'a') || (*(Device + 2) > ('a' + 9)) ||
  		   ((*(Device + 3) != '\0') &&
  			((*(Device + 4) != '\0') ||
  			 ((*(Device + 3) < '0') || (*(Device + 3) > '9'))
  			)
  		   )
  		  )
  		{
  			return NULL;
  		}
  
  		DiskNumber = (UCHAR)(*(Device + 2) - 'a');
  
  		if(*(Device + 3) != '\0')
  		{
  			PartitionNumber = (*(Device + 3) - '0');
  		}
  
  	}
  	else if('f' == *Device)
  	{
  		//
  		// 3-d letted should be a digit.
  		//
  
  		if((*(Device + 3) != '\0') ||
  		   (*(Device + 2) < '0') || (*(Device + 2) > '9'))
  		{
  			return NULL;
  		}
  
  		DiskNumber = (UCHAR)(*(Device + 2) - '0');
  
  	}
  	else
  	{
  		//
  		// invalid prefix
  		//
  
  		return NULL;
  	}
  
  
  
  	//
  	// Prefix
  	//
  
  	strcpy(NormalizedDeviceNameBuffer, "\\Device\\");
  
  	//
  	// Media name
  	//
  
  	switch(*Device)
  	{
  
  	case 'f':
  		strcat(NormalizedDeviceNameBuffer, "Floppy0");
  		break;
  
  	case 'h':
  		strcat(NormalizedDeviceNameBuffer, "Harddisk0");
  		break;
  	}
  
  
  	p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
  	*p = (CHAR)(*p + DiskNumber);
  
  
  	//
  	// Partition nr.
  	//
  
  	if(PartitionNumber >= 0)
  	{
  		strcat(NormalizedDeviceNameBuffer, "\\Partition0");
  
  		p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
  		*p = (CHAR)(*p + PartitionNumber);
  	}
  
  
  	return NormalizedDeviceNameBuffer;
  }
  
  
  
  
  static
  VOID
  _GetDeviceSize(
      IN HANDLE h,
      OUT unsigned __int64 *FsSize
     )
  {
  	PARTITION_INFORMATION pi;
  	DISK_GEOMETRY gi;
  	NTSTATUS Status;
  	IO_STATUS_BLOCK IoStatusBlock;
  
  	//
  	// Zero it
  	//
  
  	*FsSize = 0;
  
  	//
  	// Call driver
  	//
  
  	RtlZeroMemory(&pi, sizeof(PARTITION_INFORMATION));
  
  	Status = NtDeviceIoControlFile(
  		h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO,
  		&pi, sizeof(PARTITION_INFORMATION),
  		&pi, sizeof(PARTITION_INFORMATION));
  
  
  	if(NT_SUCCESS(Status))
  	{
  		*FsSize = pi.PartitionLength.QuadPart;
  	}
  	else if(STATUS_INVALID_DEVICE_REQUEST == Status)
  	{
  		//
  		// No partitions: get device info.
  		//
  
  		RtlZeroMemory(&gi, sizeof(DISK_GEOMETRY));
  
  		Status = NtDeviceIoControlFile(
  				h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY,
  				&gi, sizeof(DISK_GEOMETRY),
  				&gi, sizeof(DISK_GEOMETRY));
  
  
  		if(NT_SUCCESS(Status))
  		{
  			*FsSize =
  				gi.BytesPerSector *
  				gi.SectorsPerTrack *
  				gi.TracksPerCylinder *
  				gi.Cylinders.QuadPart;
  		}
  
  	}
  }
  
  
  
  //
  // Open device by name.
  //
  
  static
  BOOLEAN
  _Ext2OpenDevice(
      IN PCSTR Name,
      IN BOOLEAN ReadOnly,
      OUT PHANDLE Handle,
      OUT PBOOLEAN OpenedReadonly OPTIONAL,
      OUT unsigned *Errno OPTIONAL
     )
  {
  	CHAR NormalizedDeviceName[512];
  	NTSTATUS Status;
  
  	if(NULL == Name)
  	{
  		//
  		// Set not found
  		//
  
  		if(ARGUMENT_PRESENT(Errno))
  			*Errno = ENOENT;
  
  		return FALSE;
  	}
  
  
  	if((((*Name) | 0x20) >= 'a') && (((*Name) | 0x20) <= 'z') &&
  		(':' == *(Name + 1)) && ('\0' == *(Name + 2)))
  	{
  		Status = _OpenDriveLetter(*Name, ReadOnly, Handle, OpenedReadonly);
  	}
  	else
  	{
  		//
  		// Make name
  		//
  
  		Name = _NormalizeDeviceName(Name, NormalizedDeviceName);
  
  		if(NULL == Name)
  		{
  			//
  			// Set not found
  			//
  
  			if(ARGUMENT_PRESENT(Errno))
  				*Errno = ENOENT;
  
  			return FALSE;
  		}
  
  		//
  		// Try to open it
  		//
  
  		Status = _OpenNtName(Name, ReadOnly, Handle, OpenedReadonly);
  	}
  
  
  	if(!NT_SUCCESS(Status))
  	{
  		if(ARGUMENT_PRESENT(Errno))
  			*Errno = _MapNtStatus(Status);
  
  		return FALSE;
  	}
  
  	return TRUE;
  }
  
  
  //
  // Raw block io. Sets dos errno
  //
  
  static
  BOOLEAN
  _BlockIo(
      IN HANDLE Handle,
      IN LARGE_INTEGER Offset,
      IN ULONG Bytes,
      IN OUT PCHAR Buffer,
      IN BOOLEAN Read,
      OUT unsigned* Errno
     )
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	NTSTATUS Status;
  
  	//
  	// Should be aligned
  	//
  
  	ASSERT(0 == (Bytes % 512));
  	ASSERT(0 == (Offset.LowPart % 512));
  
  
  	//
  	// perform io
  	//
  
  	if(Read)
  	{
  		Status = NtReadFile(Handle, NULL, NULL, NULL,
  			&IoStatusBlock, Buffer, Bytes, &Offset, NULL);
  	}
  	else
  	{
  		Status = NtWriteFile(Handle, NULL, NULL, NULL,
  			&IoStatusBlock, Buffer, Bytes, &Offset, NULL);
  	}
  
  
  	//
  	// translate error
  	//
  
  	if(NT_SUCCESS(Status))
  	{
  		*Errno = 0;
  		return TRUE;
  	}
  
  	*Errno = _MapNtStatus(Status);
  
  	return FALSE;
  }
  
  
  
  __inline
  BOOLEAN
  _RawWrite(
      IN HANDLE Handle,
      IN LARGE_INTEGER Offset,
      IN ULONG Bytes,
      OUT const CHAR* Buffer,
      OUT unsigned* Errno
     )
  {
  	return _BlockIo(Handle, Offset, Bytes, (PCHAR)Buffer, FALSE, Errno);
  }
  
  __inline
  BOOLEAN
  _RawRead(
      IN HANDLE Handle,
      IN LARGE_INTEGER Offset,
      IN ULONG Bytes,
      IN PCHAR Buffer,
      OUT unsigned* Errno
     )
  {
  	return _BlockIo(Handle, Offset, Bytes, Buffer, TRUE, Errno);
  }
  
  
  
  __inline
  BOOLEAN
  _SetPartType(
      IN HANDLE Handle,
      IN UCHAR Type
     )
  {
  	IO_STATUS_BLOCK IoStatusBlock;
  	return STATUS_SUCCESS == NtDeviceIoControlFile(
  												   Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_SET_PARTITION_INFO,
  												   &Type, sizeof(Type),
  												   NULL, 0);
  }
  
  
  
  //--------------------- interface part
  
  //
  // Interface functions.
  // Is_mounted is set to 1 if the device is mounted, 0 otherwise
  //
  
  errcode_t
  ext2fs_check_if_mounted(const char *file, int *mount_flags)
  {
  	HANDLE h;
  	BOOLEAN Readonly;
  
  	*mount_flags = 0;
  
  	if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
  	{
  		return 0;
  	}
  
  
  	__try{
  		*mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0;
  	}
  	__finally{
  		_CloseDisk(h);
  	}
  
  	return 0;
  }
  
  
  
  //
  // Returns the number of blocks in a partition
  //
  
  static __int64 FsSize = 0;
  static char knowndevice[1024] = "";
  
  
  errcode_t
  ext2fs_get_device_size(const char *file, int blocksize,
  				 blk_t *retblocks)
  {
  	HANDLE h;
  	BOOLEAN Readonly;
  
  	if((0 == FsSize) || (0 != strcmp(knowndevice, file)))
  	{
  
  		if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
  		{
  			return 0;
  		}
  
  
  		__try{
  
  			//
  			// Get size
  			//
  
  			_GetDeviceSize(h, &FsSize);
  			strcpy(knowndevice, file);
  		}
  		__finally{
  			_CloseDisk(h);
  		}
  
  	}
  
  	*retblocks = (blk_t)(unsigned __int64)(FsSize / blocksize);
  	UNREFERENCED_PARAMETER(file);
  	return 0;
  }
  
  
  
  
  
  
  //
  // Table elements
  //
  
  
  static
  errcode_t
  nt_open(const char *name, int flags, io_channel *channel)
  {
  	io_channel      io = NULL;
  	PNT_PRIVATE_DATA NtData = NULL;
  	errcode_t Errno = 0;
  
  	//
  	// Check name
  	//
  
  	if (NULL == name)
  	{
  		return EXT2_ET_BAD_DEVICE_NAME;
  	}
  
  	__try{
  
  		//
  		// Allocate channel handle
  		//
  
  		io = (io_channel) malloc(sizeof(struct struct_io_channel));
  
  		if (NULL == io)
  		{
  			Errno = ENOMEM;
  			__leave;
  		}
  
  		RtlZeroMemory(io, sizeof(struct struct_io_channel));
  		io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
  
  		NtData = (PNT_PRIVATE_DATA)malloc(sizeof(NT_PRIVATE_DATA));
  
  		if (NULL == NtData)
  		{
  			Errno = ENOMEM;
  			__leave;
  		}
  
  
  		io->manager = nt_io_manager();
  		io->name = malloc(strlen(name) + 1);
  		if (NULL == io->name)
  		{
  			Errno = ENOMEM;
  			__leave;
  		}
  
  		strcpy(io->name, name);
  		io->private_data = NtData;
  		io->block_size = 1024;
  		io->read_error = 0;
  		io->write_error = 0;
  		io->refcount = 1;
  
  		//
  		// Initialize data
  		//
  
  		RtlZeroMemory(NtData, sizeof(NT_PRIVATE_DATA));
  
  		NtData->magic = EXT2_ET_MAGIC_NT_IO_CHANNEL;
  		NtData->BufferBlockNumber = 0xffffffff;
  		NtData->BufferSize = 1024;
  		NtData->Buffer = malloc(NtData->BufferSize);
  
  		if (NULL == NtData->Buffer)
  		{
  			Errno = ENOMEM;
  			__leave;
  		}
  
  		//
  		// Open it
  		//
  
  		if(!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &NtData->Handle, &NtData->OpenedReadonly, &Errno))
  		{
  			__leave;
  		}
  
  
  		//
  		// get size
  		//
  
  		_GetDeviceSize(NtData->Handle, &FsSize);
  		strcpy(knowndevice, name);
  
  
  		//
  		// Lock/dismount
  		//
  
  		if(!NT_SUCCESS(_LockDrive(NtData->Handle)) /*|| !NT_SUCCESS(_DismountDrive(NtData->Handle))*/)
  		{
  			NtData->OpenedReadonly = TRUE;
  		}
  
  		//
  		// Done
  		//
  
  		*channel = io;
  
  
  	}
  	__finally{
  
  		if(0 != Errno)
  		{
  			//
  			// Cleanup
  			//
  
  			if (NULL != io)
  			{
  				free(io->name);
  				free(io);
  			}
  
  			if (NULL != NtData)
  			{
  				if(NULL != NtData->Handle)
  				{
  					_UnlockDrive(NtData->Handle);
  					_CloseDisk(NtData->Handle);
  				}
  
  				free(NtData->Buffer);
  				free(NtData);
  			}
  		}
  	}
  
  	return Errno;
  }
  
  
  //
  // Close api
  //
  
  static
  errcode_t
  nt_close(io_channel channel)
  {
  	PNT_PRIVATE_DATA NtData = NULL;
  
  	if(NULL == channel)
  	{
  		return 0;
  	}
  
  	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
  	NtData = (PNT_PRIVATE_DATA) channel->private_data;
  	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
  
  	if (--channel->refcount > 0)
  	{
  		return 0;
  	}
  
  	free(channel->name);
  	free(channel);
  
  	if (NULL != NtData)
  	{
  		if(NULL != NtData->Handle)
  		{
  			_DismountDrive(NtData->Handle);
  			_UnlockDrive(NtData->Handle);
  			_CloseDisk(NtData->Handle);
  		}
  
  		free(NtData->Buffer);
  		free(NtData);
  	}
  
  	return 0;
  }
  
  
  
  //
  // set block size
  //
  
  static
  errcode_t
  nt_set_blksize(io_channel channel, int blksize)
  {
  	PNT_PRIVATE_DATA NtData = NULL;
  
  	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
  	NtData = (PNT_PRIVATE_DATA) channel->private_data;
  	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
  
  	if (channel->block_size != blksize)
  	{
  		channel->block_size = blksize;
  
  		free(NtData->Buffer);
  		NtData->BufferBlockNumber = 0xffffffff;
  		NtData->BufferSize = channel->block_size;
  		ASSERT(0 == (NtData->BufferSize % 512));
  
  		NtData->Buffer = malloc(NtData->BufferSize);
  
  		if (NULL == NtData->Buffer)
  		{
  			return ENOMEM;
  		}
  
  	}
  
  	return 0;
  }
  
  
  //
  // read block
  //
  
  static
  errcode_t
  nt_read_blk(io_channel channel, unsigned long block,
  			       int count, void *buf)
  {
  	PVOID BufferToRead;
  	ULONG SizeToRead;
  	ULONG Size;
  	LARGE_INTEGER Offset;
  	PNT_PRIVATE_DATA NtData = NULL;
  	unsigned Errno = 0;
  
  	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
  	NtData = (PNT_PRIVATE_DATA) channel->private_data;
  	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
  
  	//
  	// If it's in the cache, use it!
  	//
  
  	if ((1 == count) &&
  		(block == NtData->BufferBlockNumber) &&
  		(NtData->BufferBlockNumber != 0xffffffff))
  	{
  		memcpy(buf, NtData->Buffer, channel->block_size);
  		return 0;
  	}
  
  	Size = (count < 0) ? (ULONG)(-count) : (ULONG)(count * channel->block_size);
  
  	Offset.QuadPart = block * channel->block_size;
  
  	//
  	// If not fit to the block
  	//
  
  	if(Size <= NtData->BufferSize)
  	{
  		//
  		// Update the cache
  		//
  
  		NtData->BufferBlockNumber = block;
  		BufferToRead = NtData->Buffer;
  		SizeToRead = NtData->BufferSize;
  	}
  	else
  	{
  		SizeToRead = Size;
  		BufferToRead = buf;
  		ASSERT(0 == (SizeToRead % channel->block_size));
  	}
  
  	if(!_RawRead(NtData->Handle, Offset, SizeToRead, BufferToRead, &Errno))
  	{
  
  		if (channel->read_error)
  		{
  			return (channel->read_error)(channel, block, count, buf,
  					       Size, 0, Errno);
  		}
  		else
  		{
  			return Errno;
  		}
  	}
  
  
  	if(BufferToRead != buf)
  	{
  		ASSERT(Size <= SizeToRead);
  		memcpy(buf, BufferToRead, Size);
  	}
  
  	return 0;
  }
  
  
  //
  // write block
  //
  
  static
  errcode_t
  nt_write_blk(io_channel channel, unsigned long block,
  				int count, const void *buf)
  {
  	ULONG SizeToWrite;
  	LARGE_INTEGER Offset;
  	PNT_PRIVATE_DATA NtData = NULL;
  	unsigned Errno = 0;
  
  	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
  	NtData = (PNT_PRIVATE_DATA) channel->private_data;
  	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
  
  	if(NtData->OpenedReadonly)
  	{
  		return EACCES;
  	}
  
  	if (count == 1)
  	{
  		SizeToWrite = channel->block_size;
  	}
  	else
  	{
  		NtData->BufferBlockNumber = 0xffffffff;
  
  		if (count < 0)
  		{
  			SizeToWrite = (ULONG)(-count);
  		}
  		else
  		{
  			SizeToWrite = (ULONG)(count * channel->block_size);
  		}
  	}
  
  
  	ASSERT(0 == (SizeToWrite % 512));
  	Offset.QuadPart = block * channel->block_size;
  
  	if(!_RawWrite(NtData->Handle, Offset, SizeToWrite, buf, &Errno))
  	{
  		if (channel->write_error)
  		{
  			return (channel->write_error)(channel, block, count, buf,
  						SizeToWrite, 0, Errno);
  		}
  		else
  		{
  			return Errno;
  		}
  	}
  
  
  	//
  	// Stash a copy.
  	//
  
  	if(SizeToWrite >= NtData->BufferSize)
  	{
  		NtData->BufferBlockNumber = block;
  		memcpy(NtData->Buffer, buf, NtData->BufferSize);
  	}
  
  	NtData->Written = TRUE;
  
  	return 0;
  
  }
  
  
  
  //
  // Flush data buffers to disk.  Since we are currently using a
  // write-through cache, this is a no-op.
  //
  
  static
  errcode_t
  nt_flush(io_channel channel)
  {
  	PNT_PRIVATE_DATA NtData = NULL;
  
  	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
  	NtData = (PNT_PRIVATE_DATA) channel->private_data;
  	EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
  
  	if(NtData->OpenedReadonly)
  	{
  		return 0; // EACCESS;
  	}
  
  
  	//
  	// Flush file buffers.
  	//
  
  	_FlushDrive(NtData->Handle);
  
  
  	//
  	// Test and correct partition type.
  	//
  
  	if(NtData->Written)
  	{
  		_SetPartType(NtData->Handle, 0x83);
  	}
  
  	return 0;
  }
  
  
  