/*
* 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;
}