/* * 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 * Copyright (C) 2018-2019 Pete Batard * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #include #include #include #include #include #include #include "config.h" #include "ext2fs.h" #include "rufus.h" #include "msapi_utf8.h" extern char* NtStatusError(NTSTATUS Status); PF_TYPE_DECL(NTAPI, ULONG, RtlNtStatusToDosError, (NTSTATUS)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtClose, (HANDLE)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtOpenFile, (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtFlushBuffersFile, (HANDLE, PIO_STATUS_BLOCK)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtReadFile, (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtWriteFile, (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtDeviceIoControlFile, (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtFsControlFile, (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG)); PF_TYPE_DECL(NTAPI, NTSTATUS, NtDelayExecution, (BOOLEAN, PLARGE_INTEGER)); // TODO: Sort out ASSERT and __attribute__(align) #define ASSERT(x) #define ARGUMENT_PRESENT(ArgumentPointer) ((CHAR *)((ULONG_PTR)(ArgumentPointer)) != (CHAR *)(NULL)) #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) #define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS)0xC0000010L) #define BooleanFlagOn(Flags, SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0))) #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; char* buffer; __u32 buffer_block_number; ULONG buffer_size; BOOLEAN read_only; 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 }; io_manager nt_io_manager(void) { return &struct_nt_manager; } // 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; 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; } // Map NT status to dos error. static __inline unsigned _MapNtStatus(IN NTSTATUS Status) { PF_INIT(RtlNtStatusToDosError, Ntdll); return (pfRtlNtStatusToDosError == NULL) ? EFAULT: _MapDosError(pfRtlNtStatusToDosError(Status)); } // // Helper functions // static NTSTATUS _OpenNtName(IN PCSTR Name, IN BOOLEAN Readonly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL) { // ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; WCHAR Buffer[512]; NTSTATUS Status = EFAULT; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtDelayExecution, Ntdll); PF_INIT_OR_OUT(NtOpenFile, Ntdll); utf8_to_wchar_no_alloc(Name, Buffer, ARRAYSIZE(Buffer)); // Make Unicode name from input string UnicodeString.Buffer = Buffer; UnicodeString.Length = (USHORT) wcslen(Buffer) * 2; UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!! // Initialize object InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); // Try to open it in initial mode if (ARGUMENT_PRESENT(OpenedReadonly)) *OpenedReadonly = Readonly; Status = pfNtOpenFile(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)) { uprintf("_OpenNtName: [%x] %s", Status, NtStatusError(Status)); // Maybe was just mounted? wait 0.5 sec and retry. LARGE_INTEGER Interval; Interval.QuadPart = -5000000; // 0.5 sec. from now if (pfNtDelayExecution != NULL) pfNtDelayExecution(FALSE, &Interval); else Sleep(500); Status = pfNtOpenFile(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 == STATUS_ACCESS_DENIED) && !Readonly) { if (ARGUMENT_PRESENT(OpenedReadonly)) *OpenedReadonly = TRUE; Status = pfNtOpenFile(Handle, SYNCHRONIZE | FILE_READ_DATA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_WRITE | FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); } } out: 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); } static __inline NTSTATUS _FlushDrive(IN HANDLE Handle) { IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtFlushBuffersFile, NtDll); return (pfNtFlushBuffersFile == NULL) ? STATUS_DLL_NOT_FOUND : pfNtFlushBuffersFile(Handle, &IoStatusBlock); } static __inline NTSTATUS _LockDrive(IN HANDLE Handle) { IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtFsControlFile, NtDll); return (pfNtFsControlFile == NULL) ? STATUS_DLL_NOT_FOUND : pfNtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_LOCK_VOLUME, 0, 0, 0, 0); } static __inline NTSTATUS _UnlockDrive(IN HANDLE Handle) { IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtFsControlFile, NtDll); return (pfNtFsControlFile == NULL) ? STATUS_DLL_NOT_FOUND : pfNtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0); } static __inline NTSTATUS _DismountDrive(IN HANDLE Handle) { IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtFsControlFile, NtDll); return (pfNtFsControlFile == NULL) ? STATUS_DLL_NOT_FOUND : pfNtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0); } static __inline BOOLEAN _IsMounted(IN HANDLE Handle) { IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status = STATUS_DLL_NOT_FOUND; PF_INIT(NtFsControlFile, NtDll); if (pfNtFsControlFile != NULL) pfNtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_IS_VOLUME_MOUNTED, 0, 0, 0, 0); return (BOOLEAN)(Status == STATUS_SUCCESS); } static __inline NTSTATUS _CloseDisk(IN HANDLE Handle) { PF_INIT(NtClose, Ntdll); return (pfNtClose == NULL) ? STATUS_DLL_NOT_FOUND : pfNtClose(Handle); } // // Make NT name from any recognized name // static PCSTR _NormalizeDeviceName(IN PCSTR Device, IN PSTR NormalizedDeviceNameBuffer) { int PartitionNumber = -1; UCHAR DiskNumber; PSTR p; // Convert non NT paths to NT if (Device[0] == '\\') { if ((strlen(Device) < 4) || (Device[3] != '\\')) return Device; strcpy(NormalizedDeviceNameBuffer, Device); if ((NormalizedDeviceNameBuffer[1] == '\\') || (NormalizedDeviceNameBuffer[1] == '.')) NormalizedDeviceNameBuffer[1] = '?'; if (NormalizedDeviceNameBuffer[2] == '.') NormalizedDeviceNameBuffer[2] = '?'; return NormalizedDeviceNameBuffer; } // For now, don't allow the conversion of non absolute paths. // Too easy to get a C:\ drive altered on a mishap otherwise... return NULL; // Strip leading '/dev/' if any if ((Device[0] == '/') && (Device[1] == 'd') && (Device[2] == 'e') && (Device[3] == 'v') && (Device[4] == '/')) { Device = &Device[5]; } if (Device[0] == '\0') { return NULL; } // forms: hda[n], sda[n], fd[n] if (Device[1] != 'd') { return NULL; } if ((Device[0] == 'h') || (Device[0] == 's')) { 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 (Device[0] == 'f') { // 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[0] = (CHAR)(p[0] + DiskNumber); // Partition nr. if (PartitionNumber >= 0) { strcat(NormalizedDeviceNameBuffer, "\\Partition0"); p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1; p[0] = (CHAR)(p[0] + PartitionNumber); } return NormalizedDeviceNameBuffer; } static VOID _GetDeviceSize(IN HANDLE h, OUT unsigned __int64 *FsSize) { PARTITION_INFORMATION_EX pi; DISK_GEOMETRY_EX gi; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; LARGE_INTEGER li; *FsSize = 0; PF_INIT(NtDeviceIoControlFile, NtDll); if (pfNtDeviceIoControlFile == NULL) return; RtlZeroMemory(&pi, sizeof(pi)); Status = pfNtDeviceIoControlFile(h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO_EX, &pi, sizeof(pi), &pi, sizeof(pi)); if (NT_SUCCESS(Status)) { uprintf("Size retrieved with: IOCTL_DISK_GET_PARTITION_INFO_EX"); *FsSize = pi.PartitionLength.QuadPart; } else if (Status == STATUS_INVALID_DEVICE_REQUEST) { // No partitions: Try a drive geometry request uprintf("IOCTL_DISK_GET_PARTITION_INFO_EX failed, trying with IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); RtlZeroMemory(&gi, sizeof(gi)); Status = pfNtDeviceIoControlFile(h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, &gi, sizeof(gi), &gi, sizeof(gi)); if (NT_SUCCESS(Status)) { uprintf("Size retrieved with: IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); *FsSize = gi.DiskSize.QuadPart; } else { uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: [%x] %s", Status, NtStatusError(Status)); } } else if (Status == STATUS_INVALID_PARAMETER) { // Possibly a straight image file if (GetFileSizeEx(h, &li)) *FsSize = li.QuadPart; } } static BOOLEAN _Ext2OpenDevice(IN PCSTR Name, IN BOOLEAN ReadOnly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL, OUT errcode_t *Errno OPTIONAL) { CHAR NormalizedDeviceName[512]; NTSTATUS Status; if (Name == NULL) { 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 { Name = _NormalizeDeviceName(Name, NormalizedDeviceName); if (Name == NULL) { if (ARGUMENT_PRESENT(Errno)) *Errno = ENOENT; return FALSE; } Status = _OpenNtName(Name, ReadOnly, Handle, OpenedReadonly); } if (!NT_SUCCESS(Status)) { if (ARGUMENT_PRESENT(Errno)) *Errno = _MapNtStatus(Status); return FALSE; } return TRUE; } 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 = STATUS_DLL_NOT_FOUND; PF_INIT_OR_OUT(NtReadFile, NtDll); PF_INIT_OR_OUT(NtWriteFile, NtDll); // Should be aligned ASSERT(0 == (Bytes % 512)); ASSERT(0 == (Offset.LowPart % 512)); // Perform io if(Read) { Status = pfNtReadFile(Handle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Bytes, &Offset, NULL); } else { Status = pfNtWriteFile(Handle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Bytes, &Offset, NULL); } out: if (NT_SUCCESS(Status)) { *Errno = 0; return TRUE; } *Errno = _MapNtStatus(Status); return FALSE; } static 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); } static 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); } static BOOLEAN _SetPartType(IN HANDLE Handle, IN UCHAR Type) { IO_STATUS_BLOCK IoStatusBlock; PF_INIT(NtDeviceIoControlFile, NtDll); if (pfNtDeviceIoControlFile == NULL) return FALSE; return NT_SUCCESS(pfNtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_SET_PARTITION_INFO, &Type, sizeof(Type), NULL, 0)); } // // 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; *mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0; _CloseDisk(h); return 0; } // Returns the number of blocks in a partition // Note: Do *NOT* be tempted to cache the device size according to the NT path as // different removable devices (e.g. UFD) may be remounted under the same path. errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk64_t *retblocks) { __int64 fs_size = 0; HANDLE h; BOOLEAN Readonly; if (!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL)) { uprintf("FAILED TO OPEN '%s'", file); return EACCES; } _GetDeviceSize(h, &fs_size); _CloseDisk(h); *retblocks = (blk64_t)(fs_size / blocksize); return 0; } // // Table elements // static errcode_t nt_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; PNT_PRIVATE_DATA nt_data = NULL; errcode_t errcode = 0; if (name == NULL) return EXT2_ET_BAD_DEVICE_NAME; // Allocate channel handle io = (io_channel) malloc(sizeof(struct struct_io_channel)); if (io == NULL) { errcode = ENOMEM; goto out; } RtlZeroMemory(io, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; nt_data = (PNT_PRIVATE_DATA) malloc(sizeof(NT_PRIVATE_DATA)); if (nt_data == NULL) { errcode = ENOMEM; goto out; } io->manager = nt_io_manager(); io->name = malloc(strlen(name) + 1); if (io->name == NULL) { errcode = ENOMEM; goto out; } strcpy(io->name, name); io->private_data = nt_data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; // Initialize data RtlZeroMemory(nt_data, sizeof(NT_PRIVATE_DATA)); nt_data->magic = EXT2_ET_MAGIC_NT_IO_CHANNEL; nt_data->buffer_block_number = 0xffffffff; nt_data->buffer_size = 1024; nt_data->buffer = malloc(nt_data->buffer_size); if (nt_data->buffer == NULL) { errcode = ENOMEM; goto out; } // Open the device if (!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &nt_data->handle, &nt_data->read_only, &errcode)) goto out; // Get the size // _GetDeviceSize(nt_data->handle, &fs_size); // strcpy(known_device, name); // Lock/dismount // if (!NT_SUCCESS(_LockDrive(nt_data->handle)) /*|| !NT_SUCCESS(_DismountDrive(NtData->Handle))*/) // nt_data->read_only = TRUE; // Done *channel = io; out: if (errcode) { if (io != NULL) { free(io->name); free(io); } if (nt_data != NULL) { if (nt_data->handle != NULL) { _UnlockDrive(nt_data->handle); _CloseDisk(nt_data->handle); } free(nt_data->buffer); free(nt_data); } } return errcode; } static errcode_t nt_close(io_channel channel) { PNT_PRIVATE_DATA nt_data = NULL; if (channel == NULL) return 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); nt_data = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(nt_data, EXT2_ET_MAGIC_NT_IO_CHANNEL); if (--channel->refcount > 0) return 0; free(channel->name); free(channel); if (nt_data != NULL) { if (nt_data->handle != NULL) CloseHandle(nt_data->handle); free(nt_data->buffer); free(nt_data); } return 0; } static errcode_t nt_set_blksize(io_channel channel, int blksize) { PNT_PRIVATE_DATA nt_data = NULL; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); nt_data = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(nt_data, EXT2_ET_MAGIC_NT_IO_CHANNEL); if (channel->block_size != blksize) { channel->block_size = blksize; free(nt_data->buffer); nt_data->buffer_block_number = 0xffffffff; nt_data->buffer_size = channel->block_size; ASSERT(0 == (nt_data->BufferSize % 512)); nt_data->buffer = malloc(nt_data->buffer_size); if (nt_data->buffer == NULL) return ENOMEM; } return 0; } static errcode_t nt_read_blk(io_channel channel, unsigned long block, int count, void *buf) { PVOID read_buffer; ULONG read_size; ULONG size; LARGE_INTEGER offset; PNT_PRIVATE_DATA nt_data = NULL; unsigned errcode = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); nt_data = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(nt_data, EXT2_ET_MAGIC_NT_IO_CHANNEL); // If it's in the cache, use it! if ((count == 1) && (block == nt_data->buffer_block_number) && (nt_data->buffer_block_number != 0xffffffff)) { memcpy(buf, nt_data->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 <= nt_data->buffer_size) { // Update the cache nt_data->buffer_block_number = block; read_buffer = nt_data->buffer; read_size = nt_data->buffer_size; } else { read_size = size; read_buffer = buf; ASSERT((read_size % channel->block_size) == 0); } if (!_RawRead(nt_data->handle, offset, read_size, read_buffer, &errcode)) { if (channel->read_error) return (channel->read_error)(channel, block, count, buf, size, 0, errcode); else return errcode; } if (read_buffer != buf) { ASSERT(size <= read_size); memcpy(buf, read_buffer, size); } return 0; } // TODO: Add an nt_write_blk64() static errcode_t nt_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { ULONG write_size; LARGE_INTEGER offset; PNT_PRIVATE_DATA nt_data = NULL; unsigned errcode = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); nt_data = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(nt_data, EXT2_ET_MAGIC_NT_IO_CHANNEL); if (nt_data->read_only) return EACCES; if (count == 1) { write_size = channel->block_size; } else { nt_data->buffer_block_number = 0xffffffff; if (count < 0) write_size = (ULONG)(-count); else write_size = (ULONG)(count * channel->block_size); } ASSERT(0 == (write_size % 512)); offset.QuadPart = block * channel->block_size; if (!_RawWrite(nt_data->handle, offset, write_size, buf, &errcode)) { if (channel->write_error) return (channel->write_error)(channel, block, count, buf, write_size, 0, errcode); else return errcode; } // Stash a copy. if(write_size >= nt_data->buffer_size) { nt_data->buffer_block_number = block; memcpy(nt_data->buffer, buf, nt_data->buffer_size); } nt_data->written = TRUE; return 0; } static errcode_t nt_flush(io_channel channel) { PNT_PRIVATE_DATA nt_data = NULL; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); nt_data = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(nt_data, EXT2_ET_MAGIC_NT_IO_CHANNEL); if(nt_data->read_only) return 0; // Flush file buffers. _FlushDrive(nt_data->handle); // Test and correct partition type. if (nt_data->written) _SetPartType(nt_data->handle, 0x83); return 0; }