aboutsummaryrefslogtreecommitdiff
path: root/e2fslib/nt_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'e2fslib/nt_io.c')
-rw-r--r--e2fslib/nt_io.c1513
1 files changed, 1513 insertions, 0 deletions
diff --git a/e2fslib/nt_io.c b/e2fslib/nt_io.c
new file mode 100644
index 0000000..c39127c
--- /dev/null
+++ b/e2fslib/nt_io.c
@@ -0,0 +1,1513 @@
+/*
+ * 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 Public
+ * License.
+ * %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)
+ {
+ if(NULL != io->name)
+ {
+ free(io->name);
+ }
+
+ free(io);
+ }
+
+ if (NULL != NtData)
+ {
+ if(NULL != NtData->Handle)
+ {
+ _UnlockDrive(NtData->Handle);
+ _CloseDisk(NtData->Handle);
+ }
+
+ if(NULL != NtData->Buffer)
+ {
+ 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;
+ }
+
+ if(NULL != channel->name)
+ {
+ free(channel->name);
+ }
+
+
+ free(channel);
+
+ if (NULL != NtData)
+ {
+ if(NULL != NtData->Handle)
+ {
+ _DismountDrive(NtData->Handle);
+ _UnlockDrive(NtData->Handle);
+ _CloseDisk(NtData->Handle);
+ }
+
+ if(NULL != NtData->Buffer)
+ {
+ 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;
+}
+
+