You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1495 lines
25 KiB
1495 lines
25 KiB
/*
|
|
* 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 = {
|
|
.magic = EXT2_ET_MAGIC_IO_MANAGER,
|
|
.name = "NT I/O Manager",
|
|
.open = nt_open,
|
|
.close = nt_close,
|
|
.set_blksize = nt_set_blksize,
|
|
.read_blk = nt_read_blk,
|
|
.write_blk = nt_write_blk,
|
|
.flush = 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 easier
|
|
//
|
|
|
|
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 input 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; // Unmappable 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 letter 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;
|
|
}
|
|
|
|
|