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