diff --git a/cpl/amd64/imdisk.exp b/cpl/amd64/imdisk.exp index cf4a3ea..8259838 100644 Binary files a/cpl/amd64/imdisk.exp and b/cpl/amd64/imdisk.exp differ diff --git a/cpl/amd64/imdisk.lib b/cpl/amd64/imdisk.lib index d167600..9d937a8 100644 Binary files a/cpl/amd64/imdisk.lib and b/cpl/amd64/imdisk.lib differ diff --git a/cpl/i386/imdisk.exp b/cpl/i386/imdisk.exp index bd7f113..b803adb 100644 Binary files a/cpl/i386/imdisk.exp and b/cpl/i386/imdisk.exp differ diff --git a/cpl/i386/imdisk.lib b/cpl/i386/imdisk.lib index 2d1a50b..7f2fdf2 100644 Binary files a/cpl/i386/imdisk.lib and b/cpl/i386/imdisk.lib differ diff --git a/cpl/ia64/imdisk.exp b/cpl/ia64/imdisk.exp index 4c1a8d3..da69bec 100644 Binary files a/cpl/ia64/imdisk.exp and b/cpl/ia64/imdisk.exp differ diff --git a/cpl/ia64/imdisk.lib b/cpl/ia64/imdisk.lib index 14bb826..71facc8 100644 Binary files a/cpl/ia64/imdisk.lib and b/cpl/ia64/imdisk.lib differ diff --git a/cpl/imdisk.cpl.manifest b/cpl/imdisk.cpl.manifest new file mode 100644 index 0000000..9fbfa96 --- /dev/null +++ b/cpl/imdisk.cpl.manifest @@ -0,0 +1,30 @@ + + + + ImDisk Virtual Disk Driver + + + + + + + + + + + + + diff --git a/cplcore/amd64/imdisk.exp b/cplcore/amd64/imdisk.exp index 2c58e0f..2393fc2 100644 Binary files a/cplcore/amd64/imdisk.exp and b/cplcore/amd64/imdisk.exp differ diff --git a/cplcore/amd64/imdisk.lib b/cplcore/amd64/imdisk.lib index 9fadc50..6f9555d 100644 Binary files a/cplcore/amd64/imdisk.lib and b/cplcore/amd64/imdisk.lib differ diff --git a/cplcore/i386/imdisk.exp b/cplcore/i386/imdisk.exp index f19296a..55e5248 100644 Binary files a/cplcore/i386/imdisk.exp and b/cplcore/i386/imdisk.exp differ diff --git a/cplcore/i386/imdisk.lib b/cplcore/i386/imdisk.lib index 2876559..76df4fd 100644 Binary files a/cplcore/i386/imdisk.lib and b/cplcore/i386/imdisk.lib differ diff --git a/cplcore/ia64/imdisk.exp b/cplcore/ia64/imdisk.exp index 2d7568a..e5de361 100644 Binary files a/cplcore/ia64/imdisk.exp and b/cplcore/ia64/imdisk.exp differ diff --git a/cplcore/ia64/imdisk.lib b/cplcore/ia64/imdisk.lib index 07a97f6..b9b2126 100644 Binary files a/cplcore/ia64/imdisk.lib and b/cplcore/ia64/imdisk.lib differ diff --git a/devio/devio.props b/devio/devio.props new file mode 100644 index 0000000..4f3dc0c --- /dev/null +++ b/devio/devio.props @@ -0,0 +1,12 @@ + + + + + + + + _WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + + + + \ No newline at end of file diff --git a/imdisk.inf b/imdisk.inf index 6cb78ad..82cada0 100644 --- a/imdisk.inf +++ b/imdisk.inf @@ -1,7 +1,7 @@ [Version] signature = "$Windows NT$" Provider = "LTR Data" -DriverVer=12/08/2015,6.0.6001.18000 +DriverVer=12/14/2015,6.0.6001.18000 [SourceDisksNames] diff --git a/inc/imdiskver.h b/inc/imdiskver.h index 957d9a5..c7a758a 100644 --- a/inc/imdiskver.h +++ b/inc/imdiskver.h @@ -1,6 +1,6 @@ -#define IMDISK_RC_VERSION_STR "2.0.8" +#define IMDISK_RC_VERSION_STR "2.0.9" #define IMDISK_MAJOR_VERSION 2 #define IMDISK_MINOR_VERSION 0 -#define IMDISK_MINOR_LOW_VERSION 8 +#define IMDISK_MINOR_LOW_VERSION 9 #define IMDISK_RC_VERSION_FLD IMDISK_MAJOR_VERSION,IMDISK_MINOR_VERSION,IMDISK_MINOR_LOW_VERSION diff --git a/inc/wkmem.hpp b/inc/wkmem.hpp new file mode 100644 index 0000000..87866f6 --- /dev/null +++ b/inc/wkmem.hpp @@ -0,0 +1,205 @@ +#pragma once + +inline void *operator_new(size_t Size, UCHAR FillByte) +{ + void * result = ExAllocatePoolWithTag(NonPagedPool, Size, POOL_TAG); + + if (result != NULL) + { + RtlFillMemory(result, Size, FillByte); + } + + return result; +} + +inline void * __CRTDECL operator new(size_t Size) +{ + return operator_new(Size, 0); +} + +inline void * __CRTDECL operator new[](size_t Size) +{ + return operator_new(Size, 0); +} + +inline void * __CRTDECL operator new(size_t Size, UCHAR FillByte) +{ + return operator_new(Size, FillByte); +} + +inline void operator_delete(void *Ptr) +{ + if (Ptr != NULL) + { + ExFreePoolWithTag(Ptr, POOL_TAG); + } +} + +inline void __CRTDECL operator delete(void * Ptr) +{ + operator_delete(Ptr); +} + +inline void __CRTDECL operator delete(void * Ptr, size_t) +{ + operator_delete(Ptr); +} + +inline void __CRTDECL operator delete[](void * Ptr) +{ + operator_delete(Ptr); +} + +template class WPoolMem +{ +protected: + T *ptr; + SIZE_T bytecount; + + explicit WPoolMem(T *pBlk, SIZE_T AllocationSize) + : ptr(pBlk), + bytecount(pBlk != NULL ? AllocationSize : 0) { } + +public: + operator bool() + { + return ptr != NULL; + } + + bool operator!() + { + return ptr == NULL; + } + + operator T*() + { + return ptr; + } + + T* operator ->() + { + return ptr; + } + + T* operator+(int i) + { + return ptr + i; + } + + T* operator-(int i) + { + return ptr - i; + } + + T* operator =(T *pBlk) + { + Free(); + return ptr = pBlk; + } + + SIZE_T Count() const + { + return GetSize() / sizeof(T); + } + + SIZE_T GetSize() const + { + return ptr != NULL ? bytecount : 0; + } + + void Free() + { + if (ptr != NULL) + { + ExFreePoolWithTag(ptr, POOL_TAG); + ptr = NULL; + } + } + + void Clear() + { + if ((ptr != NULL) && (bytecount > 0)) + { + RtlZeroMemory(ptr, bytecount); + } + } + + T* Abandon() + { + T* ab_ptr = ptr; + ptr = NULL; + bytecount = 0; + return ab_ptr; + } + + ~WPoolMem() + { + Free(); + } + + void Initialize(SIZE_T AllocateSize) + { + ptr = (T*)ExAllocatePoolWithTag(pool_type, AllocateSize, POOL_TAG); + bytecount = AllocateSize; + } + +public: + WPoolMem() : + ptr(NULL), + bytecount(0) { } + + explicit WPoolMem(SIZE_T AllocateSize) + { + Initialize(AllocateSize); + } + + T* Alloc(SIZE_T AllocateSize) + { + Free(); + Initialize(AllocateSize); + return ptr; + } +}; + +class WHandle +{ +private: + HANDLE h; + +public: + operator bool() + { + return h != NULL; + } + + bool operator !() + { + return h == NULL; + } + + operator HANDLE() + { + return h; + } + + void Close() + { + if (h != NULL) + { + ZwClose(h); + h = NULL; + } + } + + WHandle() : + h(NULL) { } + + explicit WHandle(HANDLE h) : + h(h) { } + + ~WHandle() + { + Close(); + } +}; + diff --git a/readme.txt b/readme.txt index 9aae8ee..52c55cc 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ - ImDisk Virtual Disk Driver for Windows NT/2000/XP/2003/Vista/7/8/8.1. + ImDisk Virtual Disk Driver for Windows NT/2000/XP. This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM drives from disk image files, in virtual memory or by redirecting I/O @@ -21,7 +21,7 @@ you want to use this product under NT 3.51 you have to manually add the registry entries for the driver and the service. - Copyright (c) 2005-2015 Olof Lagerkvist + Copyright (c) 2005-2009 Olof Lagerkvist http://www.ltr-data.se olof@ltr-data.se Permission is hereby granted, free of charge, to any person diff --git a/sys/commonio.cpp b/sys/commonio.cpp new file mode 100644 index 0000000..ff09d3d --- /dev/null +++ b/sys/commonio.cpp @@ -0,0 +1,326 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +// +// Reads in a loop up to "Length" or until eof reached. +// +NTSTATUS +ImDiskSafeReadFile(IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN SIZE_T Length, + IN PLARGE_INTEGER Offset) +{ + NTSTATUS status = STATUS_SUCCESS; + SIZE_T length_done = 0; + PUCHAR intermediate_buffer = NULL; + ULONG request_length; + + ASSERT(FileHandle != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(Buffer != NULL); + + if (Length > (8UL << 20)) + { + request_length = (8UL << 20); + } + else + { + request_length = (ULONG)Length; + } + + while (length_done < Length) + { + SIZE_T LongRequestLength = Length - length_done; + if (LongRequestLength < request_length) + { + request_length = (ULONG)LongRequestLength; + } + + for (;;) + { + LARGE_INTEGER current_file_offset; + + current_file_offset.QuadPart = Offset->QuadPart + length_done; + + if (intermediate_buffer == NULL) + { + intermediate_buffer = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, + request_length, + POOL_TAG); + + if (intermediate_buffer == NULL) + { + DbgPrint("ImDisk: ImDiskSafeReadFile: Insufficient paged pool to allocate " + "intermediate buffer (%u bytes).\n", request_length); + + IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + } + + status = ZwReadFile(FileHandle, + NULL, + NULL, + NULL, + IoStatusBlock, + intermediate_buffer, + request_length, + ¤t_file_offset, + NULL); + + if (((status == STATUS_INSUFFICIENT_RESOURCES) | + (status == STATUS_INVALID_BUFFER_SIZE) | + (status == STATUS_INVALID_PARAMETER)) & + (request_length >= 2048)) + { + ExFreePoolWithTag(intermediate_buffer, POOL_TAG); + intermediate_buffer = NULL; + + DbgPrint("ImDisk: ImDiskSafeReadFile: ZwReadFile error reading " + "%u bytes. Retrying with smaller read size. (Status 0x%X)\n", + request_length, + status); + + request_length >>= 2; + + continue; + } + + if (!NT_SUCCESS(status)) + { + DbgPrint("ImDisk: ImDiskSafeReadFile: ZwReadFile error reading " + "%u bytes. (Status 0x%X)\n", + request_length, + status); + + break; + } + + RtlCopyMemory((PUCHAR)Buffer + length_done, intermediate_buffer, + IoStatusBlock->Information); + + break; + } + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Information = length_done; + break; + } + + if (IoStatusBlock->Information == 0) + { + DbgPrint("ImDisk: ImDiskSafeReadFile: IoStatusBlock->Information == 0, " + "returning STATUS_CONNECTION_RESET.\n"); + + status = STATUS_CONNECTION_RESET; + break; + } + + KdPrint(("ImDisk: ImDiskSafeReadFile: Done %u bytes.\n", + (ULONG)IoStatusBlock->Information)); + + length_done += IoStatusBlock->Information; + } + + if (intermediate_buffer != NULL) + { + ExFreePoolWithTag(intermediate_buffer, POOL_TAG); + intermediate_buffer = NULL; + } + + if (!NT_SUCCESS(status)) + { + DbgPrint("ImDisk: ImDiskSafeReadFile: Error return " + "(Status 0x%X)\n", status); + } + else + { + KdPrint(("ImDisk: ImDiskSafeReadFile: Successful.\n")); + } + + IoStatusBlock->Status = status; + IoStatusBlock->Information = length_done; + return status; +} + +NTSTATUS +ImDiskSafeIOStream(IN PFILE_OBJECT FileObject, + IN UCHAR MajorFunction, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent, + IN OUT PVOID Buffer, + IN ULONG Length) +{ + NTSTATUS status; + ULONG length_done = 0; + KEVENT io_complete_event; + PIO_STACK_LOCATION io_stack; + LARGE_INTEGER offset = { 0 }; + PKEVENT wait_object[] = { + &io_complete_event, + CancelEvent + }; + ULONG number_of_wait_objects = CancelEvent != NULL ? 2 : 1; + + KdPrint2(("ImDiskSafeIOStream: FileObject=%#x, MajorFunction=%#x, " + "IoStatusBlock=%#x, Buffer=%#x, Length=%#x.\n", + FileObject, MajorFunction, IoStatusBlock, Buffer, Length)); + + ASSERT(FileObject != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(Buffer != NULL); + + KeInitializeEvent(&io_complete_event, + NotificationEvent, + FALSE); + + while (length_done < Length) + { + ULONG RequestLength = Length - length_done; + + do + { + PIRP irp; + PDEVICE_OBJECT device_object = IoGetRelatedDeviceObject(FileObject); + + KdPrint2(("ImDiskSafeIOStream: Building IRP...\n")); + +#pragma warning(suppress: 6102) + irp = IoBuildSynchronousFsdRequest(MajorFunction, + device_object, + (PUCHAR)Buffer + length_done, + RequestLength, + &offset, + &io_complete_event, + IoStatusBlock); + + if (irp == NULL) + { + KdPrint(("ImDiskSafeIOStream: Error building IRP.\n")); + + IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + KdPrint2(("ImDiskSafeIOStream: Built IRP=%#x.\n", irp)); + + io_stack = IoGetNextIrpStackLocation(irp); + io_stack->FileObject = FileObject; + + KdPrint2(("ImDiskSafeIOStream: MajorFunction=%#x, Length=%#x\n", + io_stack->MajorFunction, + io_stack->Parameters.Read.Length)); + + KeClearEvent(&io_complete_event); + + status = IoCallDriver(device_object, irp); + + if (status == STATUS_PENDING) + { + status = KeWaitForMultipleObjects(number_of_wait_objects, + (PVOID*)wait_object, + WaitAny, + Executive, + KernelMode, + FALSE, + NULL, + NULL); + + if (KeReadStateEvent(&io_complete_event) == 0) + { + IoCancelIrp(irp); + KeWaitForSingleObject(&io_complete_event, + Executive, + KernelMode, + FALSE, + NULL); + } + } + else if (!NT_SUCCESS(status)) + break; + + status = IoStatusBlock->Status; + + KdPrint2(("ImDiskSafeIOStream: IRP %#x completed. Status=%#x.\n", + irp, IoStatusBlock->Status)); + + RequestLength >>= 1; + } while ((status == STATUS_INVALID_BUFFER_SIZE) | + (status == STATUS_INVALID_PARAMETER)); + + if (!NT_SUCCESS(status)) + { + KdPrint2(("ImDiskSafeIOStream: I/O failed. Status=%#x.\n", status)); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint2(("ImDiskSafeIOStream: I/O done. Status=%#x. Length=%#x\n", + status, IoStatusBlock->Information)); + + if (IoStatusBlock->Information == 0) + { + IoStatusBlock->Status = STATUS_CONNECTION_RESET; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + length_done += (ULONG)IoStatusBlock->Information; + } + + KdPrint2(("ImDiskSafeIOStream: I/O complete.\n")); + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; +} + diff --git a/sys/createdev.cpp b/sys/createdev.cpp new file mode 100644 index 0000000..e2e71ad --- /dev/null +++ b/sys/createdev.cpp @@ -0,0 +1,2147 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +#ifdef INCLUDE_VFD_ORIGIN + +// For floppy devices. Based on Virtual Floppy Driver, VFD, by Ken Kato. +#define SECTOR_SIZE_FDD 512 +#define TRACKS_PER_CYLINDER_FDD 12 +// +// Sizes in bytes of floppies +// +// 3.5" UHD +#define MEDIA_SIZE_240MB (234752 << 10) +#define MEDIA_SIZE_120MB (123264 << 10) +// 3.5" +#define MEDIA_SIZE_2800KB (2880 << 10) +#define MEDIA_SIZE_1722KB (1722 << 10) +#define MEDIA_SIZE_1680KB (1680 << 10) +#define MEDIA_SIZE_1440KB (1440 << 10) +#define MEDIA_SIZE_820KB (820 << 10) +#define MEDIA_SIZE_720KB (720 << 10) +// 5.25" +#define MEDIA_SIZE_1200KB (1200 << 10) +#define MEDIA_SIZE_640KB (640 << 10) +#define MEDIA_SIZE_360KB (360 << 10) +#define MEDIA_SIZE_320KB (320 << 10) +#define MEDIA_SIZE_180KB (180 << 10) +#define MEDIA_SIZE_160KB (160 << 10) + +// +// Indexes for the following DISK_GEOMETRY table. +// +enum { + // 3.5" UHD + MEDIA_TYPE_240M, + MEDIA_TYPE_120M, + // 3.5" + MEDIA_TYPE_2880K, + MEDIA_TYPE_1722K, + MEDIA_TYPE_1680K, + MEDIA_TYPE_1440K, + MEDIA_TYPE_820K, + MEDIA_TYPE_720K, + // 5.12" + MEDIA_TYPE_1200K, + MEDIA_TYPE_640K, + MEDIA_TYPE_360K, + MEDIA_TYPE_320K, + MEDIA_TYPE_180K, + MEDIA_TYPE_160K +}; + +const DISK_GEOMETRY media_table[] = { + // 3.5" UHD + { { 963 }, F3_120M_512, 8, 32, 512 }, + { { 262 }, F3_120M_512, 32, 56, 512 }, + // 3.5" + { { 80 }, F3_2Pt88_512, 2, 36, 512 }, + { { 82 }, F3_1Pt44_512, 2, 21, 512 }, + { { 80 }, F3_1Pt44_512, 2, 21, 512 }, + { { 80 }, F3_1Pt44_512, 2, 18, 512 }, + { { 82 }, F3_720_512, 2, 10, 512 }, + { { 80 }, F3_720_512, 2, 9, 512 }, + // 5.25" + { { 80 }, F5_1Pt2_512, 2, 15, 512 }, + { { 40 }, F5_640_512, 2, 18, 512 }, + { { 40 }, F5_360_512, 2, 9, 512 }, + { { 40 }, F5_320_512, 2, 8, 512 }, + { { 40 }, F5_180_512, 1, 9, 512 }, + { { 40 }, F5_160_512, 1, 8, 512 } +}; + +#define SET_MEDIA_TYPE(geometry, media_index) \ + (geometry.MediaType = media_table[media_index].MediaType) + +#endif // INCLUDE_VFD_ORIGIN + +VOID +ImDiskFindFreeDeviceNumber(PDRIVER_OBJECT DriverObject, + PULONG DeviceNumber) +{ + KLOCK_QUEUE_HANDLE lock_handle; + PDEVICE_OBJECT device_object; + + ImDiskAcquireLock(&DeviceListLock, &lock_handle); + + *DeviceNumber = 0; + for (device_object = DriverObject->DeviceObject; + device_object != NULL; +#pragma warning(suppress: 28175) + device_object = device_object->NextDevice) + { + PDEVICE_EXTENSION device_extension = (PDEVICE_EXTENSION) + device_object->DeviceExtension; + + // Skip over control device + if (device_extension->device_number == -1) + continue; + + KdPrint2(("ImDisk: Found device %i.\n", + device_extension->device_number)); + + // Result will be one number above highest existing + if (device_extension->device_number >= *DeviceNumber) + *DeviceNumber = device_extension->device_number + 1; + } + + ImDiskReleaseLock(&lock_handle); +} + +#pragma code_seg("PAGE") + +// Parses BPB formatted geometry to a DISK_GEOMETRY structure. +VOID +ImDiskReadFormattedGeometry(IN OUT PIMDISK_CREATE_DATA CreateData, + IN PFAT_BPB BPB) +{ + USHORT tmp; + + PAGED_CODE(); + + KdPrint + (("ImDisk: Detected BPB values:\n" + "Bytes per sect: %u\n" + "Sectors per cl: %u\n" + "Reserved sect : %u\n" + "FAT count : %u\n" + "FAT root entr : %u\n" + "Total sectors : %u\n" + "Media descript: 0x%.2X\n" + "Sectors pr FAT: %u\n" + "Sect per track: %u\n" + "Number of head: %u\n", + (ULONG)BPB->BytesPerSector, + (ULONG)BPB->SectorsPerCluster, + (ULONG)BPB->ReservedSectors, + (ULONG)BPB->NumberOfFileAllocationTables, + (ULONG)BPB->NumberOfRootEntries, + (ULONG)BPB->NumberOfSectors, + (LONG)BPB->MediaDescriptor, + (ULONG)BPB->SectorsPerFileAllocationTable, + (ULONG)BPB->SectorsPerTrack, + (ULONG)BPB->NumberOfHeads)); + + // Some sanity checks. Could this really be valid BPB values? Bytes per + // sector is multiple of 2 etc? + if (BPB->BytesPerSector == 0) + return; + + if (BPB->SectorsPerTrack >= 64) + return; + + if (BPB->NumberOfHeads >= 256) + return; + + tmp = BPB->BytesPerSector; + while ((tmp & 0x0001) == 0) + tmp >>= 1; + if ((tmp ^ 0x0001) != 0) + return; + + if (CreateData->DiskGeometry.SectorsPerTrack == 0) + CreateData->DiskGeometry.SectorsPerTrack = BPB->SectorsPerTrack; + + if (CreateData->DiskGeometry.TracksPerCylinder == 0) + CreateData->DiskGeometry.TracksPerCylinder = BPB->NumberOfHeads; + + if (CreateData->DiskGeometry.BytesPerSector == 0) + CreateData->DiskGeometry.BytesPerSector = BPB->BytesPerSector; + +#ifdef INCLUDE_VFD_ORIGIN + + if (((IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) | + (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0)) & + (CreateData->DiskGeometry.MediaType == Unknown)) + switch (CreateData->DiskGeometry.Cylinders.QuadPart) + { + // 3.5" formats + case MEDIA_SIZE_240MB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_240M); + break; + + case MEDIA_SIZE_120MB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_120M); + break; + + case MEDIA_SIZE_2800KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_2880K); + break; + + case MEDIA_SIZE_1722KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1722K); + break; + + case MEDIA_SIZE_1680KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1680K); + break; + + case MEDIA_SIZE_1440KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1440K); + break; + + case MEDIA_SIZE_820KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_820K); + break; + + case MEDIA_SIZE_720KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_720K); + break; + + // 5.25" formats + case MEDIA_SIZE_1200KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1200K); + break; + + case MEDIA_SIZE_640KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_640K); + break; + + case MEDIA_SIZE_360KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_360K); + break; + + case MEDIA_SIZE_320KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_320K); + break; + + case MEDIA_SIZE_180KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_180K); + break; + + case MEDIA_SIZE_160KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_160K); + break; + } + +#endif // INCLUDE_VFD_ORIGIN + + KdPrint + (("ImDisk: Values after BPB geometry detection:\n" + "DeviceNumber = %#x\n" + "DiskGeometry\n" + " .Cylinders = 0x%.8x%.8x\n" + " .MediaType = %i\n" + " .T/C = %u\n" + " .S/T = %u\n" + " .B/S = %u\n" + "Offset = 0x%.8x%.8x\n" + "Flags = %#x\n" + "FileNameLength = %u\n" + "FileName = '%.*ws'\n" + "DriveLetter = %wc\n", + CreateData->DeviceNumber, + CreateData->DiskGeometry.Cylinders.HighPart, + CreateData->DiskGeometry.Cylinders.LowPart, + CreateData->DiskGeometry.MediaType, + CreateData->DiskGeometry.TracksPerCylinder, + CreateData->DiskGeometry.SectorsPerTrack, + CreateData->DiskGeometry.BytesPerSector, + CreateData->ImageOffset.HighPart, + CreateData->ImageOffset.LowPart, + CreateData->Flags, + CreateData->FileNameLength, + (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), + CreateData->FileName, + CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); +} + +NTSTATUS +ImDiskGetDiskSize(IN HANDLE FileHandle, + IN OUT PIO_STATUS_BLOCK IoStatus, + IN OUT PLARGE_INTEGER DiskSize) +{ + NTSTATUS status; + + PAGED_CODE(); + + { + FILE_STANDARD_INFORMATION file_standard; + + status = ZwQueryInformationFile(FileHandle, + IoStatus, + &file_standard, + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); + + if (NT_SUCCESS(status)) + { + *DiskSize = file_standard.EndOfFile; + return status; + } + + KdPrint(("ImDisk: FileStandardInformation not supported for " + "target device. %#x\n", status)); + } + + // Retry with IOCTL_DISK_GET_LENGTH_INFO instead + { + GET_LENGTH_INFORMATION part_info = { 0 }; + + status = + ZwDeviceIoControlFile(FileHandle, + NULL, + NULL, + NULL, + IoStatus, + IOCTL_DISK_GET_LENGTH_INFO, + NULL, + 0, + &part_info, + sizeof(part_info)); + + if (status == STATUS_PENDING) + { + ZwWaitForSingleObject(FileHandle, FALSE, NULL); + status = IoStatus->Status; + } + + if (NT_SUCCESS(status)) + { + *DiskSize = part_info.Length; + return status; + } + + KdPrint(("ImDisk: IOCTL_DISK_GET_LENGTH_INFO not supported " + "for target device. %#x\n", status)); + } + + // Retry with IOCTL_DISK_GET_PARTITION_INFO instead + { + PARTITION_INFORMATION part_info = { 0 }; + + status = + ZwDeviceIoControlFile(FileHandle, + NULL, + NULL, + NULL, + IoStatus, + IOCTL_DISK_GET_PARTITION_INFO, + NULL, + 0, + &part_info, + sizeof(part_info)); + + if (status == STATUS_PENDING) + { + ZwWaitForSingleObject(FileHandle, FALSE, NULL); + status = IoStatus->Status; + } + + if (NT_SUCCESS(status)) + { + *DiskSize = part_info.PartitionLength; + return status; + } + + KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO not supported " + "for target device. %#x\n", status)); + } + + return status; +} + +NTSTATUS +ImDiskCreateDevice(__in PDRIVER_OBJECT DriverObject, + __inout __deref PIMDISK_CREATE_DATA CreateData, + __in PETHREAD ClientThread, + __out PDEVICE_OBJECT *DeviceObject) +{ + UNICODE_STRING device_name; + NTSTATUS status; + PDEVICE_EXTENSION device_extension; + BOOLEAN proxy_supports_unmap = FALSE; + BOOLEAN proxy_supports_zero = FALSE; + DEVICE_TYPE device_type; + ULONG device_characteristics; + HANDLE file_handle = NULL; + PFILE_OBJECT file_object = NULL; + PDEVICE_OBJECT dev_object = NULL; + BOOLEAN parallel_io = FALSE; + PUCHAR image_buffer = NULL; + PROXY_CONNECTION proxy = { (PROXY_CONNECTION::PROXY_CONNECTION_TYPE)0 }; + ULONG alignment_requirement; + CCHAR extra_stack_locations = 0; + + PAGED_CODE(); + + ASSERT(CreateData != NULL); + + KdPrint + (("ImDisk: Got request to create a virtual disk. Request data:\n" + "DeviceNumber = %#x\n" + "DiskGeometry\n" + " .Cylinders = 0x%.8x%.8x\n" + " .MediaType = %i\n" + " .T/C = %u\n" + " .S/T = %u\n" + " .B/S = %u\n" + "Offset = 0x%.8x%.8x\n" + "Flags = %#x\n" + "FileNameLength = %u\n" + "FileName = '%.*ws'\n" + "DriveLetter = %wc\n", + CreateData->DeviceNumber, + CreateData->DiskGeometry.Cylinders.HighPart, + CreateData->DiskGeometry.Cylinders.LowPart, + CreateData->DiskGeometry.MediaType, + CreateData->DiskGeometry.TracksPerCylinder, + CreateData->DiskGeometry.SectorsPerTrack, + CreateData->DiskGeometry.BytesPerSector, + CreateData->ImageOffset.HighPart, + CreateData->ImageOffset.LowPart, + CreateData->Flags, + CreateData->FileNameLength, + (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), + CreateData->FileName, + CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); + + // Auto-select type if not specified. + if (IMDISK_TYPE(CreateData->Flags) == 0) + if (CreateData->FileNameLength == 0) + CreateData->Flags |= IMDISK_TYPE_VM; + else + CreateData->Flags |= IMDISK_TYPE_FILE; + + // Blank filenames only supported for memory disks where size is specified. + if ((CreateData->FileNameLength == 0) && + !(((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) & + (CreateData->DiskGeometry.Cylinders.QuadPart > 65536)) | + ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC) & + (CreateData->DiskGeometry.Cylinders.QuadPart > 65536)))) + { + KdPrint(("ImDisk: Blank filenames only supported for memory disks where " + "size is specified..\n")); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + STATUS_INVALID_PARAMETER, + 102, + STATUS_INVALID_PARAMETER, + 0, + 0, + NULL, + L"Blank filenames only supported for non-zero length " + L"vm type disks.")); + + return STATUS_INVALID_PARAMETER; + } + +#ifndef _WIN64 + // Cannot create >= 2 GB VM disk in 32 bit version. + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) & + ((CreateData->DiskGeometry.Cylinders.QuadPart & 0xFFFFFFFF80000000) != + 0)) + { + KdPrint(("ImDisk: Cannot create >= 2GB vm disks on 32-bit system.\n")); + + return STATUS_INVALID_PARAMETER; + } +#endif + + // Auto-find first free device number + if (CreateData->DeviceNumber == IMDISK_AUTO_DEVICE_NUMBER) + { + ImDiskFindFreeDeviceNumber(DriverObject, &CreateData->DeviceNumber); + + KdPrint(("ImDisk: Free device number %i.\n", CreateData->DeviceNumber)); + } + + /* for (CreateData->DeviceNumber = 0; */ + /* CreateData->DeviceNumber < MaxDevices; */ + /* CreateData->DeviceNumber++) */ + /* if ((~DeviceList) & (1ULL << CreateData->DeviceNumber)) */ + /* break; */ + + if (CreateData->DeviceNumber >= MaxDevices) + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + STATUS_INVALID_PARAMETER, + 102, + STATUS_INVALID_PARAMETER, + 0, + 0, + NULL, + L"Device number too high.")); + + return STATUS_INVALID_PARAMETER; + } + + if (IMDISK_BYTE_SWAP(CreateData->Flags) && + (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_PARALLEL_IO)) + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + STATUS_INVALID_PARAMETER, + 102, + STATUS_INVALID_PARAMETER, + 0, + 0, + NULL, + L"Byte swapping not supported together with parallel I/O.")); + + return STATUS_INVALID_PARAMETER; + } + + WPoolMem file_name_buffer; + + UNICODE_STRING file_name; + file_name.Length = CreateData->FileNameLength; + file_name.MaximumLength = CreateData->FileNameLength; + file_name.Buffer = NULL; + + // If a file is to be opened or created, allocate name buffer and open that + // file... + if ((CreateData->FileNameLength > 0) | + ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC))) + { + IO_STATUS_BLOCK io_status; + OBJECT_ATTRIBUTES object_attributes; + ACCESS_MASK desired_access = 0; + ULONG share_access = 0; + ULONG create_options = 0; + + if (file_name.MaximumLength > 0) + { + file_name.Buffer = file_name_buffer.Alloc(file_name.MaximumLength); + + if (file_name.Buffer == NULL) + { + KdPrint(("ImDisk: Error allocating buffer for filename.\n")); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + STATUS_INSUFFICIENT_RESOURCES, + 102, + STATUS_INSUFFICIENT_RESOURCES, + 0, + 0, + NULL, + L"Memory allocation error.")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(file_name.Buffer, CreateData->FileName, + CreateData->FileNameLength); + } + + // If no device-type specified, check if filename ends with .iso, .nrg or + // .bin. In that case, set device-type automatically to FILE_DEVICE_CDROM + if ((IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) & + (CreateData->FileNameLength >= (4 * sizeof(*CreateData->FileName)))) + { + LPWSTR name = CreateData->FileName + + (CreateData->FileNameLength / sizeof(*CreateData->FileName)) - 4; + if ((_wcsnicmp(name, L".iso", 4) == 0) | + (_wcsnicmp(name, L".nrg", 4) == 0) | + (_wcsnicmp(name, L".bin", 4) == 0)) + CreateData->Flags |= IMDISK_DEVICE_TYPE_CD | IMDISK_OPTION_RO; + } + + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) + CreateData->Flags |= IMDISK_OPTION_RO; + + KdPrint(("ImDisk: Done with device type auto-selection by file ext.\n")); + + if (ClientThread != NULL) + { + SECURITY_QUALITY_OF_SERVICE security_quality_of_service; + SECURITY_CLIENT_CONTEXT security_client_context; + + RtlZeroMemory(&security_quality_of_service, + sizeof(SECURITY_QUALITY_OF_SERVICE)); + + security_quality_of_service.Length = + sizeof(SECURITY_QUALITY_OF_SERVICE); + security_quality_of_service.ImpersonationLevel = + SecurityImpersonation; + security_quality_of_service.ContextTrackingMode = + SECURITY_STATIC_TRACKING; + security_quality_of_service.EffectiveOnly = FALSE; + + SeCreateClientSecurity(ClientThread, + &security_quality_of_service, + FALSE, + &security_client_context); + + SeImpersonateClient(&security_client_context, NULL); + + SeDeleteClientSecurity(&security_client_context); + } + else + KdPrint(("ImDisk: No impersonation information.\n")); + + WPoolMem real_file_name_buffer; + UNICODE_STRING real_file_name = { 0 }; + + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC)) + { + real_file_name.MaximumLength = file_name.Length + + sizeof(AWEALLOC_DEVICE_NAME); + + real_file_name.Buffer = real_file_name_buffer.Alloc(real_file_name.MaximumLength); + + if (real_file_name.Buffer == NULL) + { + KdPrint(("ImDisk: Out of memory while allocating %#x bytes\n", + real_file_name.MaximumLength)); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + real_file_name.Length = 0; + + status = + RtlAppendUnicodeToString(&real_file_name, + AWEALLOC_DEVICE_NAME); + + if (NT_SUCCESS(status)) + status = + RtlAppendUnicodeStringToString(&real_file_name, + &file_name); + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Internal error: " + "RtlAppendUnicodeStringToString failed with " + "pre-allocated buffers.\n")); + + return STATUS_DRIVER_INTERNAL_ERROR; + } + + InitializeObjectAttributes(&object_attributes, + &real_file_name, + OBJ_CASE_INSENSITIVE | + OBJ_FORCE_ACCESS_CHECK, + NULL, + NULL); + } + else if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) & + ((IMDISK_PROXY_TYPE(CreateData->Flags) == + IMDISK_PROXY_TYPE_TCP) | + (IMDISK_PROXY_TYPE(CreateData->Flags) == + IMDISK_PROXY_TYPE_COMM))) + { + RtlInitUnicodeString(&real_file_name, IMDPROXY_SVC_PIPE_NATIVE_NAME); + + InitializeObjectAttributes(&object_attributes, + &real_file_name, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + } + else + { + real_file_name = file_name; + + InitializeObjectAttributes(&object_attributes, + &real_file_name, + OBJ_CASE_INSENSITIVE | + OBJ_FORCE_ACCESS_CHECK, + NULL, + NULL); + } + + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) && + (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM)) + { + proxy.connection_type = PROXY_CONNECTION::PROXY_CONNECTION_SHM; + + status = + ZwOpenSection(&file_handle, + GENERIC_READ | GENERIC_WRITE, + &object_attributes); + } + else + { + desired_access = GENERIC_READ; + + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) || + ((IMDISK_TYPE(CreateData->Flags) != IMDISK_TYPE_VM) && + !IMDISK_READONLY(CreateData->Flags))) + { + desired_access |= GENERIC_WRITE; + } + + share_access = FILE_SHARE_READ | FILE_SHARE_DELETE; + + if (IMDISK_READONLY(CreateData->Flags) || + (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) || + IMDISK_SHARED_IMAGE(CreateData->Flags)) + { + share_access |= FILE_SHARE_WRITE; + } + + create_options = FILE_NON_DIRECTORY_FILE | + FILE_NO_INTERMEDIATE_BUFFERING | + FILE_SYNCHRONOUS_IO_NONALERT; + + if (IMDISK_SPARSE_FILE(CreateData->Flags)) + { + create_options |= FILE_OPEN_FOR_BACKUP_INTENT; + } + + if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) + { + create_options |= FILE_SEQUENTIAL_ONLY; + } + else + { + create_options |= FILE_RANDOM_ACCESS; + } + + KdPrint(("ImDisk: Passing DesiredAccess=%#x ShareAccess=%#x CreateOptions=%#x\n", + desired_access, share_access, create_options)); + + status = ZwCreateFile( + &file_handle, + desired_access, + &object_attributes, + &io_status, + NULL, + FILE_ATTRIBUTE_NORMAL, + share_access, + FILE_OPEN, + create_options, + NULL, + 0); + } + + // For 32 bit driver running on Windows 2000 and earlier, the above + // call will fail because OBJ_FORCE_ACCESS_CHECK is not supported. If so, + // STATUS_INVALID_PARAMETER is returned and we go on without any access + // checks in that case. +#ifndef _WIN64 + if (status == STATUS_INVALID_PARAMETER) + { + InitializeObjectAttributes(&object_attributes, + &real_file_name, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) && + (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM)) + { + proxy.connection_type = PROXY_CONNECTION::PROXY_CONNECTION_SHM; + + status = + ZwOpenSection(&file_handle, + GENERIC_READ | GENERIC_WRITE, + &object_attributes); + } + else + { + status = ZwCreateFile( + &file_handle, + desired_access, + &object_attributes, + &io_status, + NULL, + FILE_ATTRIBUTE_NORMAL, + share_access, + FILE_OPEN, + create_options, + NULL, + 0); + } + } +#endif + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Error opening file '%wZ'. " + "Status: %#x " + "SpecSize: %i " + "WritableFile: %i " + "DevTypeFile: %i " + "Flags: %#x\n" + "FileNameLength: %#x\n", + &real_file_name, + status, + CreateData->DiskGeometry.Cylinders.QuadPart != 0, + !IMDISK_READONLY(CreateData->Flags), + IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE, + CreateData->Flags, + (int)real_file_name.Length)); + } + + // If not found we will create the file if a new non-zero size is + // specified, read-only virtual disk is not specified and we are + // creating a type 'file' virtual disk. + if (((status == STATUS_OBJECT_NAME_NOT_FOUND) | + (status == STATUS_NO_SUCH_FILE)) & + (CreateData->DiskGeometry.Cylinders.QuadPart != 0) & + (!IMDISK_READONLY(CreateData->Flags)) & + (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE)) + { + KdPrint(("ImDisk: Creating new image file DesiredAccess=%#x ShareAccess=%#x CreateOptions=%#x\n", + desired_access, share_access, create_options)); + + status = + ZwCreateFile(&file_handle, + GENERIC_READ | + GENERIC_WRITE, + &object_attributes, + &io_status, + NULL, + FILE_ATTRIBUTE_NORMAL, + share_access, + FILE_OPEN_IF, + create_options, NULL, 0); + + if (!NT_SUCCESS(status)) + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Cannot create image file.")); + + KdPrint(("ImDisk: Cannot create '%.*ws'. (%#x)\n", + (int)(CreateData->FileNameLength / + sizeof(*CreateData->FileName)), + CreateData->FileName, + status)); + + return status; + } + } + else if (!NT_SUCCESS(status)) + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Cannot open image file.")); + + KdPrint(("ImDisk: Cannot open file '%wZ'. Status: %#x\n", + &real_file_name, + status)); + + return status; + } + + KdPrint(("ImDisk: File '%wZ' opened successfully.\n", + &real_file_name)); + + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & + (!IMDISK_READONLY(CreateData->Flags))) + { + // If creating a sparse image file + if (IMDISK_SPARSE_FILE(CreateData->Flags)) + { + status = ZwFsControlFile(file_handle, + NULL, + NULL, + NULL, + &io_status, + FSCTL_SET_SPARSE, + NULL, + 0, + NULL, + 0); + + if (NT_SUCCESS(status)) + KdPrint(("ImDisk: Sparse attribute set on image file.\n")); + else + { + DbgPrint("ImDisk: Cannot set sparse attribute " + "on image file: %X\n", status); + + CreateData->Flags &= ~IMDISK_OPTION_SPARSE_FILE; + } + } + + // Adjust the file length to the requested virtual disk size. + if ((CreateData->DiskGeometry.Cylinders.QuadPart != 0) & + (CreateData->ImageOffset.QuadPart == 0)) + { + status = ZwSetInformationFile( + file_handle, + &io_status, + &CreateData->DiskGeometry.Cylinders, + sizeof + (FILE_END_OF_FILE_INFORMATION), + FileEndOfFileInformation); + + if (NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Image file size adjusted.\n")); + } + else + { + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error setting file size.")); + + KdPrint(("ImDisk: Error setting eof (%#x).\n", status)); + return status; + } + } + } + + if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) + { + if (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM) + { + status = ZwMapViewOfSection( + file_handle, + NtCurrentProcess(), + (PVOID*)&proxy.shared_memory, + 0, + 0, + NULL, + &proxy.shared_memory_size, + ViewUnmap, + 0, + PAGE_READWRITE); + } + else + { + status = ObReferenceObjectByHandle( + file_handle, + FILE_READ_ATTRIBUTES | + FILE_READ_DATA | + FILE_WRITE_DATA, + *IoFileObjectType, + KernelMode, + (PVOID*)&proxy.device, + NULL); + } + + if (!NT_SUCCESS(status)) + { + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error referencing proxy device.")); + + KdPrint(("ImDisk: Error referencing proxy device (%#x).\n", + status)); + + return status; + } + + KdPrint(("ImDisk: Got reference to proxy object %p.\n", + proxy.connection_type == PROXY_CONNECTION:: + PROXY_CONNECTION_TYPE::PROXY_CONNECTION_DEVICE ? + (PVOID)proxy.device : + (PVOID)proxy.shared_memory)); + + if (IMDISK_PROXY_TYPE(CreateData->Flags) != IMDISK_PROXY_TYPE_DIRECT) + { + status = ImDiskConnectProxy(&proxy, + &io_status, + NULL, + CreateData->Flags, + CreateData->FileName, + CreateData->FileNameLength); + } + + if (!NT_SUCCESS(status)) + { + ImDiskCloseProxy(&proxy); + + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error connecting proxy.")); + + KdPrint(("ImDisk: Error connecting proxy (%#x).\n", status)); + + return status; + } + } + + // Get the file size of the disk file. + if (IMDISK_TYPE(CreateData->Flags) != IMDISK_TYPE_PROXY) + { + LARGE_INTEGER disk_size; + + status = + ImDiskGetDiskSize(file_handle, + &io_status, + &disk_size); + + if (!NT_SUCCESS(status)) + { + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error getting image size.")); + + KdPrint + (("ImDisk: Error getting image size (%#x).\n", + status)); + + return status; + } + + // Allocate virtual memory for 'vm' type. + if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) + { + SIZE_T max_size; + + // If no size given for VM disk, use size of pre-load image file. + // This code is somewhat easier for 64 bit architectures. + +#ifdef _WIN64 + if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) + CreateData->DiskGeometry.Cylinders.QuadPart = + disk_size.QuadPart - + CreateData->ImageOffset.QuadPart; + + max_size = CreateData->DiskGeometry.Cylinders.QuadPart; +#else + if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) + // Check that file size < 2 GB. + if ((disk_size.QuadPart - + CreateData->ImageOffset.QuadPart) & 0xFFFFFFFF80000000) + { + ZwClose(file_handle); + + KdPrint(("ImDisk: VM disk >= 2GB not supported.\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + else + CreateData->DiskGeometry.Cylinders.QuadPart = + disk_size.QuadPart - + CreateData->ImageOffset.QuadPart; + + max_size = CreateData->DiskGeometry.Cylinders.LowPart; +#endif + + status = + ZwAllocateVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + 0, + &max_size, + MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(status)) + { + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Not enough memory for VM disk.")); + + KdPrint(("ImDisk: Error allocating vm for image. (%#x)\n", + status)); + + return STATUS_NO_MEMORY; + } + + alignment_requirement = FILE_BYTE_ALIGNMENT; + + // Loading of image file has been moved to be done just before + // the service loop. + } + else + { + FILE_ALIGNMENT_INFORMATION file_alignment; + + status = ZwQueryInformationFile(file_handle, + &io_status, + &file_alignment, + sizeof + (FILE_ALIGNMENT_INFORMATION), + FileAlignmentInformation); + + if (!NT_SUCCESS(status)) + { + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error getting alignment information.")); + + KdPrint(("ImDisk: Error querying file alignment (%#x).\n", + status)); + + return status; + } + + if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) + CreateData->DiskGeometry.Cylinders.QuadPart = + disk_size.QuadPart - + CreateData->ImageOffset.QuadPart; + + alignment_requirement = file_alignment.AlignmentRequirement; + } + + if ((CreateData->DiskGeometry.TracksPerCylinder == 0) | + (CreateData->DiskGeometry.SectorsPerTrack == 0) | + (CreateData->DiskGeometry.BytesPerSector == 0)) + { + SIZE_T free_size = 0; + WPoolMem fat_vbr(sizeof(FAT_VBR)); + + if (!fat_vbr) + { + if (file_handle != NULL) + ZwClose(file_handle); + + if (image_buffer != NULL) + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + &free_size, MEM_RELEASE); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Insufficient memory.")); + + KdPrint(("ImDisk: Error allocating memory.\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + status = + ZwReadFile(file_handle, + NULL, + NULL, + NULL, + &io_status, + fat_vbr, + sizeof(FAT_VBR), + &CreateData->ImageOffset, + NULL); + + if (NT_SUCCESS(status)) + { + ImDiskReadFormattedGeometry(CreateData, &fat_vbr->BPB); + } + else + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error reading first sector.")); + + KdPrint(("ImDisk: Error reading first sector (%#x).\n", + status)); + } + } + } + else + // If proxy is used, get the image file size from the proxy instead. + { + IMDPROXY_INFO_RESP proxy_info; + + status = ImDiskQueryInformationProxy(&proxy, + &io_status, + NULL, + &proxy_info, + sizeof(IMDPROXY_INFO_RESP)); + + if (!NT_SUCCESS(status)) + { + ImDiskCloseProxy(&proxy); + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error querying proxy.")); + + KdPrint(("ImDisk: Error querying proxy (%#x).\n", status)); + + return status; + } + + if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) + CreateData->DiskGeometry.Cylinders.QuadPart = proxy_info.file_size; + + if ((CreateData->DiskGeometry.TracksPerCylinder == 0) | + (CreateData->DiskGeometry.SectorsPerTrack == 0) | + (CreateData->DiskGeometry.BytesPerSector == 0)) + { + WPoolMem fat_vbr(sizeof(FAT_VBR)); + + if (!fat_vbr) + { + ImDiskCloseProxy(&proxy); + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Insufficient memory.")); + + KdPrint(("ImDisk: Error allocating memory.\n")); + + return STATUS_INSUFFICIENT_RESOURCES; + } + + status = ImDiskReadProxy(&proxy, + &io_status, + NULL, + fat_vbr, + sizeof(FAT_VBR), + &CreateData->ImageOffset); + + if (!NT_SUCCESS(status)) + { + ImDiskCloseProxy(&proxy); + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error reading first sector.")); + + KdPrint(("ImDisk: Error reading first sector (%#x).\n", + status)); + + return status; + } + + ImDiskReadFormattedGeometry(CreateData, &fat_vbr->BPB); + } + + if ((proxy_info.req_alignment - 1 > FILE_512_BYTE_ALIGNMENT) | + (CreateData->DiskGeometry.Cylinders.QuadPart == 0)) + { + ImDiskCloseProxy(&proxy); + ZwClose(file_handle); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Unsupported sizes.")); + +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk: Unsupported sizes. " + "Got 0x%.8x%.8x size and 0x%.8x%.8x alignment.\n", + proxy_info.file_size, + proxy_info.req_alignment)); + + return STATUS_INVALID_PARAMETER; + } + + alignment_requirement = (ULONG)proxy_info.req_alignment - 1; + + if (proxy_info.flags & IMDPROXY_FLAG_RO) + CreateData->Flags |= IMDISK_OPTION_RO; + + if (proxy_info.flags & IMDPROXY_FLAG_SUPPORTS_UNMAP) + proxy_supports_unmap = TRUE; + + if (proxy_info.flags & IMDPROXY_FLAG_SUPPORTS_ZERO) + proxy_supports_zero = TRUE; + + KdPrint(("ImDisk: Got from proxy: Siz=0x%.8x%.8x Flg=%#x Alg=%#x.\n", + CreateData->DiskGeometry.Cylinders.HighPart, + CreateData->DiskGeometry.Cylinders.LowPart, + (ULONG)proxy_info.flags, + (ULONG)proxy_info.req_alignment)); + } + + if (CreateData->DiskGeometry.Cylinders.QuadPart <= 65536) + { + SIZE_T free_size = 0; + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Number of cylinders equals zero.")); + + KdPrint(("ImDisk: Fatal error: Number of cylinders equals zero.\n")); + + ImDiskCloseProxy(&proxy); + + if (file_handle != NULL) + ZwClose(file_handle); + + if (image_buffer != NULL) + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + &free_size, MEM_RELEASE); + + return STATUS_INVALID_PARAMETER; + } + } + // Blank vm-disk, just allocate... + else + { + SIZE_T max_size; +#ifdef _WIN64 + max_size = CreateData->DiskGeometry.Cylinders.QuadPart; +#else + max_size = CreateData->DiskGeometry.Cylinders.LowPart; +#endif + + image_buffer = NULL; + status = + ZwAllocateVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + 0, + &max_size, + MEM_COMMIT, + PAGE_READWRITE); + if (!NT_SUCCESS(status)) + { + KdPrint + (("ImDisk: Error allocating virtual memory for vm disk (%#x).\n", + status)); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Not enough free memory for VM disk.")); + + return STATUS_NO_MEMORY; + } + + alignment_requirement = FILE_BYTE_ALIGNMENT; + } + + KdPrint(("ImDisk: Done with file/memory checks.\n")); + +#ifdef INCLUDE_VFD_ORIGIN + + // If no device-type specified and size matches common floppy sizes, + // auto-select FILE_DEVICE_DISK with FILE_FLOPPY_DISKETTE and + // FILE_REMOVABLE_MEDIA. + // If still no device-type specified, specify FILE_DEVICE_DISK with no + // particular characteristics. This will emulate a hard disk partition. + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) + switch (CreateData->DiskGeometry.Cylinders.QuadPart) + { + case MEDIA_SIZE_240MB: + case MEDIA_SIZE_120MB: + case MEDIA_SIZE_2800KB: + case MEDIA_SIZE_1722KB: + case MEDIA_SIZE_1680KB: + case MEDIA_SIZE_1440KB: + case MEDIA_SIZE_820KB: + case MEDIA_SIZE_720KB: + case MEDIA_SIZE_1200KB: + case MEDIA_SIZE_640KB: + case MEDIA_SIZE_360KB: + case MEDIA_SIZE_320KB: + case MEDIA_SIZE_180KB: + case MEDIA_SIZE_160KB: + CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; + break; + + default: + CreateData->Flags |= IMDISK_DEVICE_TYPE_HD; + } + + KdPrint(("ImDisk: Done with device type selection for floppy sizes.\n")); + +#else // INCLUDE_VFD_ORIGIN + + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) + CreateData->Flags |= IMDISK_DEVICE_TYPE_HD; + +#endif // INCLUDE_VFD_ORIGIN + + // If some parts of the DISK_GEOMETRY structure are zero, auto-fill with + // typical values for this type of disk. + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) + { + LONGLONG calccyl = CreateData->DiskGeometry.Cylinders.QuadPart; + + if (CreateData->DiskGeometry.BytesPerSector == 0) + CreateData->DiskGeometry.BytesPerSector = SECTOR_SIZE_CD_ROM; + + calccyl /= CreateData->DiskGeometry.BytesPerSector; + + if (CreateData->DiskGeometry.SectorsPerTrack == 0) + { + if (calccyl / SECTORS_PER_TRACK_CD_ROM * SECTORS_PER_TRACK_CD_ROM == + calccyl) + CreateData->DiskGeometry.SectorsPerTrack = + SECTORS_PER_TRACK_CD_ROM; + else + CreateData->DiskGeometry.SectorsPerTrack = 1; + } + + calccyl /= CreateData->DiskGeometry.SectorsPerTrack; + + if (CreateData->DiskGeometry.TracksPerCylinder == 0) + { + if (calccyl / + TRACKS_PER_CYLINDER_CD_ROM * TRACKS_PER_CYLINDER_CD_ROM == + calccyl) + CreateData->DiskGeometry.TracksPerCylinder = + TRACKS_PER_CYLINDER_CD_ROM; + else + CreateData->DiskGeometry.TracksPerCylinder = 1; + } + + if (CreateData->DiskGeometry.MediaType == Unknown) + CreateData->DiskGeometry.MediaType = RemovableMedia; + } + // Common floppy sizes geometries. + else + { + LONGLONG calccyl = CreateData->DiskGeometry.Cylinders.QuadPart; + +#ifdef INCLUDE_VFD_ORIGIN + + if ((IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) & + (CreateData->DiskGeometry.BytesPerSector == 0) & + (CreateData->DiskGeometry.SectorsPerTrack == 0) & + (CreateData->DiskGeometry.TracksPerCylinder == 0) & + (CreateData->DiskGeometry.MediaType == Unknown)) + switch (calccyl) + { + // 3.5" formats + case MEDIA_SIZE_240MB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_240M]; + break; + + case MEDIA_SIZE_120MB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_120M]; + break; + + case MEDIA_SIZE_2800KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_2880K]; + break; + + case MEDIA_SIZE_1722KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_1722K]; + break; + + case MEDIA_SIZE_1680KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_1680K]; + break; + + case MEDIA_SIZE_1440KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_1440K]; + break; + + case MEDIA_SIZE_820KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_820K]; + break; + + case MEDIA_SIZE_720KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_720K]; + break; + + // 5.25" formats + case MEDIA_SIZE_1200KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_1200K]; + break; + + case MEDIA_SIZE_640KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_640K]; + break; + + case MEDIA_SIZE_360KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_360K]; + break; + + case MEDIA_SIZE_320KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_320K]; + break; + + case MEDIA_SIZE_180KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_180K]; + break; + + case MEDIA_SIZE_160KB: + CreateData->DiskGeometry = media_table[MEDIA_TYPE_160K]; + break; + } + + // In this case the Cylinders member actually specifies the total size of + // the virtual disk so restore that in case overwritten by the pre- + // defined floppy geometries above. + CreateData->DiskGeometry.Cylinders.QuadPart = calccyl; + +#endif // INCLUDE_VFD_ORIGIN + + if (CreateData->DiskGeometry.BytesPerSector == 0) + CreateData->DiskGeometry.BytesPerSector = SECTOR_SIZE_HDD; + + calccyl /= CreateData->DiskGeometry.BytesPerSector; + + if (CreateData->DiskGeometry.SectorsPerTrack == 0) + CreateData->DiskGeometry.SectorsPerTrack = HEAD_SIZE_HDD; + + calccyl /= CreateData->DiskGeometry.SectorsPerTrack; + + // Former auto-selection of HDD head size + /* + if (CreateData->DiskGeometry.SectorsPerTrack == 0) + { + CreateData->DiskGeometry.SectorsPerTrack = 1; + + if ((calccyl / 7 * 7 == calccyl) & + (CreateData->DiskGeometry.SectorsPerTrack * 7 < 64)) + { + CreateData->DiskGeometry.SectorsPerTrack *= 7; + calccyl /= 7; + } + + if ((calccyl / 3 * 3 == calccyl) & + (CreateData->DiskGeometry.SectorsPerTrack * 3 < 64)) + { + CreateData->DiskGeometry.SectorsPerTrack *= 3; + calccyl /= 3; + } + + if ((calccyl / 3 * 3 == calccyl) & + (CreateData->DiskGeometry.SectorsPerTrack * 3 < 64)) + { + CreateData->DiskGeometry.SectorsPerTrack *= 3; + calccyl /= 3; + } + + while (((calccyl & 1) == 0) & + (CreateData->DiskGeometry.SectorsPerTrack <= 16)) + { + CreateData->DiskGeometry.SectorsPerTrack <<= 1; + calccyl >>= 1; + } + } + else + calccyl /= CreateData->DiskGeometry.SectorsPerTrack; + */ + + if (CreateData->DiskGeometry.TracksPerCylinder == 0) + { + CreateData->DiskGeometry.TracksPerCylinder = 1; + + if (calccyl >= 130560) + { + CreateData->DiskGeometry.TracksPerCylinder = 255; + calccyl /= 255; + } + else + while ((calccyl > 128) & + (CreateData->DiskGeometry.TracksPerCylinder < 128)) + { + CreateData->DiskGeometry.TracksPerCylinder <<= 1; + calccyl >>= 1; + } + + /* + if (calccyl % 17 == 0) + { + CreateData->DiskGeometry.TracksPerCylinder *= 17; + calccyl /= 17; + } + + if (calccyl % 5 == 0) + { + CreateData->DiskGeometry.TracksPerCylinder *= 5; + calccyl /= 5; + } + + if (calccyl % 3 == 0) + { + CreateData->DiskGeometry.TracksPerCylinder *= 3; + calccyl /= 3; + } + + while (((calccyl & 1) == 0) & + (CreateData->DiskGeometry.TracksPerCylinder <= 64)) + { + CreateData->DiskGeometry.TracksPerCylinder <<= 1; + calccyl >>= 1; + } + */ + } + + if (CreateData->DiskGeometry.MediaType == Unknown) + CreateData->DiskGeometry.MediaType = FixedMedia; + } + + KdPrint(("ImDisk: Done with disk geometry setup.\n")); + + // Ensure upper-case drive letter. + CreateData->DriveLetter &= ~0x20; + + // Now build real DeviceType and DeviceCharacteristics parameters. + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) + { + device_type = FILE_DEVICE_CD_ROM; + device_characteristics = FILE_READ_ONLY_DEVICE | FILE_REMOVABLE_MEDIA; + } + else if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_RAW) + { + device_type = FILE_DEVICE_UNKNOWN; + device_characteristics = 0; + } + else + { + device_type = FILE_DEVICE_DISK; + + if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) + device_characteristics = FILE_FLOPPY_DISKETTE | FILE_REMOVABLE_MEDIA; + else + device_characteristics = 0; + } + + if (IMDISK_REMOVABLE(CreateData->Flags)) + device_characteristics |= FILE_REMOVABLE_MEDIA; + + if (IMDISK_READONLY(CreateData->Flags)) + device_characteristics |= FILE_READ_ONLY_DEVICE; + + KdPrint + (("ImDisk: After checks and translations we got this create data:\n" + "DeviceNumber = %#x\n" + "DiskGeometry\n" + " .Cylinders = 0x%.8x%.8x\n" + " .MediaType = %i\n" + " .T/C = %u\n" + " .S/T = %u\n" + " .B/S = %u\n" + "Offset = 0x%.8x%.8x\n" + "Flags = %#x\n" + "FileNameLength = %u\n" + "FileName = '%.*ws'\n" + "DriveLetter = %wc\n", + CreateData->DeviceNumber, + CreateData->DiskGeometry.Cylinders.HighPart, + CreateData->DiskGeometry.Cylinders.LowPart, + CreateData->DiskGeometry.MediaType, + CreateData->DiskGeometry.TracksPerCylinder, + CreateData->DiskGeometry.SectorsPerTrack, + CreateData->DiskGeometry.BytesPerSector, + CreateData->ImageOffset.HighPart, + CreateData->ImageOffset.LowPart, + CreateData->Flags, + CreateData->FileNameLength, + (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), + CreateData->FileName, + CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); + + status = STATUS_SUCCESS; + + // Get FILE_OBJECT if we will need that later + if ((file_handle != NULL) && + (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && + ((IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC) || + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_PARALLEL_IO))) + { + ACCESS_MASK file_access_mask = + SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA; + + if (device_characteristics & FILE_READ_ONLY_DEVICE) + file_access_mask |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES; + + status = ObReferenceObjectByHandle(file_handle, + file_access_mask, + *IoFileObjectType, + KernelMode, (PVOID*)&file_object, NULL); + + if (!NT_SUCCESS(status)) + { + file_object = NULL; + + DbgPrint("ImDisk: Error referencing image file handle: %#x\n", + status); + } + else + { + parallel_io = TRUE; + + dev_object = IoGetRelatedDeviceObject(file_object); + + if ((dev_object->Flags & DO_DIRECT_IO) == DO_DIRECT_IO) + { + extra_stack_locations = dev_object->StackSize; + } + } + } + + WPoolMem device_name_buffer; + + if (NT_SUCCESS(status)) + { + // Buffer for device name + device_name_buffer.Alloc(MAXIMUM_FILENAME_LENGTH * + sizeof(*device_name_buffer)); + + if (!device_name_buffer) + { + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + STATUS_INSUFFICIENT_RESOURCES, + 102, + STATUS_INSUFFICIENT_RESOURCES, + 0, + 0, + NULL, + L"Memory allocation error.")); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (!NT_SUCCESS(status)) + { + SIZE_T free_size = 0; + + ImDiskCloseProxy(&proxy); + + if (file_handle != NULL) + ZwClose(file_handle); + + if (file_object != NULL) + ObDereferenceObject(file_object); + + if (image_buffer != NULL) + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + &free_size, MEM_RELEASE); + + return status; + } + + _snwprintf(device_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, + IMDISK_DEVICE_BASE_NAME L"%u", CreateData->DeviceNumber); + device_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; + + RtlInitUnicodeString(&device_name, device_name_buffer); + + KdPrint + (("ImDisk: Creating device '%ws'. Device type %#x, characteristics %#x.\n", + (PWSTR)device_name_buffer, device_type, device_characteristics)); + + status = IoCreateDevice(DriverObject, + sizeof(DEVICE_EXTENSION), + &device_name, + device_type, + device_characteristics, + FALSE, + DeviceObject); + + if (NT_SUCCESS(status)) + { + WPoolMem symlink_name_buffer( + MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR)); + + if (!symlink_name_buffer) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + UNICODE_STRING symlink_name; + + _snwprintf(symlink_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, + IMDISK_SYMLNK_NATIVE_BASE_NAME L"%u", CreateData->DeviceNumber); + symlink_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; + + RtlInitUnicodeString(&symlink_name, symlink_name_buffer); + + KdPrint(("ImDisk: Creating symlink '%ws'.\n", (PWSTR)symlink_name_buffer)); + + status = IoCreateUnprotectedSymbolicLink(&symlink_name, &device_name); + } + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Cannot create symlink. (%#x)\n", status)); + IoDeleteDevice(*DeviceObject); + } + } + + if (!NT_SUCCESS(status)) + { + SIZE_T free_size = 0; + + ImDiskCloseProxy(&proxy); + + if (file_handle != NULL) + ZwClose(file_handle); + + if (file_object != NULL) + ObDereferenceObject(file_object); + + if (image_buffer != NULL) + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&image_buffer, + &free_size, MEM_RELEASE); + + ImDiskLogError((DriverObject, + 0, + 0, + NULL, + 0, + 1000, + status, + 102, + status, + 0, + 0, + NULL, + L"Error creating device object.")); + + KdPrint(("ImDisk: Cannot create device. (%#x)\n", status)); + + return status; + } + + KdPrint + (("ImDisk: Setting the AlignmentRequirement field to %#x.\n", + alignment_requirement)); + + (*DeviceObject)->Flags |= DO_DIRECT_IO; + + (*DeviceObject)->AlignmentRequirement = alignment_requirement; + +#pragma warning(suppress: 4244) + (*DeviceObject)->StackSize += extra_stack_locations; + + device_extension = (PDEVICE_EXTENSION)(*DeviceObject)->DeviceExtension; + + RtlZeroMemory(device_extension, sizeof(*device_extension)); + + // Auto-set our own read-only flag if the characteristics of the device + // object is set to read-only. + if ((*DeviceObject)->Characteristics & FILE_READ_ONLY_DEVICE) + device_extension->read_only = TRUE; + + InitializeListHead(&device_extension->list_head); + + KeInitializeSpinLock(&device_extension->list_lock); + + KeInitializeSpinLock(&device_extension->last_io_lock); + + KeInitializeEvent(&device_extension->request_event, + NotificationEvent, FALSE); + + KeInitializeEvent(&device_extension->terminate_thread, + NotificationEvent, FALSE); + + device_extension->device_number = CreateData->DeviceNumber; + + device_extension->file_name = file_name; + + file_name_buffer.Abandon(); + + device_extension->disk_geometry = CreateData->DiskGeometry; + + device_extension->image_offset = CreateData->ImageOffset; + + if (IMDISK_SPARSE_FILE(CreateData->Flags)) + device_extension->use_set_zero_data = TRUE; + + // VM disk. + if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) + device_extension->vm_disk = TRUE; + else + device_extension->vm_disk = FALSE; + + // AWEAlloc disk. + if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & + (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC)) + device_extension->awealloc_disk = TRUE; + else + device_extension->awealloc_disk = FALSE; + + // Byte-swap + if (IMDISK_BYTE_SWAP(CreateData->Flags)) + device_extension->byte_swap = TRUE; + else + device_extension->byte_swap = FALSE; + + // Image opened for shared writing + if (IMDISK_SHARED_IMAGE(CreateData->Flags)) + device_extension->shared_image = TRUE; + else + device_extension->shared_image = FALSE; + + device_extension->image_buffer = image_buffer; + device_extension->file_handle = file_handle; + + // Use proxy service. + if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) + { + device_extension->proxy = proxy; + device_extension->use_proxy = TRUE; + } + else + device_extension->use_proxy = FALSE; + + device_extension->proxy_unmap = proxy_supports_unmap; + + device_extension->proxy_zero = proxy_supports_zero; + + device_extension->media_change_count++; + + device_extension->drive_letter = CreateData->DriveLetter; + + device_extension->device_thread = KeGetCurrentThread(); + + device_extension->parallel_io = parallel_io; + + device_extension->file_object = file_object; + + device_extension->dev_object = dev_object; + + (*DeviceObject)->Flags &= ~DO_DEVICE_INITIALIZING; + + KdPrint(("ImDisk: Device '%ws' created.\n", (PWSTR)device_name_buffer)); + + return STATUS_SUCCESS; +} + diff --git a/sys/devthrd.cpp b/sys/devthrd.cpp new file mode 100644 index 0000000..339ce45 --- /dev/null +++ b/sys/devthrd.cpp @@ -0,0 +1,1281 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +VOID +ImDiskDeviceThreadRead(IN PIRP Irp, + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_OBJECT DeviceObject) +{ + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + PUCHAR system_buffer = + (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, + NormalPagePriority); + LARGE_INTEGER offset; + KLOCK_QUEUE_HANDLE lock_handle; + + if (system_buffer == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + return; + } + + if (DeviceExtension->vm_disk) + { +#ifdef _WIN64 + ULONG_PTR vm_offset = + io_stack->Parameters.Read.ByteOffset.QuadPart; +#else + ULONG_PTR vm_offset = + io_stack->Parameters.Read.ByteOffset.LowPart; +#endif + + RtlCopyMemory(system_buffer, + DeviceExtension->image_buffer + + vm_offset, + io_stack->Parameters.Read.Length); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = io_stack->Parameters.Read.Length; + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset.QuadPart += + Irp->IoStatus.Information; + } + + return; + } + + offset.QuadPart = io_stack->Parameters.Read.ByteOffset.QuadPart + + DeviceExtension->image_offset.QuadPart; + + ImDiskAcquireLock(&DeviceExtension->last_io_lock, &lock_handle); + + if ((DeviceExtension->last_io_data != NULL) & + (DeviceExtension->last_io_length < + io_stack->Parameters.Read.Length)) + { + ExFreePoolWithTag(DeviceExtension->last_io_data, POOL_TAG); + DeviceExtension->last_io_data = NULL; + } + + DeviceExtension->last_io_offset = 0; + DeviceExtension->last_io_length = 0; + + ImDiskReleaseLock(&lock_handle); + + if (DeviceExtension->last_io_data == NULL) + DeviceExtension->last_io_data = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, + io_stack->Parameters.Read.Length, + POOL_TAG); + + if (DeviceExtension->last_io_data == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + return; + } + + if (DeviceExtension->use_proxy) + { + Irp->IoStatus.Status = + ImDiskReadProxy(&DeviceExtension->proxy, + &Irp->IoStatus, + &DeviceExtension->terminate_thread, + DeviceExtension->last_io_data, + io_stack->Parameters.Read.Length, + &offset); + + if (!NT_SUCCESS(Irp->IoStatus.Status)) + { + KdPrint(("ImDisk: Read failed on device %i: %#x.\n", + DeviceExtension->device_number, + Irp->IoStatus.Status)); + + // If indicating that proxy connection died we can do + // nothing else but remove this device. + // if (Irp->IoStatus.Status == STATUS_CONNECTION_RESET) + ImDiskRemoveVirtualDisk(DeviceObject); + + Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; + Irp->IoStatus.Information = 0; + } + } + else + { + Irp->IoStatus.Status = + NtReadFile(DeviceExtension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + DeviceExtension->last_io_data, + io_stack->Parameters.Read.Length, + &offset, + NULL); + } + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + RtlCopyMemory(system_buffer, DeviceExtension->last_io_data, + Irp->IoStatus.Information); + + if (DeviceExtension->byte_swap) + ImDiskByteSwapBuffer(system_buffer, + Irp->IoStatus.Information); + + DeviceExtension->last_io_offset = + io_stack->Parameters.Read.ByteOffset.QuadPart; + + DeviceExtension->last_io_length = + (ULONG)Irp->IoStatus.Information; + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset.QuadPart += + Irp->IoStatus.Information; + } + } + else + { + ExFreePoolWithTag(DeviceExtension->last_io_data, POOL_TAG); + DeviceExtension->last_io_data = NULL; + } + + return; +} + +VOID +ImDiskDeviceThreadWrite(IN PIRP Irp, + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_OBJECT DeviceObject) +{ + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + PUCHAR system_buffer = + (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, + NormalPagePriority); + LARGE_INTEGER offset; + BOOLEAN set_zero_data = FALSE; + KLOCK_QUEUE_HANDLE lock_handle; + + if (system_buffer == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + return; + } + + if (!DeviceExtension->image_modified) + { + DeviceExtension->image_modified = TRUE; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + } + + if (DeviceExtension->vm_disk) + { +#ifdef _WIN64 + ULONG_PTR vm_offset = + io_stack->Parameters.Write.ByteOffset.QuadPart; +#else + ULONG_PTR vm_offset = + io_stack->Parameters.Write.ByteOffset.LowPart; +#endif + + RtlCopyMemory(DeviceExtension->image_buffer + + vm_offset, + system_buffer, + io_stack->Parameters.Write.Length); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = io_stack->Parameters.Write.Length; + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset.QuadPart += + Irp->IoStatus.Information; + } + + return; + } + + offset.QuadPart = io_stack->Parameters.Write.ByteOffset.QuadPart + + DeviceExtension->image_offset.QuadPart; + + ImDiskAcquireLock(&DeviceExtension->last_io_lock, &lock_handle); + + if ((DeviceExtension->last_io_data != NULL) & + (DeviceExtension->last_io_length < + io_stack->Parameters.Write.Length)) + { + ExFreePoolWithTag(DeviceExtension->last_io_data, POOL_TAG); + DeviceExtension->last_io_data = NULL; + } + + DeviceExtension->last_io_offset = 0; + DeviceExtension->last_io_length = 0; + + ImDiskReleaseLock(&lock_handle); + + if (DeviceExtension->last_io_data == NULL) + { + DeviceExtension->last_io_data = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, + io_stack->Parameters.Write.Length, + POOL_TAG); + } + + if (DeviceExtension->last_io_data == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + return; + } + + RtlCopyMemory(DeviceExtension->last_io_data, system_buffer, + io_stack->Parameters.Write.Length); + + if ((DeviceExtension->use_set_zero_data || + (DeviceExtension->use_proxy && + DeviceExtension->proxy_zero)) && + ImDiskIsBufferZero(DeviceExtension->last_io_data, + io_stack->Parameters.Write.Length)) + { + set_zero_data = TRUE; + } + + if ((!set_zero_data) && DeviceExtension->byte_swap) + { + ImDiskByteSwapBuffer(DeviceExtension->last_io_data, + Irp->IoStatus.Information); + } + + if (DeviceExtension->use_proxy) + { + if (set_zero_data && DeviceExtension->proxy_zero) + { + DEVICE_DATA_SET_RANGE range; + range.StartingOffset = offset.QuadPart; + range.LengthInBytes = io_stack->Parameters.Write.Length; + + Irp->IoStatus.Status = + ImDiskUnmapOrZeroProxy(&DeviceExtension->proxy, + IMDPROXY_REQ_ZERO, + &Irp->IoStatus, + &DeviceExtension->terminate_thread, + 1, + &range); + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + Irp->IoStatus.Information = + io_stack->Parameters.Write.Length; + } + } + else + { + Irp->IoStatus.Status = + ImDiskWriteProxy(&DeviceExtension->proxy, + &Irp->IoStatus, + &DeviceExtension->terminate_thread, + DeviceExtension->last_io_data, + io_stack->Parameters.Write.Length, + &offset); + } + + if (!NT_SUCCESS(Irp->IoStatus.Status)) + { + KdPrint(("ImDisk: Write failed on device %i: %#x.\n", + DeviceExtension->device_number, + Irp->IoStatus.Status)); + + // If indicating that proxy connection died we can do + // nothing else but remove this device. + if (Irp->IoStatus.Status == STATUS_CONNECTION_RESET) + ImDiskRemoveVirtualDisk(DeviceObject); + + Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; + Irp->IoStatus.Information = 0; + } + } + else + { + if (set_zero_data) + { + FILE_ZERO_DATA_INFORMATION zero_data; + zero_data.FileOffset = offset; + zero_data.BeyondFinalZero.QuadPart = offset.QuadPart + + io_stack->Parameters.Write.Length; + + Irp->IoStatus.Status = + NtFsControlFile(DeviceExtension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + FSCTL_SET_ZERO_DATA, + &zero_data, + sizeof(zero_data), + NULL, + 0); + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + KdPrint2(("ImDisk: Zero block set.\n")); + Irp->IoStatus.Information = + io_stack->Parameters.Write.Length; + } + else + { + KdPrint(("ImDisk: Volume does not support " + "FSCTL_SET_ZERO_DATA: 0x%#X\n", + Irp->IoStatus.Status)); + + Irp->IoStatus.Information = 0; + set_zero_data = FALSE; + DeviceExtension->use_set_zero_data = FALSE; + } + } + + if (!set_zero_data) + { + Irp->IoStatus.Status = NtWriteFile( + DeviceExtension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + DeviceExtension->last_io_data, + io_stack->Parameters.Write.Length, + &offset, + NULL); + } + } + + if (NT_SUCCESS(Irp->IoStatus.Status)) + { + DeviceExtension->last_io_offset = + io_stack->Parameters.Write.ByteOffset.QuadPart; + DeviceExtension->last_io_length = + io_stack->Parameters.Write.Length; + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset.QuadPart += + Irp->IoStatus.Information; + } + } + else + { + ExFreePoolWithTag(DeviceExtension->last_io_data, POOL_TAG); + DeviceExtension->last_io_data = NULL; + } + + return; +} + +VOID +ImDiskDeviceThreadFlushBuffers(IN PIRP Irp, + IN PDEVICE_EXTENSION DeviceExtension) +{ + NTSTATUS status; + PIRP image_irp; + KEVENT io_complete_event; + PIO_STACK_LOCATION image_io_stack; + + if (DeviceExtension->file_object == NULL) + { + status = ObReferenceObjectByHandle( + DeviceExtension->file_handle, + FILE_WRITE_ATTRIBUTES | + FILE_WRITE_DATA | + SYNCHRONIZE, + *IoFileObjectType, + KernelMode, + (PVOID*)&DeviceExtension->file_object, + NULL); + + if (!NT_SUCCESS(status)) + { + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + KdPrint(("ImDisk: ObReferenceObjectByHandle failed on image handle: %#x\n", + status)); + + return; + } + + DeviceExtension->dev_object = IoGetRelatedDeviceObject( + DeviceExtension->file_object); + } + + KeInitializeEvent(&io_complete_event, + NotificationEvent, + FALSE); + + image_irp = IoBuildSynchronousFsdRequest( + IRP_MJ_FLUSH_BUFFERS, + DeviceExtension->dev_object, + NULL, + 0, + NULL, + &io_complete_event, + &Irp->IoStatus); + + if (image_irp == NULL) + { + KdPrint(("ImDisk: IoBuildSynchronousFsdRequest failed for image object.\n")); + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + return; + } + + image_io_stack = IoGetNextIrpStackLocation(image_irp); + image_io_stack->FileObject = DeviceExtension->file_object; + + status = IoCallDriver(DeviceExtension->dev_object, image_irp); + + if (status == STATUS_PENDING) + { + KdPrint(("ImDisk: Waiting for IoCallDriver to complete.\n")); + + KeWaitForSingleObject(&io_complete_event, + Executive, + KernelMode, + FALSE, + NULL); + } + else + { + Irp->IoStatus.Status = status; + } + + KdPrint(("ImDisk: IoCallDriver result for flush request: %#x\n", + status)); + + return; +} + +VOID +ImDiskDeviceThreadDeviceControl(IN PIRP Irp, + IN PDEVICE_EXTENSION DeviceExtension, + IN PDEVICE_OBJECT DeviceObject) +{ + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + + switch (io_stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_CDROM_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY2: + { + PUCHAR buffer; + + buffer = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, 1, POOL_TAG); + + if (buffer == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + break; + } + + if (DeviceExtension->use_proxy) + { + Irp->IoStatus.Status = + ImDiskReadProxy(&DeviceExtension->proxy, + &Irp->IoStatus, + &DeviceExtension->terminate_thread, + buffer, + 0, + &DeviceExtension->image_offset); + } + else + { + Irp->IoStatus.Status = + NtReadFile(DeviceExtension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + buffer, + 0, + &DeviceExtension->image_offset, + NULL); + } + + ExFreePoolWithTag(buffer, POOL_TAG); + + if (!NT_SUCCESS(Irp->IoStatus.Status)) + { + KdPrint(("ImDisk: Verify failed on device %i.\n", + DeviceExtension->device_number)); + + // If indicating that proxy connection died we can do + // nothing else but remove this device. + if (Irp->IoStatus.Status == STATUS_CONNECTION_RESET) + ImDiskRemoveVirtualDisk(DeviceObject); + + Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; + Irp->IoStatus.Information = 0; + break; + } + + KdPrint(("ImDisk: Verify ok on device %i.\n", + DeviceExtension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(ULONG)) + Irp->IoStatus.Information = 0; + else + { + *(PULONG)Irp->AssociatedIrp.SystemBuffer = + DeviceExtension->media_change_count; + + Irp->IoStatus.Information = sizeof(ULONG); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + + case IOCTL_IMDISK_IOCTL_PASS_THROUGH: + case IOCTL_IMDISK_FSCTL_PASS_THROUGH: + { + NTSTATUS status; + ULONG ctl_code = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + PVOID in_buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer + + sizeof(ULONG); + PVOID out_buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG in_size = + io_stack->Parameters.DeviceIoControl.InputBufferLength - + sizeof(ULONG); + ULONG out_size = + io_stack->Parameters.DeviceIoControl.OutputBufferLength; + + UCHAR func; + PIRP lower_irp; + KEVENT event; + PIO_STACK_LOCATION lower_io_stack; + + if (DeviceExtension->file_object == NULL) + { + status = ObReferenceObjectByHandle( + DeviceExtension->file_handle, + FILE_WRITE_ATTRIBUTES | + FILE_WRITE_DATA | + SYNCHRONIZE, + *IoFileObjectType, + KernelMode, + (PVOID*)&DeviceExtension->file_object, + NULL); + + if (!NT_SUCCESS(status)) + { + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + KdPrint(("ImDisk: ObReferenceObjectByHandle failed on image handle: %#x\n", + status)); + + break; + } + + DeviceExtension->dev_object = IoGetRelatedDeviceObject( + DeviceExtension->file_object); + } + + if (io_stack->MajorFunction == IOCTL_IMDISK_FSCTL_PASS_THROUGH) + { + KdPrint(("ImDisk: IOCTL_IMDISK_FSCTL_PASS_THROUGH for device %i control code %#x.\n", + DeviceExtension->device_number, ctl_code)); + + func = IRP_MJ_FILE_SYSTEM_CONTROL; + } + else + { + KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL_PASS_THROUGH for device %i control code %#x.\n", + DeviceExtension->device_number, ctl_code)); + + func = IRP_MJ_DEVICE_CONTROL; + } + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + lower_irp = IoBuildDeviceIoControlRequest( + ctl_code, + DeviceExtension->dev_object, + in_buffer, + in_size, + out_buffer, + out_size, + FALSE, + &event, + &Irp->IoStatus); + + if (lower_irp == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + break; + } + + lower_irp->RequestorMode = Irp->RequestorMode; + + lower_io_stack = IoGetNextIrpStackLocation(lower_irp); + lower_io_stack->FileObject = DeviceExtension->file_object; + lower_io_stack->MajorFunction = func; + + status = IoCallDriver(DeviceExtension->dev_object, + lower_irp); + + if (status == STATUS_PENDING) + { + KeWaitForSingleObject(&event, Executive, KernelMode, + FALSE, NULL); + } + else + { + Irp->IoStatus.Status = status; + } + + KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL/FSCTL_PASS_THROUGH for device %i control code %#x result status %#x.\n", + DeviceExtension->device_number, ctl_code, status)); + + break; + } + +#ifdef INCLUDE_VFD_ORIGIN + + case IOCTL_DISK_FORMAT_TRACKS: + case IOCTL_DISK_FORMAT_TRACKS_EX: + { + NTSTATUS status = + ImDiskFloppyFormat(DeviceExtension, Irp); + + if (!NT_SUCCESS(status)) + { + // If indicating that proxy connection died we can do + // nothing else but remove this device. + if (status == STATUS_CONNECTION_RESET) + ImDiskRemoveVirtualDisk(DeviceObject); + + Irp->IoStatus.Status = STATUS_DEVICE_REMOVED; + Irp->IoStatus.Information = 0; + break; + } + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + break; + } + +#endif // INCLUDE_VFD_ORIGIN + + case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: + { + Irp->IoStatus.Information = 0; + + PDEVICE_MANAGE_DATA_SET_ATTRIBUTES attrs = + (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES) + Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Status = STATUS_SUCCESS; + + int items = attrs->DataSetRangesLength / + sizeof(DEVICE_DATA_SET_RANGE); + + PDEVICE_DATA_SET_RANGE range = (PDEVICE_DATA_SET_RANGE) + ExAllocatePoolWithTag(PagedPool, + items * sizeof(DEVICE_DATA_SET_RANGE), POOL_TAG); + + if (range == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(range, (PUCHAR)attrs + attrs->DataSetRangesOffset, + items * sizeof(DEVICE_DATA_SET_RANGE)); + + if (DeviceExtension->image_offset.QuadPart > 0) + { + for (int i = 0; i < items; i++) + { + range[i].StartingOffset += + DeviceExtension->image_offset.QuadPart; + } + } + + Irp->IoStatus.Status = ImDiskUnmapOrZeroProxy( + &DeviceExtension->proxy, + IMDPROXY_REQ_UNMAP, + &Irp->IoStatus, + &DeviceExtension->terminate_thread, + items, + range); + + ExFreePoolWithTag(range, POOL_TAG); + + KdPrint(("ImDisk: Unmap result on device %i: %#x.\n", + DeviceExtension->device_number, + Irp->IoStatus.Status)); + + break; + } + + case IOCTL_DISK_GROW_PARTITION: + { + NTSTATUS status; + FILE_END_OF_FILE_INFORMATION new_size; + FILE_STANDARD_INFORMATION file_standard_information; + PDISK_GROW_PARTITION grow_data = (PDISK_GROW_PARTITION)Irp->AssociatedIrp.SystemBuffer; + + KdPrint(("ImDisk: Request to grow device %i by %I64i bytes.\n", + DeviceExtension->device_number, + grow_data->BytesToGrow.QuadPart)); + + new_size.EndOfFile.QuadPart = + DeviceExtension->disk_geometry.Cylinders.QuadPart + + grow_data->BytesToGrow.QuadPart; + + KdPrint(("ImDisk: New size of device %i will be %I64i bytes.\n", + DeviceExtension->device_number, + new_size.EndOfFile.QuadPart)); + + if (new_size.EndOfFile.QuadPart <= 0) + { + Irp->IoStatus.Status = STATUS_END_OF_MEDIA; + Irp->IoStatus.Information = 0; + break; + } + + if (DeviceExtension->vm_disk) + { + PVOID new_image_buffer = NULL; + SIZE_T free_size = 0; +#ifdef _WIN64 + ULONG_PTR old_size = + DeviceExtension->disk_geometry.Cylinders.QuadPart; + SIZE_T max_size = new_size.EndOfFile.QuadPart; +#else + ULONG_PTR old_size = + DeviceExtension->disk_geometry.Cylinders.LowPart; + SIZE_T max_size = new_size.EndOfFile.LowPart; + + // A vm type disk cannot be extended to a larger size than + // 2 GB. + if (new_size.EndOfFile.QuadPart & 0xFFFFFFFF80000000) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + break; + } +#endif // _WIN64 + + KdPrint(("ImDisk: Allocating %I64u bytes.\n", + (ULONGLONG)max_size)); + + status = ZwAllocateVirtualMemory(NtCurrentProcess(), + &new_image_buffer, + 0, + &max_size, + MEM_COMMIT, + PAGE_READWRITE); + + if (!NT_SUCCESS(status)) + { + status = STATUS_NO_MEMORY; + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + break; + } + + RtlCopyMemory(new_image_buffer, + DeviceExtension->image_buffer, + min(old_size, max_size)); + + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&DeviceExtension->image_buffer, + &free_size, + MEM_RELEASE); + + DeviceExtension->image_buffer = (PUCHAR)new_image_buffer; + DeviceExtension->disk_geometry.Cylinders = + new_size.EndOfFile; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + + // For proxy-type disks the new size is just accepted and + // that's it. + if (DeviceExtension->use_proxy) + { + DeviceExtension->disk_geometry.Cylinders = + new_size.EndOfFile; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + + // Image file backed disks left to do. + + // For disks with offset, refuse to extend size. Otherwise we + // could break compatibility with the header data we have + // skipped and we don't know about. + if (DeviceExtension->image_offset.QuadPart != 0) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + break; + } + + status = + ZwQueryInformationFile(DeviceExtension->file_handle, + &Irp->IoStatus, + &file_standard_information, + sizeof file_standard_information, + FileStandardInformation); + + if (!NT_SUCCESS(status)) + { + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + break; + } + + KdPrint(("ImDisk: Current image size is %I64u bytes.\n", + file_standard_information.EndOfFile.QuadPart)); + + if (file_standard_information.EndOfFile.QuadPart >= + new_size.EndOfFile.QuadPart) + { + DeviceExtension->disk_geometry.Cylinders = + new_size.EndOfFile; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + break; + } + + // For other, fixed file-backed disks we need to adjust the + // physical file size. + + KdPrint(("ImDisk: Setting new image size to %I64u bytes.\n", + new_size.EndOfFile.QuadPart)); + + status = ZwSetInformationFile(DeviceExtension->file_handle, + &Irp->IoStatus, + &new_size, + sizeof new_size, + FileEndOfFileInformation); + + if (NT_SUCCESS(status)) + { + DeviceExtension->disk_geometry.Cylinders = + new_size.EndOfFile; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + } + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + break; + } + + default: + Irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR; + } +} + +VOID +ImDiskDeviceThread(IN PVOID Context) +{ + PDEVICE_THREAD_DATA device_thread_data; + PDEVICE_OBJECT device_object; + PDEVICE_EXTENSION device_extension; + LARGE_INTEGER time_out; + BOOLEAN system_drive_letter; + + ASSERT(Context != NULL); + + KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); + + device_thread_data = (PDEVICE_THREAD_DATA)Context; + + system_drive_letter = !device_thread_data->caller_waiting; + + // This is in case this thread is created by + // ImDiskAddVirtualDiskAfterInitialization() when called from DriverEntry(). + // That indicates that no-one is waiting for us to return any status + // in the device_thread_data members and that there is no-one freeing the + // init structures after we are finished with them. + // It also means that we need to wait for the control device to get ready for + // I/O (in case a proxy or something need to call this driver during device + // initialization). + while (ImDiskCtlDevice->Flags & DO_DEVICE_INITIALIZING) + { + LARGE_INTEGER wait_time; + + KdPrint2(("ImDisk: Driver still initializing, waiting 100 ms...\n")); + + wait_time.QuadPart = -1000000; + KeDelayExecutionThread(KernelMode, FALSE, &wait_time); + } + + device_thread_data->status = ImDiskCreateDevice( + device_thread_data->driver_object, + device_thread_data->create_data, + device_thread_data->client_thread, + &device_object); + + if (!NT_SUCCESS(device_thread_data->status)) + { + if (device_thread_data->caller_waiting) + KeSetEvent(&device_thread_data->created_event, (KPRIORITY)0, FALSE); + else + { + ExFreePoolWithTag(device_thread_data->create_data, POOL_TAG); + ExFreePoolWithTag(device_thread_data, POOL_TAG); + } + + ImDiskLogError((device_thread_data->driver_object, + 0, + 0, + NULL, + 0, + 1000, + device_thread_data->status, + 102, + device_thread_data->status, + 0, + 0, + NULL, + L"Error creating virtual disk.")); + + PsTerminateSystemThread(STATUS_SUCCESS); + + return; + } + + // Now we are done with initialization. Let the one that asks us to create + // this device now that, or if no-one left there, clean up init structures + // here. + if (device_thread_data->caller_waiting) + KeSetEvent(&device_thread_data->created_event, (KPRIORITY)0, FALSE); + else + { + ImDiskCreateDriveLetter(device_thread_data->create_data->DriveLetter, + device_thread_data->create_data->DeviceNumber); + + ExFreePoolWithTag(device_thread_data->create_data, POOL_TAG); + ExFreePoolWithTag(device_thread_data, POOL_TAG); + } + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + KdPrint(("ImDisk: Device thread initialized. (flags=%#x)\n", + device_object->Flags)); + + device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension; + + time_out.QuadPart = -1000000; + + // If this is a VM backed disk that should be pre-loaded with an image file + // we have to load the contents of that file now before entering the service + // loop. + if (device_extension->vm_disk && (device_extension->file_handle != NULL)) + { + LARGE_INTEGER byte_offset = device_extension->image_offset; + IO_STATUS_BLOCK io_status; + NTSTATUS status; +#ifdef _WIN64 + SIZE_T disk_size = device_extension->disk_geometry.Cylinders.QuadPart; +#else + SIZE_T disk_size = device_extension->disk_geometry.Cylinders.LowPart; +#endif + + KdPrint(("ImDisk: Reading image file into vm disk buffer.\n")); + + status = + ImDiskSafeReadFile(device_extension->file_handle, + &io_status, + device_extension->image_buffer, + disk_size, + &byte_offset); + + ZwClose(device_extension->file_handle); + device_extension->file_handle = NULL; + + // Failure to read pre-load image is now considered a fatal error + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Failed to read image file (%#x).\n", status)); + + ImDiskRemoveVirtualDisk(device_object); + + // Continue into IRP loop anyway. Above call has inserted a request + // to terminate so everything will be safely freed anyway. + } + else + { + KdPrint(("ImDisk: Image loaded successfully.\n")); + + if (device_extension->byte_swap) + ImDiskByteSwapBuffer(device_extension->image_buffer, + disk_size); + } + } + + for (;;) + { + PLIST_ENTRY request; + + KeClearEvent(&device_extension->request_event); + + request = + ImDiskInterlockedRemoveHeadList(&device_extension->list_head, + &device_extension->list_lock); + + if (request == NULL) + { + PWCHAR symlink_name_buffer; + NTSTATUS status; + PKEVENT wait_objects[] = { + &device_extension->request_event, + &device_extension->terminate_thread + }; + + KdPrint2(("ImDisk: No pending requests. Waiting.\n")); + + status = KeWaitForMultipleObjects(sizeof(wait_objects) / + sizeof(*wait_objects), + (PVOID*)wait_objects, + WaitAny, + Executive, + KernelMode, + FALSE, + NULL, + NULL); + + // While pending requests in queue, service them before terminating + // thread. + if (KeReadStateEvent(&device_extension->request_event)) + continue; + + KdPrint(("ImDisk: Device %i thread is shutting down.\n", + device_extension->device_number)); + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + if (device_extension->drive_letter != 0) + if (system_drive_letter) + ImDiskRemoveDriveLetter(device_extension->drive_letter); + + ImDiskCloseProxy(&device_extension->proxy); + + if (device_extension->last_io_data != NULL) + { + ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); + device_extension->last_io_data = NULL; + } + + if (device_extension->vm_disk) + { + SIZE_T free_size = 0; + if (device_extension->image_buffer != NULL) + ZwFreeVirtualMemory(NtCurrentProcess(), + (PVOID*)&device_extension->image_buffer, + &free_size, MEM_RELEASE); + + device_extension->image_buffer = NULL; + } + else + { + if (device_extension->file_handle != NULL) + ZwClose(device_extension->file_handle); + + device_extension->file_handle = NULL; + + if (device_extension->file_object != NULL) + ObDereferenceObject(device_extension->file_object); + + device_extension->file_object = NULL; + } + + if (device_extension->file_name.Buffer != NULL) + { + ExFreePoolWithTag(device_extension->file_name.Buffer, POOL_TAG); + device_extension->file_name.Buffer = NULL; + device_extension->file_name.Length = 0; + device_extension->file_name.MaximumLength = 0; + } + + // If ReferenceCount is not zero, this device may have outstanding + // IRP-s or otherwise unfinished things to do. Let IRP-s be done by + // continuing this dispatch loop until ReferenceCount is zero. +#pragma warning(suppress: 28175) + if (device_object->ReferenceCount != 0) + { +#if DBG +#pragma warning(suppress: 28175) + LONG ref_count = device_object->ReferenceCount; + DbgPrint("ImDisk: Device %i has %i references. Waiting.\n", + device_extension->device_number, + ref_count); +#endif + + KeDelayExecutionThread(KernelMode, FALSE, &time_out); + + time_out.LowPart <<= 4; + continue; + } + + KdPrint(("ImDisk: Deleting symlink for device %i.\n", + device_extension->device_number)); + + symlink_name_buffer = (PWCHAR) + ExAllocatePoolWithTag(PagedPool, + MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR), POOL_TAG); + + if (symlink_name_buffer == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + UNICODE_STRING symlink_name; + + _snwprintf(symlink_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, + IMDISK_SYMLNK_NATIVE_BASE_NAME L"%u", device_extension->device_number); + symlink_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; + + RtlInitUnicodeString(&symlink_name, symlink_name_buffer); + + KdPrint(("ImDisk: Deleting symlink '%ws'.\n", symlink_name_buffer)); + + status = IoDeleteSymbolicLink(&symlink_name); + + ExFreePoolWithTag(symlink_name_buffer, POOL_TAG); + } + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Cannot delete symlink. (%#x)\n", status)); + } + + KdPrint(("ImDisk: Deleting device object %i.\n", + device_extension->device_number)); + + IoDeleteDevice(device_object); + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + + PsTerminateSystemThread(STATUS_SUCCESS); + + return; + } + + PIRP Irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry); + + switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction) + { + case IRP_MJ_FLUSH_BUFFERS: + ImDiskDeviceThreadFlushBuffers(Irp, device_extension); + break; + + case IRP_MJ_READ: + ImDiskDeviceThreadRead(Irp, device_extension, device_object); + break; + + case IRP_MJ_WRITE: + ImDiskDeviceThreadWrite(Irp, device_extension, device_object); + break; + + case IRP_MJ_DEVICE_CONTROL: + ImDiskDeviceThreadDeviceControl(Irp, device_extension, device_object); + break; + + default: + Irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR; + } + + IoCompleteRequest(Irp, + NT_SUCCESS(Irp->IoStatus.Status) ? + IO_DISK_INCREMENT : IO_NO_INCREMENT); + } +} + diff --git a/sys/floppy.cpp b/sys/floppy.cpp new file mode 100644 index 0000000..d705785 --- /dev/null +++ b/sys/floppy.cpp @@ -0,0 +1,156 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +#pragma code_seg("PAGE") + +#ifdef INCLUDE_VFD_ORIGIN + +// +// Format tracks +// Actually, just fills specified range of tracks with fill characters +// +NTSTATUS +ImDiskFloppyFormat(IN PDEVICE_EXTENSION Extension, + IN PIRP Irp) +{ + PFORMAT_PARAMETERS param; + ULONG track_length; + PUCHAR format_buffer; + LARGE_INTEGER start_offset; + LARGE_INTEGER end_offset; + NTSTATUS status; + + PAGED_CODE(); + + ASSERT(Extension != NULL); + ASSERT(Irp != NULL); + + param = (PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; + + track_length = + Extension->disk_geometry.BytesPerSector * + Extension->disk_geometry.SectorsPerTrack; + + start_offset.QuadPart = + param->StartCylinderNumber * Extension->disk_geometry.TracksPerCylinder * + track_length + param->StartHeadNumber * track_length; + + end_offset.QuadPart = + param->EndCylinderNumber * Extension->disk_geometry.TracksPerCylinder * + track_length + param->EndHeadNumber * track_length; + + if (Extension->vm_disk) + { + LARGE_INTEGER wait_time; + + RtlFillMemory(((PUCHAR)Extension->image_buffer) + start_offset.LowPart, + end_offset.LowPart - start_offset.LowPart + track_length, + MEDIA_FORMAT_FILL_DATA); + + wait_time.QuadPart = -1; + KeDelayExecutionThread(KernelMode, FALSE, &wait_time); + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + start_offset.QuadPart += Extension->image_offset.QuadPart; + end_offset.QuadPart += Extension->image_offset.QuadPart; + + format_buffer = (PUCHAR) + ExAllocatePoolWithTag(PagedPool, track_length, POOL_TAG); + + if (format_buffer == NULL) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlFillMemory(format_buffer, track_length, MEDIA_FORMAT_FILL_DATA); + + do + { + if (Extension->use_proxy) + { + status = ImDiskWriteProxy(&Extension->proxy, + &Irp->IoStatus, + &Extension->terminate_thread, + format_buffer, + track_length, + &start_offset); + } + else + { + status = NtWriteFile(Extension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + format_buffer, + track_length, + &start_offset, + NULL); + } + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk Format failed: Write failed with status %#x.\n", + status)); + + break; + } + + start_offset.QuadPart += track_length; + + } while (start_offset.QuadPart <= end_offset.QuadPart); + + ExFreePoolWithTag(format_buffer, POOL_TAG); + + Irp->IoStatus.Information = 0; + + return status; +} + +#endif // INCLUDE_VFD_ORIGIN diff --git a/sys/imdisk.cpp b/sys/imdisk.cpp index dfa1817..04b5e9c 100644 --- a/sys/imdisk.cpp +++ b/sys/imdisk.cpp @@ -41,466 +41,7 @@ Copyright (C) The FreeBSD Project. Copyright (C) The Regents of the University of California. */ -#include -#include -#include -#include -#include -#include -#include -#include - -/// -/// Definitions and imports are now in the "sources" file and managed by the -/// build utility. -/// - -#ifndef DEBUG_LEVEL -#define DEBUG_LEVEL 0 -#endif - -#if DEBUG_LEVEL >= 2 -#define KdPrint2(x) DbgPrint x -#else -#define KdPrint2(x) -#endif - -#if DEBUG_LEVEL >= 1 -#undef KdPrint -#define KdPrint(x) DbgPrint x -#define ImDiskLogError(x) ImDiskLogDbgError x -#else -#define ImDiskLogError(x) -#endif - -#include "..\inc\ntkmapi.h" -#include "..\inc\imdisk.h" -#include "..\inc\imdproxy.h" - -#pragma warning(disable: 28719) - -#define POOL_TAG 'iDmI' - -#define IMDISK_DEFAULT_LOAD_DEVICES 0 -#define IMDISK_DEFAULT_MAX_DEVICES 64000 - -/// -/// Constants for synthetic geometry of the virtual disks -/// - -// For hard drive partition-style devices -#define SECTOR_SIZE_HDD 512 -#define HEAD_SIZE_HDD 63 - -// For CD-ROM/DVD-style devices -#define SECTOR_SIZE_CD_ROM 2048 -#define SECTORS_PER_TRACK_CD_ROM 32 -#define TRACKS_PER_CYLINDER_CD_ROM 64 - -#ifdef INCLUDE_VFD_ORIGIN - -// For floppy devices. Based on Virtual Floppy Driver, VFD, by Ken Kato. -#define SECTOR_SIZE_FDD 512 -#define TRACKS_PER_CYLINDER_FDD 12 -// -// Sizes in bytes of floppies -// -// 3.5" UHD -#define MEDIA_SIZE_240MB (234752 << 10) -#define MEDIA_SIZE_120MB (123264 << 10) -// 3.5" -#define MEDIA_SIZE_2800KB (2880 << 10) -#define MEDIA_SIZE_1722KB (1722 << 10) -#define MEDIA_SIZE_1680KB (1680 << 10) -#define MEDIA_SIZE_1440KB (1440 << 10) -#define MEDIA_SIZE_820KB (820 << 10) -#define MEDIA_SIZE_720KB (720 << 10) -// 5.25" -#define MEDIA_SIZE_1200KB (1200 << 10) -#define MEDIA_SIZE_640KB (640 << 10) -#define MEDIA_SIZE_360KB (360 << 10) -#define MEDIA_SIZE_320KB (320 << 10) -#define MEDIA_SIZE_180KB (180 << 10) -#define MEDIA_SIZE_160KB (160 << 10) - -// -// Indexes for the following DISK_GEOMETRY table. -// -enum { - // 3.5" UHD - MEDIA_TYPE_240M, - MEDIA_TYPE_120M, - // 3.5" - MEDIA_TYPE_2880K, - MEDIA_TYPE_1722K, - MEDIA_TYPE_1680K, - MEDIA_TYPE_1440K, - MEDIA_TYPE_820K, - MEDIA_TYPE_720K, - // 5.12" - MEDIA_TYPE_1200K, - MEDIA_TYPE_640K, - MEDIA_TYPE_360K, - MEDIA_TYPE_320K, - MEDIA_TYPE_180K, - MEDIA_TYPE_160K -}; - -DISK_GEOMETRY media_table[] = { - // 3.5" UHD - { { 963 }, F3_120M_512, 8, 32, 512 }, - { { 262 }, F3_120M_512, 32, 56, 512 }, - // 3.5" - { { 80 }, F3_2Pt88_512, 2, 36, 512 }, - { { 82 }, F3_1Pt44_512, 2, 21, 512 }, - { { 80 }, F3_1Pt44_512, 2, 21, 512 }, - { { 80 }, F3_1Pt44_512, 2, 18, 512 }, - { { 82 }, F3_720_512, 2, 10, 512 }, - { { 80 }, F3_720_512, 2, 9, 512 }, - // 5.25" - { { 80 }, F5_1Pt2_512, 2, 15, 512 }, - { { 40 }, F5_640_512, 2, 18, 512 }, - { { 40 }, F5_360_512, 2, 9, 512 }, - { { 40 }, F5_320_512, 2, 8, 512 }, - { { 40 }, F5_180_512, 1, 9, 512 }, - { { 40 }, F5_160_512, 1, 8, 512 } -}; - -#define SET_MEDIA_TYPE(geometry, media_index) \ - (geometry.MediaType = media_table[media_index].MediaType) - -#endif // INCLUDE_VFD_ORIGIN - -// -// TOC Data Track returned for virtual CD/DVD -// -#define TOC_DATA_TRACK 0x04 - -// -// Fill character for formatting virtual floppy media -// -#define MEDIA_FORMAT_FILL_DATA 0xf6 - -// This structure is used when a new device is about to be created. It is sent -// to the created device dispatch thread which also creates the device object. -typedef struct _DEVICE_THREAD_DATA -{ - PDRIVER_OBJECT driver_object; - PIMDISK_CREATE_DATA create_data; - PETHREAD client_thread; // The client thread that device should impersonate - KEVENT created_event; // Set when device is created (or creation failed) - BOOLEAN caller_waiting; // If there is a caller waiting to free this data - NTSTATUS status; // Set after device creation attempt -} DEVICE_THREAD_DATA, *PDEVICE_THREAD_DATA; - -typedef struct _PROXY_CONNECTION -{ - enum PROXY_CONNECTION_TYPE - { - PROXY_CONNECTION_DEVICE, - PROXY_CONNECTION_SHM - } connection_type; // Connection type - - union - { - // Valid if connection_type is PROXY_CONNECTION_DEVICE - PFILE_OBJECT device; // Pointer to proxy communication object - - // Valid if connection_type is PROXY_CONNECTION_SHM - struct - { - HANDLE request_event_handle; - PKEVENT request_event; - HANDLE response_event_handle; - PKEVENT response_event; - PUCHAR shared_memory; - ULONG_PTR shared_memory_size; - }; - }; -} PROXY_CONNECTION, *PPROXY_CONNECTION; - -typedef struct _DEVICE_EXTENSION -{ - LIST_ENTRY list_head; - KSPIN_LOCK list_lock; - KEVENT request_event; - KEVENT terminate_thread; - - ULONG device_number; - - HANDLE file_handle; // For file or proxy type - PDEVICE_OBJECT dev_object; // Pointer to image I/O DEVICE_OBJECT - PFILE_OBJECT file_object; // Pointer to image I/O FILE_OBJECT - BOOLEAN parallel_io; // TRUE if image I/O is done in dispatcher thread - PUCHAR image_buffer; // For vm type - BOOLEAN byte_swap; // If image I/O should swap each pair of bytes - BOOLEAN shared_image; // Image opened for shared writing - PROXY_CONNECTION proxy; // Proxy connection data - UNICODE_STRING file_name; // Name of image file, if any - WCHAR drive_letter; // Drive letter if maintained by the driver - DISK_GEOMETRY disk_geometry; // Virtual C/H/S geometry (Cylinders=Total size) - LARGE_INTEGER image_offset; // Offset in bytes in the image file - ULONG media_change_count; - BOOLEAN read_only; - BOOLEAN vm_disk; // TRUE if this device is a virtual memory disk - BOOLEAN awealloc_disk; // TRUE if this device is a physical memory disk - // through AWEAlloc driver - BOOLEAN use_proxy; // TRUE if this device uses proxy device for I/O - BOOLEAN proxy_unmap; // TRUE if proxy supports UNMAP operations - BOOLEAN proxy_zero; // TRUE if proxy supports ZERO operations - BOOLEAN image_modified; // TRUE if this device has been written to - LONG special_file_count; // Number of swapfiles/hiberfiles on device - BOOLEAN use_set_zero_data; // TRUE if FSCTL_SET_ZERO_DATA is used to write - // all zeros blocks - BOOLEAN no_file_level_trim; // TRUE if last file level trim failed - - PKTHREAD device_thread; // Pointer to the worker thread object - - KSPIN_LOCK last_io_lock; // Last I/O buffer for fast re-reads - PUCHAR last_io_data; - LONGLONG last_io_offset; - ULONG last_io_length; - -} DEVICE_EXTENSION, *PDEVICE_EXTENSION; - -typedef struct _REFERENCED_OBJECT -{ - LIST_ENTRY list_entry; - PFILE_OBJECT file_object; -} REFERENCED_OBJECT, *PREFERENCED_OBJECT; - -// Prototypes for functions defined in this driver - -EXTERN_C DRIVER_INITIALIZE DriverEntry; - -DRIVER_UNLOAD ImDiskUnload; - -VOID -ImDiskFindFreeDeviceNumber(PDRIVER_OBJECT DriverObject, - PULONG DeviceNumber); - -VOID -ImDiskLogDbgError(IN PVOID Object, - IN UCHAR MajorFunctionCode, - IN UCHAR RetryCount, - IN PULONG DumpData, - IN USHORT DumpDataSize, - IN USHORT EventCategory, - IN NTSTATUS ErrorCode, - IN ULONG UniqueErrorValue, - IN NTSTATUS FinalStatus, - IN ULONG SequenceNumber, - IN ULONG IoControlCode, - IN PLARGE_INTEGER DeviceOffset, - IN PWCHAR Message); - -NTSTATUS -ImDiskAddVirtualDisk(IN PDRIVER_OBJECT DriverObject, - IN OUT PIMDISK_CREATE_DATA CreateData, - IN PETHREAD ClientThread); - -NTSTATUS -ImDiskAddVirtualDiskAfterInitialization(IN PDRIVER_OBJECT DriverObject, - IN HANDLE ParameterKey, - IN ULONG DeviceNumber); - -NTSTATUS -ImDiskCreateDriveLetter(IN WCHAR DriveLetter, - IN ULONG DeviceNumber); - -NTSTATUS -ImDiskRemoveDriveLetter(IN WCHAR DriveLetter); - -VOID -ImDiskRemoveVirtualDisk(IN PDEVICE_OBJECT DeviceObject); - -__drv_dispatchType(IRP_MJ_CREATE) -__drv_dispatchType(IRP_MJ_CLOSE) -DRIVER_DISPATCH ImDiskCreateClose; - -__drv_dispatchType(IRP_MJ_READ) -__drv_dispatchType(IRP_MJ_WRITE) -DRIVER_DISPATCH ImDiskReadWrite; - -__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) -__drv_dispatchType(IRP_MJ_INTERNAL_DEVICE_CONTROL) -DRIVER_DISPATCH ImDiskDeviceControl; - -__drv_dispatchType(IRP_MJ_PNP) -DRIVER_DISPATCH ImDiskDispatchPnP; - -__drv_dispatchType(IRP_MJ_QUERY_INFORMATION) -DRIVER_DISPATCH ImDiskQueryInformation; - -__drv_dispatchType(IRP_MJ_SET_INFORMATION) -DRIVER_DISPATCH ImDiskSetInformation; - -__drv_dispatchType(IRP_MJ_FLUSH_BUFFERS) -DRIVER_DISPATCH ImDiskFlushBuffers; - -KSTART_ROUTINE ImDiskDeviceThread; - -IO_COMPLETION_ROUTINE ImDiskReadWriteLowerDeviceCompletion; - -NTSTATUS -ImDiskConnectProxy(IN OUT PPROXY_CONNECTION Proxy, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN ULONG Flags, - IN PWSTR ConnectionString, - IN USHORT ConnectionStringLength); - -VOID -ImDiskCloseProxy(IN PPROXY_CONNECTION Proxy); - -NTSTATUS -ImDiskQueryInformationProxy(IN PPROXY_CONNECTION Proxy, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - OUT PIMDPROXY_INFO_RESP ProxyInfoResponse, - IN ULONG ProxyInfoResponseLength); - -NTSTATUS -ImDiskReadProxy(IN PPROXY_CONNECTION Proxy, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - OUT PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset); - -NTSTATUS -ImDiskWriteProxy(IN PPROXY_CONNECTION Proxy, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset); - -NTSTATUS -ImDiskUnmapOrZeroProxy(IN PPROXY_CONNECTION Proxy, - IN ULONGLONG RequestCode, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN ULONG Items, - IN PDEVICE_DATA_SET_RANGE Ranges); - -// -// Reads in a loop up to "Length" or until eof reached. -// -NTSTATUS -ImDiskSafeReadFile(IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID Buffer, - IN SIZE_T Length, - IN PLARGE_INTEGER Offset); - -NTSTATUS -ImDiskReadWriteLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension); - -#ifdef INCLUDE_VFD_ORIGIN - -NTSTATUS -ImDiskFloppyFormat(IN PDEVICE_EXTENSION Extension, - IN PIRP Irp); - -#endif // INCLUDE_VFD_ORIGIN - -#ifdef _AMD64_ - -#define ImDiskAcquireLock KeAcquireInStackQueuedSpinLock - -#define ImDiskReleaseLock KeReleaseInStackQueuedSpinLock - -FORCEINLINE -VOID -ImDiskInterlockedInsertTailList( - PLIST_ENTRY ListHead, - PLIST_ENTRY ListEntry, - PKSPIN_LOCK SpinLock) -{ - KLOCK_QUEUE_HANDLE lock_handle; - - KeAcquireInStackQueuedSpinLock(SpinLock, &lock_handle); - - InsertTailList(ListHead, ListEntry); - - KeReleaseInStackQueuedSpinLock(&lock_handle); -} - -FORCEINLINE -PLIST_ENTRY -ImDiskInterlockedRemoveHeadList( - PLIST_ENTRY ListHead, - PKSPIN_LOCK SpinLock) -{ - KLOCK_QUEUE_HANDLE lock_handle; - PLIST_ENTRY item; - - KeAcquireInStackQueuedSpinLock(SpinLock, &lock_handle); - - item = RemoveHeadList(ListHead); - - if (item == ListHead) - { - item = NULL; - } - - KeReleaseInStackQueuedSpinLock(&lock_handle); - - return item; -} - -#else - -#define ImDiskAcquireLock(SpinLock, LockHandle) \ - { \ - (LockHandle)->LockQueue.Lock = (SpinLock); \ - KeAcquireSpinLock((LockHandle)->LockQueue.Lock, &(LockHandle)->OldIrql); \ - } - -#define ImDiskReleaseLock(LockHandle) \ - { \ - KeReleaseSpinLock((LockHandle)->LockQueue.Lock, (LockHandle)->OldIrql); \ - } - -#define ImDiskInterlockedInsertTailList (VOID)ExInterlockedInsertTailList - -#define ImDiskInterlockedRemoveHeadList ExInterlockedRemoveHeadList - -#endif - -FORCEINLINE -BOOLEAN -ImDiskIsBufferZero(PVOID Buffer, ULONG Length) -{ - PULONGLONG ptr; - - if (Length < sizeof(ULONGLONG)) - return FALSE; - - for (ptr = (PULONGLONG)Buffer; - (ptr <= (PULONGLONG)((PUCHAR)Buffer + Length - sizeof(ULONGLONG))) && - (*ptr == 0); ptr++); - - return (BOOLEAN)(ptr == (PULONGLONG)((PUCHAR)Buffer + Length)); -} - -FORCEINLINE -VOID -ImDiskByteSwapBuffer(IN OUT PUCHAR Buffer, - IN ULONG_PTR Length) -{ - PUCHAR ptr; - - for (ptr = Buffer; - (ULONG_PTR)(ptr - Buffer) < Length; - ptr += 2) - { - UCHAR b1 = ptr[1]; - ptr[1] = ptr[0]; - ptr[0] = b1; - } -} +#include "imdsksys.h" // // Pointer to the controller device object. @@ -767,16 +308,16 @@ DriverEntry(IN PDRIVER_OBJECT DriverObject, if (key_handle != NULL) ZwClose(key_handle); - DriverObject->MajorFunction[IRP_MJ_CREATE] = ImDiskCreateClose; - DriverObject->MajorFunction[IRP_MJ_CLOSE] = ImDiskCreateClose; - DriverObject->MajorFunction[IRP_MJ_READ] = ImDiskReadWrite; - DriverObject->MajorFunction[IRP_MJ_WRITE] = ImDiskReadWrite; - DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ImDiskDeviceControl; - DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ImDiskDeviceControl; + DriverObject->MajorFunction[IRP_MJ_CREATE] = ImDiskDispatchCreateClose; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = ImDiskDispatchCreateClose; + DriverObject->MajorFunction[IRP_MJ_READ] = ImDiskDispatchReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = ImDiskDispatchReadWrite; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ImDiskDispatchDeviceControl; + DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ImDiskDispatchDeviceControl; DriverObject->MajorFunction[IRP_MJ_PNP] = ImDiskDispatchPnP; - DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = ImDiskQueryInformation; - DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = ImDiskSetInformation; - DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ImDiskFlushBuffers; + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = ImDiskDispatchQueryInformation; + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = ImDiskDispatchSetInformation; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ImDiskDispatchFlushBuffers; DriverObject->DriverUnload = ImDiskUnload; @@ -1543,7345 +1084,244 @@ ImDiskCreateDriveLetter(IN WCHAR DriveLetter, return status; } -// Parses BPB formatted geometry to a DISK_GEOMETRY structure. VOID -ImDiskReadFormattedGeometry(IN OUT PIMDISK_CREATE_DATA CreateData, - IN PFAT_BPB BPB) +ImDiskUnload(IN PDRIVER_OBJECT DriverObject) { - USHORT tmp; + PDEVICE_OBJECT device_object; + PLIST_ENTRY list_entry; PAGED_CODE(); - KdPrint - (("ImDisk: Detected BPB values:\n" - "Bytes per sect: %u\n" - "Sectors per cl: %u\n" - "Reserved sect : %u\n" - "FAT count : %u\n" - "FAT root entr : %u\n" - "Total sectors : %u\n" - "Media descript: 0x%.2X\n" - "Sectors pr FAT: %u\n" - "Sect per track: %u\n" - "Number of head: %u\n", - (ULONG)BPB->BytesPerSector, - (ULONG)BPB->SectorsPerCluster, - (ULONG)BPB->ReservedSectors, - (ULONG)BPB->NumberOfFileAllocationTables, - (ULONG)BPB->NumberOfRootEntries, - (ULONG)BPB->NumberOfSectors, - (LONG)BPB->MediaDescriptor, - (ULONG)BPB->SectorsPerFileAllocationTable, - (ULONG)BPB->SectorsPerTrack, - (ULONG)BPB->NumberOfHeads)); - - // Some sanity checks. Could this really be valid BPB values? Bytes per - // sector is multiple of 2 etc? - if (BPB->BytesPerSector == 0) - return; + DbgPrint("ImDisk: Entering ImDiskUnload for driver %p.\n", + DriverObject); - if (BPB->SectorsPerTrack >= 64) - return; + if (RefreshEvent != NULL) + { + KdPrint(("ImDisk: Pulsing and closing refresh event.\n")); - if (BPB->NumberOfHeads >= 256) - return; + KePulseEvent(RefreshEvent, 0, FALSE); + ObDereferenceObject(RefreshEvent); + RefreshEvent = NULL; + } - tmp = BPB->BytesPerSector; - while ((tmp & 0x0001) == 0) - tmp >>= 1; - if ((tmp ^ 0x0001) != 0) - return; + KdPrint(("ImDisk: Current device objects chain dump for this driver:\n")); - if (CreateData->DiskGeometry.SectorsPerTrack == 0) - CreateData->DiskGeometry.SectorsPerTrack = BPB->SectorsPerTrack; + for (device_object = DriverObject->DeviceObject; + device_object != NULL;) + { + KdPrint(("%p -> ", device_object)); + device_object = device_object->NextDevice; + } - if (CreateData->DiskGeometry.TracksPerCylinder == 0) - CreateData->DiskGeometry.TracksPerCylinder = BPB->NumberOfHeads; + KdPrint(("End of device chain.\n")); - if (CreateData->DiskGeometry.BytesPerSector == 0) - CreateData->DiskGeometry.BytesPerSector = BPB->BytesPerSector; + while ((list_entry = RemoveHeadList(&ReferencedObjects)) != + &ReferencedObjects) + { + PREFERENCED_OBJECT record = + CONTAINING_RECORD(list_entry, REFERENCED_OBJECT, list_entry); -#ifdef INCLUDE_VFD_ORIGIN + DbgPrint("ImDisk: Freeing unclaimed referenced object: %p\n", + record->file_object); - if (((IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) | - (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0)) & - (CreateData->DiskGeometry.MediaType == Unknown)) - switch (CreateData->DiskGeometry.Cylinders.QuadPart) - { - // 3.5" formats - case MEDIA_SIZE_240MB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_240M); - break; - - case MEDIA_SIZE_120MB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_120M); - break; - - case MEDIA_SIZE_2800KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_2880K); - break; - - case MEDIA_SIZE_1722KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1722K); - break; - - case MEDIA_SIZE_1680KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1680K); - break; - - case MEDIA_SIZE_1440KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1440K); - break; - - case MEDIA_SIZE_820KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_820K); - break; - - case MEDIA_SIZE_720KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_720K); - break; - - // 5.25" formats - case MEDIA_SIZE_1200KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_1200K); - break; - - case MEDIA_SIZE_640KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_640K); - break; - - case MEDIA_SIZE_360KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_360K); - break; - - case MEDIA_SIZE_320KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_320K); - break; - - case MEDIA_SIZE_180KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_180K); - break; - - case MEDIA_SIZE_160KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - SET_MEDIA_TYPE(CreateData->DiskGeometry, MEDIA_TYPE_160K); - break; - } + ExFreePoolWithTag(record, POOL_TAG); + } -#endif // INCLUDE_VFD_ORIGIN + for (device_object = DriverObject->DeviceObject; + device_object != NULL; ) + { + PDEVICE_OBJECT next_device = device_object->NextDevice; + PDEVICE_EXTENSION device_extension = (PDEVICE_EXTENSION) + device_object->DeviceExtension; - KdPrint - (("ImDisk: Values after BPB geometry detection:\n" - "DeviceNumber = %#x\n" - "DiskGeometry\n" - " .Cylinders = 0x%.8x%.8x\n" - " .MediaType = %i\n" - " .T/C = %u\n" - " .S/T = %u\n" - " .B/S = %u\n" - "Offset = 0x%.8x%.8x\n" - "Flags = %#x\n" - "FileNameLength = %u\n" - "FileName = '%.*ws'\n" - "DriveLetter = %wc\n", - CreateData->DeviceNumber, - CreateData->DiskGeometry.Cylinders.HighPart, - CreateData->DiskGeometry.Cylinders.LowPart, - CreateData->DiskGeometry.MediaType, - CreateData->DiskGeometry.TracksPerCylinder, - CreateData->DiskGeometry.SectorsPerTrack, - CreateData->DiskGeometry.BytesPerSector, - CreateData->ImageOffset.HighPart, - CreateData->ImageOffset.LowPart, - CreateData->Flags, - CreateData->FileNameLength, - (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), - CreateData->FileName, - CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); -} + KdPrint(("ImDisk: Now deleting device %i.\n", + device_extension->device_number)); -NTSTATUS -ImDiskGetDiskSize(IN HANDLE FileHandle, - IN OUT PIO_STATUS_BLOCK IoStatus, - IN OUT PLARGE_INTEGER DiskSize) -{ - NTSTATUS status; + if (device_object == ImDiskCtlDevice) + { + UNICODE_STRING sym_link; + LARGE_INTEGER time_out; + time_out.QuadPart = -1000000; - PAGED_CODE(); +#pragma warning(suppress: 28175) + while (device_object->ReferenceCount != 0) + { + KdPrint(("ImDisk: Ctl device is busy. Waiting.\n")); - { - FILE_STANDARD_INFORMATION file_standard; + KeDelayExecutionThread(KernelMode, FALSE, &time_out); - status = ZwQueryInformationFile(FileHandle, - IoStatus, - &file_standard, - sizeof(FILE_STANDARD_INFORMATION), - FileStandardInformation); + time_out.LowPart <<= 2; + } - if (NT_SUCCESS(status)) - { - *DiskSize = file_standard.EndOfFile; - return status; + KdPrint(("ImDisk: Deleting ctl device.\n")); + RtlInitUnicodeString(&sym_link, IMDISK_CTL_SYMLINK_NAME); + IoDeleteSymbolicLink(&sym_link); + IoDeleteDevice(device_object); } + else + { + PKTHREAD device_thread; - KdPrint(("ImDisk: FileStandardInformation not supported for " - "target device. %#x\n", status)); - } + KdPrint(("ImDisk: Shutting down device %i.\n", + device_extension->device_number)); - // Retry with IOCTL_DISK_GET_LENGTH_INFO instead - { - GET_LENGTH_INFORMATION part_info = { 0 }; - - status = - ZwDeviceIoControlFile(FileHandle, - NULL, - NULL, - NULL, - IoStatus, - IOCTL_DISK_GET_LENGTH_INFO, - NULL, - 0, - &part_info, - sizeof(part_info)); - - if (status == STATUS_PENDING) - { - ZwWaitForSingleObject(FileHandle, FALSE, NULL); - status = IoStatus->Status; - } + device_thread = device_extension->device_thread; + ObReferenceObjectByPointer(device_thread, SYNCHRONIZE, NULL, + KernelMode); - if (NT_SUCCESS(status)) - { - *DiskSize = part_info.Length; - return status; - } + ImDiskRemoveVirtualDisk(device_object); - KdPrint(("ImDisk: IOCTL_DISK_GET_LENGTH_INFO not supported " - "for target device. %#x\n", status)); - } + KdPrint(("ImDisk: Waiting for device thread %i to terminate.\n", + device_extension->device_number)); - // Retry with IOCTL_DISK_GET_PARTITION_INFO instead - { - PARTITION_INFORMATION part_info = { 0 }; - - status = - ZwDeviceIoControlFile(FileHandle, - NULL, - NULL, - NULL, - IoStatus, - IOCTL_DISK_GET_PARTITION_INFO, - NULL, - 0, - &part_info, - sizeof(part_info)); - - if (status == STATUS_PENDING) - { - ZwWaitForSingleObject(FileHandle, FALSE, NULL); - status = IoStatus->Status; - } + KeWaitForSingleObject(device_thread, + Executive, + KernelMode, + FALSE, + NULL); - if (NT_SUCCESS(status)) - { - *DiskSize = part_info.PartitionLength; - return status; + ObDereferenceObject(device_thread); } - KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO not supported " - "for target device. %#x\n", status)); + device_object = next_device; } - return status; + KdPrint + (("ImDisk: No more devices to delete. Leaving ImDiskUnload.\n")); } NTSTATUS -ImDiskCreateDevice(IN PDRIVER_OBJECT DriverObject, - IN OUT PIMDISK_CREATE_DATA CreateData, - IN PETHREAD ClientThread, - OUT PDEVICE_OBJECT *DeviceObject) +ImDiskRemoveDriveLetter(IN WCHAR DriveLetter) { - UNICODE_STRING file_name; - PWCHAR device_name_buffer = NULL; - UNICODE_STRING device_name; NTSTATUS status; - PDEVICE_EXTENSION device_extension; - BOOLEAN proxy_supports_unmap = FALSE; - BOOLEAN proxy_supports_zero = FALSE; - DEVICE_TYPE device_type; - ULONG device_characteristics; - HANDLE file_handle = NULL; - PFILE_OBJECT file_object = NULL; - PDEVICE_OBJECT dev_object = NULL; - BOOLEAN parallel_io = FALSE; - PUCHAR image_buffer = NULL; - PROXY_CONNECTION proxy = { (PROXY_CONNECTION::PROXY_CONNECTION_TYPE)0 }; - ULONG alignment_requirement; - CCHAR extra_stack_locations = 0; + WCHAR sym_link_global_wchar[] = L"\\DosDevices\\Global\\ :"; + WCHAR sym_link_wchar[] = L"\\DosDevices\\ :"; + UNICODE_STRING sym_link; PAGED_CODE(); - ASSERT(CreateData != NULL); - - KdPrint - (("ImDisk: Got request to create a virtual disk. Request data:\n" - "DeviceNumber = %#x\n" - "DiskGeometry\n" - " .Cylinders = 0x%.8x%.8x\n" - " .MediaType = %i\n" - " .T/C = %u\n" - " .S/T = %u\n" - " .B/S = %u\n" - "Offset = 0x%.8x%.8x\n" - "Flags = %#x\n" - "FileNameLength = %u\n" - "FileName = '%.*ws'\n" - "DriveLetter = %wc\n", - CreateData->DeviceNumber, - CreateData->DiskGeometry.Cylinders.HighPart, - CreateData->DiskGeometry.Cylinders.LowPart, - CreateData->DiskGeometry.MediaType, - CreateData->DiskGeometry.TracksPerCylinder, - CreateData->DiskGeometry.SectorsPerTrack, - CreateData->DiskGeometry.BytesPerSector, - CreateData->ImageOffset.HighPart, - CreateData->ImageOffset.LowPart, - CreateData->Flags, - CreateData->FileNameLength, - (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), - CreateData->FileName, - CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); - - // Auto-select type if not specified. - if (IMDISK_TYPE(CreateData->Flags) == 0) - if (CreateData->FileNameLength == 0) - CreateData->Flags |= IMDISK_TYPE_VM; - else - CreateData->Flags |= IMDISK_TYPE_FILE; - - // Blank filenames only supported for memory disks where size is specified. - if ((CreateData->FileNameLength == 0) && - !(((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) & - (CreateData->DiskGeometry.Cylinders.QuadPart > 65536)) | - ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC) & - (CreateData->DiskGeometry.Cylinders.QuadPart > 65536)))) - { - KdPrint(("ImDisk: Blank filenames only supported for memory disks where " - "size is specified..\n")); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - STATUS_INVALID_PARAMETER, - 102, - STATUS_INVALID_PARAMETER, - 0, - 0, - NULL, - L"Blank filenames only supported for non-zero length " - L"vm type disks.")); - - return STATUS_INVALID_PARAMETER; - } - -#ifndef _WIN64 - // Cannot create >= 2 GB VM disk in 32 bit version. - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) & - ((CreateData->DiskGeometry.Cylinders.QuadPart & 0xFFFFFFFF80000000) != - 0)) - { - KdPrint(("ImDisk: Cannot create >= 2GB vm disks on 32-bit system.\n")); - - return STATUS_INVALID_PARAMETER; - } -#endif - - // Auto-find first free device number - if (CreateData->DeviceNumber == IMDISK_AUTO_DEVICE_NUMBER) - { - ImDiskFindFreeDeviceNumber(DriverObject, &CreateData->DeviceNumber); - - KdPrint(("ImDisk: Free device number %i.\n", CreateData->DeviceNumber)); - } - - /* for (CreateData->DeviceNumber = 0; */ - /* CreateData->DeviceNumber < MaxDevices; */ - /* CreateData->DeviceNumber++) */ - /* if ((~DeviceList) & (1ULL << CreateData->DeviceNumber)) */ - /* break; */ - - if (CreateData->DeviceNumber >= MaxDevices) - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - STATUS_INVALID_PARAMETER, - 102, - STATUS_INVALID_PARAMETER, - 0, - 0, - NULL, - L"Device number too high.")); - - return STATUS_INVALID_PARAMETER; - } - - if (IMDISK_BYTE_SWAP(CreateData->Flags) && - (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_PARALLEL_IO)) - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - STATUS_INVALID_PARAMETER, - 102, - STATUS_INVALID_PARAMETER, - 0, - 0, - NULL, - L"Byte swapping not supported together with parallel I/O.")); - - return STATUS_INVALID_PARAMETER; - } - - file_name.Length = CreateData->FileNameLength; - file_name.MaximumLength = CreateData->FileNameLength; - file_name.Buffer = NULL; - - // If a file is to be opened or created, allocate name buffer and open that - // file... - if ((CreateData->FileNameLength > 0) | - ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC))) - { - IO_STATUS_BLOCK io_status; - OBJECT_ATTRIBUTES object_attributes; - UNICODE_STRING real_file_name; - ACCESS_MASK desired_access = 0; - ULONG share_access = 0; - ULONG create_options = 0; - - if (file_name.MaximumLength > 0) - { - file_name.Buffer = (PWCHAR) - ExAllocatePoolWithTag(NonPagedPool, - file_name.MaximumLength, - POOL_TAG); - - if (file_name.Buffer == NULL) - { - KdPrint(("ImDisk: Error allocating buffer for filename.\n")); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - STATUS_INSUFFICIENT_RESOURCES, - 102, - STATUS_INSUFFICIENT_RESOURCES, - 0, - 0, - NULL, - L"Memory allocation error.")); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlCopyMemory(file_name.Buffer, CreateData->FileName, - CreateData->FileNameLength); - } - - // If no device-type specified, check if filename ends with .iso, .nrg or - // .bin. In that case, set device-type automatically to FILE_DEVICE_CDROM - if ((IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) & - (CreateData->FileNameLength >= (4 * sizeof(*CreateData->FileName)))) - { - LPWSTR name = CreateData->FileName + - (CreateData->FileNameLength / sizeof(*CreateData->FileName)) - 4; - if ((_wcsnicmp(name, L".iso", 4) == 0) | - (_wcsnicmp(name, L".nrg", 4) == 0) | - (_wcsnicmp(name, L".bin", 4) == 0)) - CreateData->Flags |= IMDISK_DEVICE_TYPE_CD | IMDISK_OPTION_RO; - } - - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) - CreateData->Flags |= IMDISK_OPTION_RO; - - KdPrint(("ImDisk: Done with device type auto-selection by file ext.\n")); - - if (ClientThread != NULL) - { - SECURITY_QUALITY_OF_SERVICE security_quality_of_service; - SECURITY_CLIENT_CONTEXT security_client_context; - - RtlZeroMemory(&security_quality_of_service, - sizeof(SECURITY_QUALITY_OF_SERVICE)); - - security_quality_of_service.Length = - sizeof(SECURITY_QUALITY_OF_SERVICE); - security_quality_of_service.ImpersonationLevel = - SecurityImpersonation; - security_quality_of_service.ContextTrackingMode = - SECURITY_STATIC_TRACKING; - security_quality_of_service.EffectiveOnly = FALSE; - - SeCreateClientSecurity(ClientThread, - &security_quality_of_service, - FALSE, - &security_client_context); - - SeImpersonateClient(&security_client_context, NULL); - - SeDeleteClientSecurity(&security_client_context); - } - else - KdPrint(("ImDisk: No impersonation information.\n")); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC)) - { - real_file_name.MaximumLength = file_name.Length + - sizeof(AWEALLOC_DEVICE_NAME); - - real_file_name.Buffer = (PWCHAR) - ExAllocatePoolWithTag(PagedPool, - real_file_name.MaximumLength, - POOL_TAG); - - if (real_file_name.Buffer == NULL) - { - KdPrint(("ImDisk: Out of memory while allocating %#x bytes\n", - real_file_name.MaximumLength)); - - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - return STATUS_INSUFFICIENT_RESOURCES; - } - - real_file_name.Length = 0; - - status = - RtlAppendUnicodeToString(&real_file_name, - AWEALLOC_DEVICE_NAME); - - if (NT_SUCCESS(status)) - status = - RtlAppendUnicodeStringToString(&real_file_name, - &file_name); - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Internal error: " - "RtlAppendUnicodeStringToString failed with " - "pre-allocated buffers.\n")); - - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - ExFreePoolWithTag(real_file_name.Buffer, POOL_TAG); - return STATUS_DRIVER_INTERNAL_ERROR; - } - - InitializeObjectAttributes(&object_attributes, - &real_file_name, - OBJ_CASE_INSENSITIVE | - OBJ_FORCE_ACCESS_CHECK, - NULL, - NULL); - } - else if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) & - ((IMDISK_PROXY_TYPE(CreateData->Flags) == - IMDISK_PROXY_TYPE_TCP) | - (IMDISK_PROXY_TYPE(CreateData->Flags) == - IMDISK_PROXY_TYPE_COMM))) - { - RtlInitUnicodeString(&real_file_name, IMDPROXY_SVC_PIPE_NATIVE_NAME); - - InitializeObjectAttributes(&object_attributes, - &real_file_name, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - } - else - { - real_file_name = file_name; - - InitializeObjectAttributes(&object_attributes, - &real_file_name, - OBJ_CASE_INSENSITIVE | - OBJ_FORCE_ACCESS_CHECK, - NULL, - NULL); - } - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) && - (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM)) - { - proxy.connection_type = PROXY_CONNECTION::PROXY_CONNECTION_SHM; - - status = - ZwOpenSection(&file_handle, - GENERIC_READ | GENERIC_WRITE, - &object_attributes); - } - else - { - desired_access = GENERIC_READ; - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) || - ((IMDISK_TYPE(CreateData->Flags) != IMDISK_TYPE_VM) && - !IMDISK_READONLY(CreateData->Flags))) - { - desired_access |= GENERIC_WRITE; - } - - share_access = FILE_SHARE_READ | FILE_SHARE_DELETE; - - if (IMDISK_READONLY(CreateData->Flags) || - (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) || - IMDISK_SHARED_IMAGE(CreateData->Flags)) - { - share_access |= FILE_SHARE_WRITE; - } - - create_options = FILE_NON_DIRECTORY_FILE | - FILE_NO_INTERMEDIATE_BUFFERING | - FILE_SYNCHRONOUS_IO_NONALERT; - - if (IMDISK_SPARSE_FILE(CreateData->Flags)) - { - create_options |= FILE_OPEN_FOR_BACKUP_INTENT; - } - - if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) - { - create_options |= FILE_SEQUENTIAL_ONLY; - } - else - { - create_options |= FILE_RANDOM_ACCESS; - } - - KdPrint(("ImDisk: Passing DesiredAccess=%#x ShareAccess=%#x CreateOptions=%#x\n", - desired_access, share_access, create_options)); - - status = ZwCreateFile( - &file_handle, - desired_access, - &object_attributes, - &io_status, - NULL, - FILE_ATTRIBUTE_NORMAL, - share_access, - FILE_OPEN, - create_options, - NULL, - 0); - } - - // For 32 bit driver running on Windows 2000 and earlier, the above - // call will fail because OBJ_FORCE_ACCESS_CHECK is not supported. If so, - // STATUS_INVALID_PARAMETER is returned and we go on without any access - // checks in that case. -#ifndef _WIN64 - if (status == STATUS_INVALID_PARAMETER) - { - InitializeObjectAttributes(&object_attributes, - &real_file_name, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) && - (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM)) - { - proxy.connection_type = PROXY_CONNECTION::PROXY_CONNECTION_SHM; - - status = - ZwOpenSection(&file_handle, - GENERIC_READ | GENERIC_WRITE, - &object_attributes); - } - else - { - status = ZwCreateFile( - &file_handle, - desired_access, - &object_attributes, - &io_status, - NULL, - FILE_ATTRIBUTE_NORMAL, - share_access, - FILE_OPEN, - create_options, - NULL, - 0); - } - } -#endif - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Error opening file '%wZ'. " - "Status: %#x " - "SpecSize: %i " - "WritableFile: %i " - "DevTypeFile: %i " - "Flags: %#x\n" - "FileNameLength: %#x\n", - &real_file_name, - status, - CreateData->DiskGeometry.Cylinders.QuadPart != 0, - !IMDISK_READONLY(CreateData->Flags), - IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE, - CreateData->Flags, - (int)real_file_name.Length)); - } - - // If not found we will create the file if a new non-zero size is - // specified, read-only virtual disk is not specified and we are - // creating a type 'file' virtual disk. - if (((status == STATUS_OBJECT_NAME_NOT_FOUND) | - (status == STATUS_NO_SUCH_FILE)) & - (CreateData->DiskGeometry.Cylinders.QuadPart != 0) & - (!IMDISK_READONLY(CreateData->Flags)) & - (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE)) - { - KdPrint(("ImDisk: Creating new image file ShareAccess=%#x CreateOptions=%#x\n", - desired_access, share_access, create_options)); - - status = - ZwCreateFile(&file_handle, - GENERIC_READ | - GENERIC_WRITE, - &object_attributes, - &io_status, - NULL, - FILE_ATTRIBUTE_NORMAL, - share_access, - FILE_OPEN_IF, - create_options, NULL, 0); - - if (!NT_SUCCESS(status)) - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Cannot create image file.")); - - KdPrint(("ImDisk: Cannot create '%.*ws'. (%#x)\n", - (int)(CreateData->FileNameLength / - sizeof(*CreateData->FileName)), - CreateData->FileName, - status)); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == - IMDISK_FILE_TYPE_AWEALLOC)) - ExFreePoolWithTag(real_file_name.Buffer, POOL_TAG); - - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - return status; - } - } - else if (!NT_SUCCESS(status)) - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Cannot open image file.")); - - KdPrint(("ImDisk: Cannot open file '%wZ'. Status: %#x\n", - &real_file_name, - status)); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == - IMDISK_FILE_TYPE_AWEALLOC)) - ExFreePoolWithTag(real_file_name.Buffer, POOL_TAG); - - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - return status; - } - - KdPrint(("ImDisk: File '%wZ' opened successfully.\n", - &real_file_name)); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC)) - ExFreePoolWithTag(real_file_name.Buffer, POOL_TAG); - - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (!IMDISK_READONLY(CreateData->Flags))) - { - // If creating a sparse image file - if (IMDISK_SPARSE_FILE(CreateData->Flags)) - { - status = ZwFsControlFile(file_handle, - NULL, - NULL, - NULL, - &io_status, - FSCTL_SET_SPARSE, - NULL, - 0, - NULL, - 0); - - if (NT_SUCCESS(status)) - KdPrint(("ImDisk: Sparse attribute set on image file.\n")); - else - { - DbgPrint("ImDisk: Cannot set sparse attribute " - "on image file: %X\n", status); - - CreateData->Flags &= ~IMDISK_OPTION_SPARSE_FILE; - } - } - - // Adjust the file length to the requested virtual disk size. - if ((CreateData->DiskGeometry.Cylinders.QuadPart != 0) & - (CreateData->ImageOffset.QuadPart == 0)) - { - status = ZwSetInformationFile( - file_handle, - &io_status, - &CreateData->DiskGeometry.Cylinders, - sizeof - (FILE_END_OF_FILE_INFORMATION), - FileEndOfFileInformation); - - if (NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Image file size adjusted.\n")); - } - else - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error setting file size.")); - - KdPrint(("ImDisk: Error setting eof (%#x).\n", status)); - return status; - } - } - } - - if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) - { - if (IMDISK_PROXY_TYPE(CreateData->Flags) == IMDISK_PROXY_TYPE_SHM) - { - status = ZwMapViewOfSection( - file_handle, - NtCurrentProcess(), - (PVOID*)&proxy.shared_memory, - 0, - 0, - NULL, - &proxy.shared_memory_size, - ViewUnmap, - 0, - PAGE_READWRITE); - } - else - { - status = ObReferenceObjectByHandle( - file_handle, - FILE_READ_ATTRIBUTES | - FILE_READ_DATA | - FILE_WRITE_DATA, - *IoFileObjectType, - KernelMode, - (PVOID*)&proxy.device, - NULL); - } - - if (!NT_SUCCESS(status)) - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error referencing proxy device.")); - - KdPrint(("ImDisk: Error referencing proxy device (%#x).\n", - status)); - - return status; - } - - KdPrint(("ImDisk: Got reference to proxy object %p.\n", - proxy.connection_type == PROXY_CONNECTION:: - PROXY_CONNECTION_TYPE::PROXY_CONNECTION_DEVICE ? - (PVOID)proxy.device : - (PVOID)proxy.shared_memory)); - - if (IMDISK_PROXY_TYPE(CreateData->Flags) != IMDISK_PROXY_TYPE_DIRECT) - { - status = ImDiskConnectProxy(&proxy, - &io_status, - NULL, - CreateData->Flags, - CreateData->FileName, - CreateData->FileNameLength); - } - - if (!NT_SUCCESS(status)) - { - ImDiskCloseProxy(&proxy); - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error connecting proxy.")); - - KdPrint(("ImDisk: Error connecting proxy (%#x).\n", status)); - - return status; - } - } - - // Get the file size of the disk file. - if (IMDISK_TYPE(CreateData->Flags) != IMDISK_TYPE_PROXY) - { - LARGE_INTEGER disk_size; - - status = - ImDiskGetDiskSize(file_handle, - &io_status, - &disk_size); - - if (!NT_SUCCESS(status)) - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error getting image size.")); - - KdPrint - (("ImDisk: Error getting image size (%#x).\n", - status)); - - return status; - } - - // Allocate virtual memory for 'vm' type. - if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) - { - SIZE_T max_size; - - // If no size given for VM disk, use size of pre-load image file. - // This code is somewhat easier for 64 bit architectures. - -#ifdef _WIN64 - if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) - CreateData->DiskGeometry.Cylinders.QuadPart = - disk_size.QuadPart - - CreateData->ImageOffset.QuadPart; - - max_size = CreateData->DiskGeometry.Cylinders.QuadPart; -#else - if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) - // Check that file size < 2 GB. - if ((disk_size.QuadPart - - CreateData->ImageOffset.QuadPart) & 0xFFFFFFFF80000000) - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - KdPrint(("ImDisk: VM disk >= 2GB not supported.\n")); - - return STATUS_INSUFFICIENT_RESOURCES; - } - else - CreateData->DiskGeometry.Cylinders.QuadPart = - disk_size.QuadPart - - CreateData->ImageOffset.QuadPart; - - max_size = CreateData->DiskGeometry.Cylinders.LowPart; -#endif - - status = - ZwAllocateVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - 0, - &max_size, - MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(status)) - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Not enough memory for VM disk.")); - - KdPrint(("ImDisk: Error allocating vm for image. (%#x)\n", - status)); - - return STATUS_NO_MEMORY; - } - - alignment_requirement = FILE_BYTE_ALIGNMENT; - - // Loading of image file has been moved to be done just before - // the service loop. - } - else - { - FILE_ALIGNMENT_INFORMATION file_alignment; - - status = ZwQueryInformationFile(file_handle, - &io_status, - &file_alignment, - sizeof - (FILE_ALIGNMENT_INFORMATION), - FileAlignmentInformation); - - if (!NT_SUCCESS(status)) - { - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error getting alignment information.")); - - KdPrint(("ImDisk: Error querying file alignment (%#x).\n", - status)); - - return status; - } - - if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) - CreateData->DiskGeometry.Cylinders.QuadPart = - disk_size.QuadPart - - CreateData->ImageOffset.QuadPart; - - alignment_requirement = file_alignment.AlignmentRequirement; - } - - if ((CreateData->DiskGeometry.TracksPerCylinder == 0) | - (CreateData->DiskGeometry.SectorsPerTrack == 0) | - (CreateData->DiskGeometry.BytesPerSector == 0)) - { - SIZE_T free_size = 0; - PFAT_VBR fat_vbr = (PFAT_VBR) - ExAllocatePoolWithTag(PagedPool, - sizeof(FAT_VBR), - POOL_TAG); - - if (fat_vbr == NULL) - { - if (file_handle != NULL) - ZwClose(file_handle); - if (file_name.Buffer != NULL) - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - if (image_buffer != NULL) - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - &free_size, MEM_RELEASE); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Insufficient memory.")); - - KdPrint(("ImDisk: Error allocating memory.\n")); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - status = - ZwReadFile(file_handle, - NULL, - NULL, - NULL, - &io_status, - fat_vbr, - sizeof(FAT_VBR), - &CreateData->ImageOffset, - NULL); - - if (NT_SUCCESS(status)) - { - ImDiskReadFormattedGeometry(CreateData, &fat_vbr->BPB); - } - else - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error reading first sector.")); - - KdPrint(("ImDisk: Error reading first sector (%#x).\n", - status)); - } - - ExFreePoolWithTag(fat_vbr, POOL_TAG); - } - } - else - // If proxy is used, get the image file size from the proxy instead. - { - IMDPROXY_INFO_RESP proxy_info; - - status = ImDiskQueryInformationProxy(&proxy, - &io_status, - NULL, - &proxy_info, - sizeof(IMDPROXY_INFO_RESP)); - - if (!NT_SUCCESS(status)) - { - ImDiskCloseProxy(&proxy); - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error querying proxy.")); - - KdPrint(("ImDisk: Error querying proxy (%#x).\n", status)); - - return status; - } - - if (CreateData->DiskGeometry.Cylinders.QuadPart == 0) - CreateData->DiskGeometry.Cylinders.QuadPart = proxy_info.file_size; - - if ((CreateData->DiskGeometry.TracksPerCylinder == 0) | - (CreateData->DiskGeometry.SectorsPerTrack == 0) | - (CreateData->DiskGeometry.BytesPerSector == 0)) - { - PFAT_VBR fat_vbr = (PFAT_VBR) - ExAllocatePoolWithTag(PagedPool, - sizeof(FAT_VBR), - POOL_TAG); - - if (fat_vbr == NULL) - { - ImDiskCloseProxy(&proxy); - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Insufficient memory.")); - - KdPrint(("ImDisk: Error allocating memory.\n")); - - return STATUS_INSUFFICIENT_RESOURCES; - } - - status = ImDiskReadProxy(&proxy, - &io_status, - NULL, - fat_vbr, - sizeof(FAT_VBR), - &CreateData->ImageOffset); - - if (!NT_SUCCESS(status)) - { - ImDiskCloseProxy(&proxy); - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - ExFreePoolWithTag(fat_vbr, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error reading first sector.")); - - KdPrint(("ImDisk: Error reading first sector (%#x).\n", - status)); - - return status; - } - - ImDiskReadFormattedGeometry(CreateData, &fat_vbr->BPB); - - ExFreePoolWithTag(fat_vbr, POOL_TAG); - } - - if ((proxy_info.req_alignment - 1 > FILE_512_BYTE_ALIGNMENT) | - (CreateData->DiskGeometry.Cylinders.QuadPart == 0)) - { - ImDiskCloseProxy(&proxy); - ZwClose(file_handle); - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Unsupported sizes.")); - -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk: Unsupported sizes. " - "Got 0x%.8x%.8x size and 0x%.8x%.8x alignment.\n", - proxy_info.file_size, - proxy_info.req_alignment)); - - return STATUS_INVALID_PARAMETER; - } - - alignment_requirement = (ULONG)proxy_info.req_alignment - 1; - - if (proxy_info.flags & IMDPROXY_FLAG_RO) - CreateData->Flags |= IMDISK_OPTION_RO; - - if (proxy_info.flags & IMDPROXY_FLAG_SUPPORTS_UNMAP) - proxy_supports_unmap = TRUE; - - if (proxy_info.flags & IMDPROXY_FLAG_SUPPORTS_ZERO) - proxy_supports_zero = TRUE; - - KdPrint(("ImDisk: Got from proxy: Siz=0x%.8x%.8x Flg=%#x Alg=%#x.\n", - CreateData->DiskGeometry.Cylinders.HighPart, - CreateData->DiskGeometry.Cylinders.LowPart, - (ULONG)proxy_info.flags, - (ULONG)proxy_info.req_alignment)); - } - - if (CreateData->DiskGeometry.Cylinders.QuadPart <= 65536) - { - SIZE_T free_size = 0; - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Number of cylinders equals zero.")); - - KdPrint(("ImDisk: Fatal error: Number of cylinders equals zero.\n")); - - ImDiskCloseProxy(&proxy); - if (file_handle != NULL) - ZwClose(file_handle); - if (file_name.Buffer != NULL) - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - if (image_buffer != NULL) - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - &free_size, MEM_RELEASE); - - return STATUS_INVALID_PARAMETER; - } - } - // Blank vm-disk, just allocate... - else - { - SIZE_T max_size; -#ifdef _WIN64 - max_size = CreateData->DiskGeometry.Cylinders.QuadPart; -#else - max_size = CreateData->DiskGeometry.Cylinders.LowPart; -#endif - - image_buffer = NULL; - status = - ZwAllocateVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - 0, - &max_size, - MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(status)) - { - KdPrint - (("ImDisk: Error allocating virtual memory for vm disk (%#x).\n", - status)); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Not enough free memory for VM disk.")); - - return STATUS_NO_MEMORY; - } - - alignment_requirement = FILE_BYTE_ALIGNMENT; - } - - KdPrint(("ImDisk: Done with file/memory checks.\n")); - -#ifdef INCLUDE_VFD_ORIGIN - - // If no device-type specified and size matches common floppy sizes, - // auto-select FILE_DEVICE_DISK with FILE_FLOPPY_DISKETTE and - // FILE_REMOVABLE_MEDIA. - // If still no device-type specified, specify FILE_DEVICE_DISK with no - // particular characteristics. This will emulate a hard disk partition. - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) - switch (CreateData->DiskGeometry.Cylinders.QuadPart) - { - case MEDIA_SIZE_240MB: - case MEDIA_SIZE_120MB: - case MEDIA_SIZE_2800KB: - case MEDIA_SIZE_1722KB: - case MEDIA_SIZE_1680KB: - case MEDIA_SIZE_1440KB: - case MEDIA_SIZE_820KB: - case MEDIA_SIZE_720KB: - case MEDIA_SIZE_1200KB: - case MEDIA_SIZE_640KB: - case MEDIA_SIZE_360KB: - case MEDIA_SIZE_320KB: - case MEDIA_SIZE_180KB: - case MEDIA_SIZE_160KB: - CreateData->Flags |= IMDISK_DEVICE_TYPE_FD; - break; - - default: - CreateData->Flags |= IMDISK_DEVICE_TYPE_HD; - } - - KdPrint(("ImDisk: Done with device type selection for floppy sizes.\n")); - -#else // INCLUDE_VFD_ORIGIN - - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == 0) - CreateData->Flags |= IMDISK_DEVICE_TYPE_HD; - -#endif // INCLUDE_VFD_ORIGIN - - // If some parts of the DISK_GEOMETRY structure are zero, auto-fill with - // typical values for this type of disk. - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) - { - LONGLONG calccyl = CreateData->DiskGeometry.Cylinders.QuadPart; - - if (CreateData->DiskGeometry.BytesPerSector == 0) - CreateData->DiskGeometry.BytesPerSector = SECTOR_SIZE_CD_ROM; - - calccyl /= CreateData->DiskGeometry.BytesPerSector; - - if (CreateData->DiskGeometry.SectorsPerTrack == 0) - { - if (calccyl / SECTORS_PER_TRACK_CD_ROM * SECTORS_PER_TRACK_CD_ROM == - calccyl) - CreateData->DiskGeometry.SectorsPerTrack = - SECTORS_PER_TRACK_CD_ROM; - else - CreateData->DiskGeometry.SectorsPerTrack = 1; - } - - calccyl /= CreateData->DiskGeometry.SectorsPerTrack; - - if (CreateData->DiskGeometry.TracksPerCylinder == 0) - { - if (calccyl / - TRACKS_PER_CYLINDER_CD_ROM * TRACKS_PER_CYLINDER_CD_ROM == - calccyl) - CreateData->DiskGeometry.TracksPerCylinder = - TRACKS_PER_CYLINDER_CD_ROM; - else - CreateData->DiskGeometry.TracksPerCylinder = 1; - } - - if (CreateData->DiskGeometry.MediaType == Unknown) - CreateData->DiskGeometry.MediaType = RemovableMedia; - } - // Common floppy sizes geometries. - else - { - LONGLONG calccyl = CreateData->DiskGeometry.Cylinders.QuadPart; - -#ifdef INCLUDE_VFD_ORIGIN - - if ((IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) & - (CreateData->DiskGeometry.BytesPerSector == 0) & - (CreateData->DiskGeometry.SectorsPerTrack == 0) & - (CreateData->DiskGeometry.TracksPerCylinder == 0) & - (CreateData->DiskGeometry.MediaType == Unknown)) - switch (calccyl) - { - // 3.5" formats - case MEDIA_SIZE_240MB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_240M]; - break; - - case MEDIA_SIZE_120MB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_120M]; - break; - - case MEDIA_SIZE_2800KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_2880K]; - break; - - case MEDIA_SIZE_1722KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_1722K]; - break; - - case MEDIA_SIZE_1680KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_1680K]; - break; - - case MEDIA_SIZE_1440KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_1440K]; - break; - - case MEDIA_SIZE_820KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_820K]; - break; - - case MEDIA_SIZE_720KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_720K]; - break; - - // 5.25" formats - case MEDIA_SIZE_1200KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_1200K]; - break; - - case MEDIA_SIZE_640KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_640K]; - break; - - case MEDIA_SIZE_360KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_360K]; - break; - - case MEDIA_SIZE_320KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_320K]; - break; - - case MEDIA_SIZE_180KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_180K]; - break; - - case MEDIA_SIZE_160KB: - CreateData->DiskGeometry = media_table[MEDIA_TYPE_160K]; - break; - } - - // In this case the Cylinders member actually specifies the total size of - // the virtual disk so restore that in case overwritten by the pre- - // defined floppy geometries above. - CreateData->DiskGeometry.Cylinders.QuadPart = calccyl; - -#endif // INCLUDE_VFD_ORIGIN - - if (CreateData->DiskGeometry.BytesPerSector == 0) - CreateData->DiskGeometry.BytesPerSector = SECTOR_SIZE_HDD; - - calccyl /= CreateData->DiskGeometry.BytesPerSector; - - if (CreateData->DiskGeometry.SectorsPerTrack == 0) - CreateData->DiskGeometry.SectorsPerTrack = HEAD_SIZE_HDD; - - calccyl /= CreateData->DiskGeometry.SectorsPerTrack; - - // Former auto-selection of HDD head size - /* - if (CreateData->DiskGeometry.SectorsPerTrack == 0) - { - CreateData->DiskGeometry.SectorsPerTrack = 1; - - if ((calccyl / 7 * 7 == calccyl) & - (CreateData->DiskGeometry.SectorsPerTrack * 7 < 64)) - { - CreateData->DiskGeometry.SectorsPerTrack *= 7; - calccyl /= 7; - } - - if ((calccyl / 3 * 3 == calccyl) & - (CreateData->DiskGeometry.SectorsPerTrack * 3 < 64)) - { - CreateData->DiskGeometry.SectorsPerTrack *= 3; - calccyl /= 3; - } - - if ((calccyl / 3 * 3 == calccyl) & - (CreateData->DiskGeometry.SectorsPerTrack * 3 < 64)) - { - CreateData->DiskGeometry.SectorsPerTrack *= 3; - calccyl /= 3; - } - - while (((calccyl & 1) == 0) & - (CreateData->DiskGeometry.SectorsPerTrack <= 16)) - { - CreateData->DiskGeometry.SectorsPerTrack <<= 1; - calccyl >>= 1; - } - } - else - calccyl /= CreateData->DiskGeometry.SectorsPerTrack; - */ - - if (CreateData->DiskGeometry.TracksPerCylinder == 0) - { - CreateData->DiskGeometry.TracksPerCylinder = 1; - - if (calccyl >= 130560) - { - CreateData->DiskGeometry.TracksPerCylinder = 255; - calccyl /= 255; - } - else - while ((calccyl > 128) & - (CreateData->DiskGeometry.TracksPerCylinder < 128)) - { - CreateData->DiskGeometry.TracksPerCylinder <<= 1; - calccyl >>= 1; - } - - /* - if (calccyl % 17 == 0) - { - CreateData->DiskGeometry.TracksPerCylinder *= 17; - calccyl /= 17; - } - - if (calccyl % 5 == 0) - { - CreateData->DiskGeometry.TracksPerCylinder *= 5; - calccyl /= 5; - } - - if (calccyl % 3 == 0) - { - CreateData->DiskGeometry.TracksPerCylinder *= 3; - calccyl /= 3; - } - - while (((calccyl & 1) == 0) & - (CreateData->DiskGeometry.TracksPerCylinder <= 64)) - { - CreateData->DiskGeometry.TracksPerCylinder <<= 1; - calccyl >>= 1; - } - */ - } - - if (CreateData->DiskGeometry.MediaType == Unknown) - CreateData->DiskGeometry.MediaType = FixedMedia; - } - - KdPrint(("ImDisk: Done with disk geometry setup.\n")); - - // Ensure upper-case drive letter. - CreateData->DriveLetter &= ~0x20; - - // Now build real DeviceType and DeviceCharacteristics parameters. - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_CD) - { - device_type = FILE_DEVICE_CD_ROM; - device_characteristics = FILE_READ_ONLY_DEVICE | FILE_REMOVABLE_MEDIA; - } - else if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_RAW) - { - device_type = FILE_DEVICE_UNKNOWN; - device_characteristics = 0; - } - else - { - device_type = FILE_DEVICE_DISK; - - if (IMDISK_DEVICE_TYPE(CreateData->Flags) == IMDISK_DEVICE_TYPE_FD) - device_characteristics = FILE_FLOPPY_DISKETTE | FILE_REMOVABLE_MEDIA; - else - device_characteristics = 0; - } - - if (IMDISK_REMOVABLE(CreateData->Flags)) - device_characteristics |= FILE_REMOVABLE_MEDIA; - - if (IMDISK_READONLY(CreateData->Flags)) - device_characteristics |= FILE_READ_ONLY_DEVICE; - - KdPrint - (("ImDisk: After checks and translations we got this create data:\n" - "DeviceNumber = %#x\n" - "DiskGeometry\n" - " .Cylinders = 0x%.8x%.8x\n" - " .MediaType = %i\n" - " .T/C = %u\n" - " .S/T = %u\n" - " .B/S = %u\n" - "Offset = 0x%.8x%.8x\n" - "Flags = %#x\n" - "FileNameLength = %u\n" - "FileName = '%.*ws'\n" - "DriveLetter = %wc\n", - CreateData->DeviceNumber, - CreateData->DiskGeometry.Cylinders.HighPart, - CreateData->DiskGeometry.Cylinders.LowPart, - CreateData->DiskGeometry.MediaType, - CreateData->DiskGeometry.TracksPerCylinder, - CreateData->DiskGeometry.SectorsPerTrack, - CreateData->DiskGeometry.BytesPerSector, - CreateData->ImageOffset.HighPart, - CreateData->ImageOffset.LowPart, - CreateData->Flags, - CreateData->FileNameLength, - (int)(CreateData->FileNameLength / sizeof(*CreateData->FileName)), - CreateData->FileName, - CreateData->DriveLetter ? CreateData->DriveLetter : L' ')); - - status = STATUS_SUCCESS; - - // Get FILE_OBJECT if we will need that later - if ((file_handle != NULL) && - (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) && - ((IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC) || - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_PARALLEL_IO))) - { - ACCESS_MASK file_access_mask = - SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA; - - if (device_characteristics & FILE_READ_ONLY_DEVICE) - file_access_mask |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES; - - status = ObReferenceObjectByHandle(file_handle, - file_access_mask, - *IoFileObjectType, - KernelMode, (PVOID*)&file_object, NULL); - - if (!NT_SUCCESS(status)) - { - file_object = NULL; - - DbgPrint("ImDisk: Error referencing image file handle: %#x\n", - status); - } - else - { - parallel_io = TRUE; - - dev_object = IoGetRelatedDeviceObject(file_object); - - if ((dev_object->Flags & DO_DIRECT_IO) == DO_DIRECT_IO) - { - extra_stack_locations = dev_object->StackSize; - } - } - } - - if (NT_SUCCESS(status)) - { - // Buffer for device name - device_name_buffer = (PWCHAR) - ExAllocatePoolWithTag(PagedPool, - MAXIMUM_FILENAME_LENGTH * - sizeof(*device_name_buffer), - POOL_TAG); - - if (device_name_buffer == NULL) - { - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - STATUS_INSUFFICIENT_RESOURCES, - 102, - STATUS_INSUFFICIENT_RESOURCES, - 0, - 0, - NULL, - L"Memory allocation error.")); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (!NT_SUCCESS(status)) - { - SIZE_T free_size = 0; - ImDiskCloseProxy(&proxy); - if (file_handle != NULL) - ZwClose(file_handle); - if (file_object != NULL) - ObDereferenceObject(file_object); - if (file_name.Buffer != NULL) - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - if (image_buffer != NULL) - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - &free_size, MEM_RELEASE); - - return status; - } - - _snwprintf(device_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, - IMDISK_DEVICE_BASE_NAME L"%u", CreateData->DeviceNumber); - device_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; - - RtlInitUnicodeString(&device_name, device_name_buffer); - - KdPrint - (("ImDisk: Creating device '%ws'. Device type %#x, characteristics %#x.\n", - device_name_buffer, device_type, device_characteristics)); - - status = IoCreateDevice(DriverObject, - sizeof(DEVICE_EXTENSION), - &device_name, - device_type, - device_characteristics, - FALSE, - DeviceObject); - - if (NT_SUCCESS(status)) - { - PWCHAR symlink_name_buffer = (PWCHAR) - ExAllocatePoolWithTag(PagedPool, - MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR), POOL_TAG); - - if (symlink_name_buffer == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - UNICODE_STRING symlink_name; - - _snwprintf(symlink_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, - IMDISK_SYMLNK_NATIVE_BASE_NAME L"%u", CreateData->DeviceNumber); - symlink_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; - - RtlInitUnicodeString(&symlink_name, symlink_name_buffer); - - KdPrint(("ImDisk: Creating symlink '%ws'.\n", symlink_name_buffer)); - - status = IoCreateUnprotectedSymbolicLink(&symlink_name, &device_name); - - ExFreePoolWithTag(symlink_name_buffer, POOL_TAG); - } - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Cannot create symlink. (%#x)\n", status)); - IoDeleteDevice(*DeviceObject); - } - } - - if (!NT_SUCCESS(status)) - { - SIZE_T free_size = 0; - - ExFreePoolWithTag(device_name_buffer, POOL_TAG); - ImDiskCloseProxy(&proxy); - if (file_handle != NULL) - ZwClose(file_handle); - if (file_object != NULL) - ObDereferenceObject(file_object); - if (file_name.Buffer != NULL) - ExFreePoolWithTag(file_name.Buffer, POOL_TAG); - if (image_buffer != NULL) - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&image_buffer, - &free_size, MEM_RELEASE); - - ImDiskLogError((DriverObject, - 0, - 0, - NULL, - 0, - 1000, - status, - 102, - status, - 0, - 0, - NULL, - L"Error creating device object.")); - - KdPrint(("ImDisk: Cannot create device. (%#x)\n", status)); - - return status; - } - - KdPrint - (("ImDisk: Setting the AlignmentRequirement field to %#x.\n", - alignment_requirement)); - - (*DeviceObject)->Flags |= DO_DIRECT_IO; - - (*DeviceObject)->AlignmentRequirement = alignment_requirement; - -#pragma warning(suppress: 4244) - (*DeviceObject)->StackSize += extra_stack_locations; - - device_extension = (PDEVICE_EXTENSION)(*DeviceObject)->DeviceExtension; - - RtlZeroMemory(device_extension, sizeof(*device_extension)); - - // Auto-set our own read-only flag if the characteristics of the device - // object is set to read-only. - if ((*DeviceObject)->Characteristics & FILE_READ_ONLY_DEVICE) - device_extension->read_only = TRUE; - - InitializeListHead(&device_extension->list_head); - - KeInitializeSpinLock(&device_extension->list_lock); - - KeInitializeSpinLock(&device_extension->last_io_lock); - - KeInitializeEvent(&device_extension->request_event, - NotificationEvent, FALSE); - - KeInitializeEvent(&device_extension->terminate_thread, - NotificationEvent, FALSE); - - device_extension->device_number = CreateData->DeviceNumber; - - device_extension->file_name = file_name; - - device_extension->disk_geometry = CreateData->DiskGeometry; - - device_extension->image_offset = CreateData->ImageOffset; - - if (IMDISK_SPARSE_FILE(CreateData->Flags)) - device_extension->use_set_zero_data = TRUE; - - // VM disk. - if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_VM) - device_extension->vm_disk = TRUE; - else - device_extension->vm_disk = FALSE; - - // AWEAlloc disk. - if ((IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_FILE) & - (IMDISK_FILE_TYPE(CreateData->Flags) == IMDISK_FILE_TYPE_AWEALLOC)) - device_extension->awealloc_disk = TRUE; - else - device_extension->awealloc_disk = FALSE; - - // Byte-swap - if (IMDISK_BYTE_SWAP(CreateData->Flags)) - device_extension->byte_swap = TRUE; - else - device_extension->byte_swap = FALSE; - - // Image opened for shared writing - if (IMDISK_SHARED_IMAGE(CreateData->Flags)) - device_extension->shared_image = TRUE; - else - device_extension->shared_image = FALSE; - - device_extension->image_buffer = image_buffer; - device_extension->file_handle = file_handle; - - // Use proxy service. - if (IMDISK_TYPE(CreateData->Flags) == IMDISK_TYPE_PROXY) - { - device_extension->proxy = proxy; - device_extension->use_proxy = TRUE; - } - else - device_extension->use_proxy = FALSE; - - device_extension->proxy_unmap = proxy_supports_unmap; - - device_extension->proxy_zero = proxy_supports_zero; - - device_extension->media_change_count++; - - device_extension->drive_letter = CreateData->DriveLetter; - - device_extension->device_thread = KeGetCurrentThread(); - - device_extension->parallel_io = parallel_io; - - device_extension->file_object = file_object; - - device_extension->dev_object = dev_object; - - (*DeviceObject)->Flags &= ~DO_DEVICE_INITIALIZING; - - KdPrint(("ImDisk: Device '%ws' created.\n", device_name_buffer)); - - ExFreePoolWithTag(device_name_buffer, POOL_TAG); - - return STATUS_SUCCESS; -} - -VOID -ImDiskUnload(IN PDRIVER_OBJECT DriverObject) -{ - PDEVICE_OBJECT device_object; - PLIST_ENTRY list_entry; - - PAGED_CODE(); - - DbgPrint("ImDisk: Entering ImDiskUnload for driver %p.\n", - DriverObject); - - if (RefreshEvent != NULL) - { - KdPrint(("ImDisk: Pulsing and closing refresh event.\n")); - - KePulseEvent(RefreshEvent, 0, FALSE); - ObDereferenceObject(RefreshEvent); - RefreshEvent = NULL; - } - - KdPrint(("ImDisk: Current device objects chain dump for this driver:\n")); - - for (device_object = DriverObject->DeviceObject; - device_object != NULL;) - { - KdPrint(("%p -> ", device_object)); - device_object = device_object->NextDevice; - } - - KdPrint(("End of device chain.\n")); - - while ((list_entry = RemoveHeadList(&ReferencedObjects)) != - &ReferencedObjects) - { - PREFERENCED_OBJECT record = - CONTAINING_RECORD(list_entry, REFERENCED_OBJECT, list_entry); - - DbgPrint("ImDisk: Freeing unclaimed referenced object: %p\n", - record->file_object); - - ExFreePoolWithTag(record, POOL_TAG); - } - - for (device_object = DriverObject->DeviceObject; - device_object != NULL; ) - { - PDEVICE_OBJECT next_device = device_object->NextDevice; - PDEVICE_EXTENSION device_extension = (PDEVICE_EXTENSION) - device_object->DeviceExtension; - - KdPrint(("ImDisk: Now deleting device %i.\n", - device_extension->device_number)); - - if (device_object == ImDiskCtlDevice) - { - UNICODE_STRING sym_link; - LARGE_INTEGER time_out; - time_out.QuadPart = -1000000; - -#pragma warning(suppress: 28175) - while (device_object->ReferenceCount != 0) - { - KdPrint(("ImDisk: Ctl device is busy. Waiting.\n")); - - KeDelayExecutionThread(KernelMode, FALSE, &time_out); - - time_out.LowPart <<= 2; - } - - KdPrint(("ImDisk: Deleting ctl device.\n")); - RtlInitUnicodeString(&sym_link, IMDISK_CTL_SYMLINK_NAME); - IoDeleteSymbolicLink(&sym_link); - IoDeleteDevice(device_object); - } - else - { - PKTHREAD device_thread; - - KdPrint(("ImDisk: Shutting down device %i.\n", - device_extension->device_number)); - - device_thread = device_extension->device_thread; - ObReferenceObjectByPointer(device_thread, SYNCHRONIZE, NULL, - KernelMode); - - ImDiskRemoveVirtualDisk(device_object); - - KdPrint(("ImDisk: Waiting for device thread %i to terminate.\n", - device_extension->device_number)); - - KeWaitForSingleObject(device_thread, - Executive, - KernelMode, - FALSE, - NULL); - - ObDereferenceObject(device_thread); - } - - device_object = next_device; - } - - KdPrint - (("ImDisk: No more devices to delete. Leaving ImDiskUnload.\n")); -} - -#pragma code_seg() - -VOID -ImDiskFindFreeDeviceNumber(PDRIVER_OBJECT DriverObject, - PULONG DeviceNumber) -{ - KLOCK_QUEUE_HANDLE lock_handle; - PDEVICE_OBJECT device_object; - - ImDiskAcquireLock(&DeviceListLock, &lock_handle); - - *DeviceNumber = 0; - for (device_object = DriverObject->DeviceObject; - device_object != NULL; -#pragma warning(suppress: 28175) - device_object = device_object->NextDevice) - { - PDEVICE_EXTENSION device_extension = (PDEVICE_EXTENSION) - device_object->DeviceExtension; - - // Skip over control device - if (device_extension->device_number == -1) - continue; - - KdPrint2(("ImDisk: Found device %i.\n", - device_extension->device_number)); - - // Result will be one number above highest existing - if (device_extension->device_number >= *DeviceNumber) - *DeviceNumber = device_extension->device_number + 1; - } - - ImDiskReleaseLock(&lock_handle); -} - -#if DEBUG_LEVEL >= 1 -VOID -ImDiskLogDbgError(IN PVOID Object, - IN UCHAR MajorFunctionCode, - IN UCHAR RetryCount, - IN PULONG DumpData, - IN USHORT DumpDataSize, - IN USHORT EventCategory, - IN NTSTATUS ErrorCode, - IN ULONG UniqueErrorValue, - IN NTSTATUS FinalStatus, - IN ULONG SequenceNumber, - IN ULONG IoControlCode, - IN PLARGE_INTEGER DeviceOffset, - IN PWCHAR Message) -{ - ULONG_PTR string_byte_size; - ULONG_PTR packet_size; - PIO_ERROR_LOG_PACKET error_log_packet; - - if (KeGetCurrentIrql() > DISPATCH_LEVEL) - return; - - string_byte_size = (wcslen(Message) + 1) << 1; - - packet_size = - sizeof(IO_ERROR_LOG_PACKET) + DumpDataSize + string_byte_size; - - if (packet_size > ERROR_LOG_MAXIMUM_SIZE) - { - KdPrint(("ImDisk: Warning: Too large error log packet.\n")); - return; - } - - error_log_packet = - (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Object, - (UCHAR)packet_size); - - if (error_log_packet == NULL) - { - KdPrint(("ImDisk: Warning: IoAllocateErrorLogEntry() returned NULL.\n")); - return; - } - - error_log_packet->MajorFunctionCode = MajorFunctionCode; - error_log_packet->RetryCount = RetryCount; - error_log_packet->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + DumpDataSize; - error_log_packet->EventCategory = EventCategory; - error_log_packet->ErrorCode = ErrorCode; - error_log_packet->UniqueErrorValue = UniqueErrorValue; - error_log_packet->FinalStatus = FinalStatus; - error_log_packet->SequenceNumber = SequenceNumber; - error_log_packet->IoControlCode = IoControlCode; - if (DeviceOffset != NULL) - error_log_packet->DeviceOffset = *DeviceOffset; - error_log_packet->DumpDataSize = DumpDataSize; - - if (DumpDataSize != 0) - memcpy(error_log_packet->DumpData, DumpData, DumpDataSize); - - if (Message == NULL) - error_log_packet->NumberOfStrings = 0; - else - { - error_log_packet->NumberOfStrings = 1; - memcpy((PUCHAR)error_log_packet + error_log_packet->StringOffset, - Message, - string_byte_size); - } - - IoWriteErrorLogEntry(error_log_packet); -} -#endif - -VOID -ImDiskRemoveVirtualDisk(IN PDEVICE_OBJECT DeviceObject) -{ - PDEVICE_EXTENSION device_extension; - - ASSERT(DeviceObject != NULL); - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - KdPrint(("ImDisk: Request to shutdown device %i.\n", - device_extension->device_number)); - - if (device_extension->drive_letter != 0) - if (KeGetCurrentIrql() == PASSIVE_LEVEL) - ImDiskRemoveDriveLetter(device_extension->drive_letter); - - KeSetEvent(&device_extension->terminate_thread, (KPRIORITY)0, FALSE); -} - -#pragma code_seg("PAGE") - -NTSTATUS -ImDiskRemoveDriveLetter(IN WCHAR DriveLetter) -{ - NTSTATUS status; - WCHAR sym_link_global_wchar[] = L"\\DosDevices\\Global\\ :"; - WCHAR sym_link_wchar[] = L"\\DosDevices\\ :"; - UNICODE_STRING sym_link; - - PAGED_CODE(); - - sym_link_global_wchar[19] = DriveLetter; - - KdPrint(("ImDisk: Removing symlink '%ws'.\n", sym_link_global_wchar)); - - RtlInitUnicodeString(&sym_link, sym_link_global_wchar); - status = IoDeleteSymbolicLink(&sym_link); - - if (!NT_SUCCESS(status)) - { - KdPrint - (("ImDisk: Cannot remove symlink '%ws'. (%#x)\n", - sym_link_global_wchar, status)); - } - - sym_link_wchar[12] = DriveLetter; - - KdPrint(("ImDisk: Removing symlink '%ws'.\n", sym_link_wchar)); - - RtlInitUnicodeString(&sym_link, sym_link_wchar); - status = IoDeleteSymbolicLink(&sym_link); - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Cannot remove symlink '%ws'. (%#x)\n", - sym_link_wchar, status)); - } - - return status; -} - -#pragma code_seg() - -NTSTATUS -ImDiskCreateClose(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PIO_STACK_LOCATION io_stack; - PDEVICE_EXTENSION device_extension; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - KdPrint(("ImDisk: Entering ImDiskCreateClose.\n")); - - io_stack = IoGetCurrentIrpStackLocation(Irp); - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - if (io_stack->FileObject->FileName.Length != 0) - { - KdPrint(("ImDisk: Attempt to open '%wZ' on device %i.\n", - &io_stack->FileObject->FileName, - device_extension->device_number)); - - status = STATUS_OBJECT_NAME_NOT_FOUND; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if ((io_stack->MajorFunction == IRP_MJ_CREATE) && - KeReadStateEvent(&device_extension->terminate_thread)) - { - KdPrint(("ImDisk: Attempt to open device %i when shut down.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - KdPrint(("ImDisk: Successfully created/closed a handle for device %i.\n", - device_extension->device_number)); - - status = STATUS_SUCCESS; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = FILE_OPENED; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; -} - -typedef struct _LOWER_DEVICE_WORK_ITEM -{ - PIRP OriginalIrp; - PDEVICE_EXTENSION DeviceExtension; - LONGLONG OriginalOffset; - PUCHAR AllocatedBuffer; - PUCHAR SystemBuffer; - BOOLEAN CopyBack; -} LOWER_DEVICE_WORK_ITEM, *PLOWER_DEVICE_WORK_ITEM; - -VOID -ImDiskFreeIrpWithMdls(PIRP Irp) -{ - PMDL mdl; - PMDL nextMdl; - for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) - { - nextMdl = mdl->Next; - - if (mdl->MdlFlags & MDL_PAGES_LOCKED) - { - MmUnlockPages(mdl); - } - - IoFreeMdl(mdl); - } - - Irp->MdlAddress = NULL; - - IoFreeIrp(Irp); -} - -NTSTATUS -ImDiskReadWriteLowerDeviceCompletion(PDEVICE_OBJECT DeviceObject, - PIRP Irp, PVOID Context) -{ - PLOWER_DEVICE_WORK_ITEM item = (PLOWER_DEVICE_WORK_ITEM)Context; - - ASSERT(item != NULL); - - __analysis_assume(item != NULL); - - UNREFERENCED_PARAMETER(DeviceObject); - - item->OriginalIrp->IoStatus = Irp->IoStatus; - - if (!NT_SUCCESS(Irp->IoStatus.Status)) - { - KdPrint(("ImDiskReadWriteLowerDeviceCompletion: Parallel I/O failed with status %#x\n", - Irp->IoStatus.Status)); - } - else - { - if (item->CopyBack) - { - RtlCopyMemory(item->SystemBuffer, item->AllocatedBuffer, - Irp->IoStatus.Information); - } - - if (item->AllocatedBuffer != NULL) - { - KLOCK_QUEUE_HANDLE lock_handle; - - ImDiskAcquireLock(&item->DeviceExtension->last_io_lock, &lock_handle); - - if (item->DeviceExtension->last_io_data != NULL) - { - ExFreePoolWithTag(item->DeviceExtension->last_io_data, - POOL_TAG); - } - - item->DeviceExtension->last_io_data = item->AllocatedBuffer; - - item->DeviceExtension->last_io_offset = item->OriginalOffset; - item->DeviceExtension->last_io_length = - (ULONG)Irp->IoStatus.Information; - - ImDiskReleaseLock(&lock_handle); - } - } - - if (Irp->MdlAddress != item->OriginalIrp->MdlAddress) - { - ImDiskFreeIrpWithMdls(Irp); - } - else - { - IoFreeIrp(Irp); - } - - IoCompleteRequest(item->OriginalIrp, IO_NO_INCREMENT); - - ExFreePoolWithTag(item, POOL_TAG); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - -NTSTATUS -ImDiskDeviceControlLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension) -{ - PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); - PIO_STACK_LOCATION lower_io_stack = IoGetNextIrpStackLocation(Irp); - - IoCopyCurrentIrpStackLocationToNext(Irp); - - if (io_stack->Parameters.DeviceIoControl.IoControlCode == - IOCTL_IMDISK_FSCTL_PASS_THROUGH) - { - lower_io_stack->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; - } - - lower_io_stack->Parameters.DeviceIoControl.IoControlCode = - *(PULONG)Irp->AssociatedIrp.SystemBuffer; - - lower_io_stack->Parameters.DeviceIoControl.InputBufferLength -= - sizeof(ULONG); - - RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, - (PUCHAR)Irp->AssociatedIrp.SystemBuffer + sizeof(ULONG), - lower_io_stack->Parameters.DeviceIoControl.InputBufferLength); - - lower_io_stack->FileObject = DeviceExtension->file_object; - - return IoCallDriver(DeviceExtension->dev_object, Irp); -} - -NTSTATUS -ImDiskReadWriteLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension) -{ - PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); - PIO_STACK_LOCATION lower_io_stack; - PIRP lower_irp; - PLOWER_DEVICE_WORK_ITEM item; - - // If image file is a direct I/O device, we simply forward the IRP with - // correct FILE_OBJECT and adjusted offset if needed. - if ((DeviceExtension->dev_object->Flags & DO_DIRECT_IO) == DO_DIRECT_IO) - { - IoCopyCurrentIrpStackLocationToNext(Irp); - - lower_io_stack = IoGetNextIrpStackLocation(Irp); - - lower_io_stack->Parameters.Read.ByteOffset.QuadPart += - DeviceExtension->image_offset.QuadPart; - - lower_io_stack->FileObject = DeviceExtension->file_object; - - if ((io_stack->MajorFunction == IRP_MJ_WRITE) && - !DeviceExtension->image_modified) - { - DeviceExtension->image_modified = TRUE; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - } - - return IoCallDriver(DeviceExtension->dev_object, Irp); - } - - // This goes for image files with DO_BUFFERED_IO or DO_NEITHER_IO. - // We allocate NP pool as buffer for a request to send down. A completion - // routine takes care of copying read operation data back to original IRP. - - item = (PLOWER_DEVICE_WORK_ITEM) - ExAllocatePoolWithTag(NonPagedPool, - sizeof(*item), POOL_TAG); - - if (item == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlZeroMemory(item, sizeof(*item)); - - item->OriginalIrp = Irp; - item->DeviceExtension = DeviceExtension; - item->OriginalOffset = io_stack->Parameters.Read.ByteOffset.QuadPart; - - if ((io_stack->MajorFunction == IRP_MJ_READ) || - (io_stack->MajorFunction == IRP_MJ_WRITE)) - { - item->SystemBuffer = (PUCHAR) - MmGetSystemAddressForMdlSafe(Irp->MdlAddress, - NormalPagePriority); - - if (item->SystemBuffer == NULL) - { - ExFreePoolWithTag(item, POOL_TAG); - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - } - - lower_irp = IoAllocateIrp(DeviceExtension->dev_object->StackSize, FALSE); - - if (lower_irp == NULL) - { - ExFreePoolWithTag(item, POOL_TAG); - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - - lower_io_stack = IoGetNextIrpStackLocation(lower_irp); - - lower_io_stack->MajorFunction = io_stack->MajorFunction; - lower_io_stack->Parameters = io_stack->Parameters; - - if ((io_stack->MajorFunction == IRP_MJ_READ) || - (io_stack->MajorFunction == IRP_MJ_WRITE)) - { - lower_irp->AssociatedIrp.SystemBuffer = - lower_irp->UserBuffer = - item->AllocatedBuffer = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, - io_stack->Parameters.Read.Length, POOL_TAG); - - if (item->AllocatedBuffer == NULL) - { - ImDiskFreeIrpWithMdls(lower_irp); - ExFreePoolWithTag(item, POOL_TAG); - - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - IoCompleteRequest(Irp, IO_NO_INCREMENT); - return STATUS_INSUFFICIENT_RESOURCES; - } - - if (io_stack->MajorFunction == IRP_MJ_WRITE) - { - RtlCopyMemory(item->AllocatedBuffer, item->SystemBuffer, - io_stack->Parameters.Write.Length); - } - else if (io_stack->MajorFunction == IRP_MJ_READ) - { - item->CopyBack = TRUE; - } - } - - lower_irp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; - - if (io_stack->MajorFunction == IRP_MJ_READ) - { - lower_irp->Flags |= IRP_READ_OPERATION; - } - else if (io_stack->MajorFunction == IRP_MJ_WRITE) - { - lower_irp->Flags |= IRP_WRITE_OPERATION; - lower_io_stack->Flags |= SL_WRITE_THROUGH; - } - - lower_irp->Flags |= IRP_NOCACHE; - - lower_io_stack->Parameters.Read = io_stack->Parameters.Read; - lower_io_stack->Parameters.Read.ByteOffset.QuadPart += - DeviceExtension->image_offset.QuadPart; - - lower_io_stack->FileObject = DeviceExtension->file_object; - - if ((io_stack->MajorFunction == IRP_MJ_WRITE) && - (!DeviceExtension->image_modified)) - { - DeviceExtension->image_modified = TRUE; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - } - - IoSetCompletionRoutine(lower_irp, ImDiskReadWriteLowerDeviceCompletion, - item, TRUE, TRUE, TRUE); - - IoMarkIrpPending(Irp); - - (void)IoCallDriver(DeviceExtension->dev_object, lower_irp); - - return STATUS_PENDING; -} - -NTSTATUS -ImDiskReadWrite(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - if ((io_stack->Parameters.Read.ByteOffset.QuadPart < 0) || - ((io_stack->Parameters.Read.ByteOffset.QuadPart + - io_stack->Parameters.Read.Length) < 0)) - { - KdPrint(("ImDisk: Read/write attempt on negative offset.\n")); - - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return STATUS_SUCCESS; - } - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - if (DeviceObject == ImDiskCtlDevice) - { - KdPrint(("ImDisk: Read/write attempt on ctl device.\n")); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: Read/write attempt on device %i while removing.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if ((io_stack->MajorFunction == IRP_MJ_WRITE) && - device_extension->read_only) - { - KdPrint(("ImDisk: Attempt to write to write-protected device %i.\n", - device_extension->device_number)); - - status = STATUS_MEDIA_WRITE_PROTECTED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if ((io_stack->Parameters.Read.ByteOffset.QuadPart + - io_stack->Parameters.Read.Length) > - (device_extension->disk_geometry.Cylinders.QuadPart)) - { - KdPrint(("ImDisk: Read/write beyond eof on device %i.\n", - device_extension->device_number)); - - status = STATUS_SUCCESS; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if (io_stack->Parameters.Read.Length == 0) - { - KdPrint(("ImDisk: Read/write zero bytes on device %i.\n", - device_extension->device_number)); - - status = STATUS_SUCCESS; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - KdPrint2(("ImDisk: Device %i got r/w request Offset=0x%.8x%.8x Len=%#x.\n", - device_extension->device_number, - io_stack->Parameters.Read.ByteOffset.HighPart, - io_stack->Parameters.Read.ByteOffset.LowPart, - io_stack->Parameters.Read.Length)); - - status = STATUS_PENDING; - - if (io_stack->MajorFunction == IRP_MJ_READ) - { - KLOCK_QUEUE_HANDLE lock_handle; - - ImDiskAcquireLock(&device_extension->last_io_lock, &lock_handle); - - if (device_extension->last_io_data != NULL) - { - if ((io_stack->Parameters.Read.ByteOffset.QuadPart >= - device_extension->last_io_offset) & - ((io_stack->Parameters.Read.ByteOffset.QuadPart + - io_stack->Parameters.Read.Length) <= - (device_extension->last_io_offset + - device_extension->last_io_length))) - { - PUCHAR system_buffer = - (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, - NormalPagePriority); - if (system_buffer == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - KdPrint(("ImDisk: Device %i read Offset=0x%.8x%.8x Len=%#x, " - "intermediate cache hit.\n", - device_extension->device_number, - io_stack->Parameters.Read.ByteOffset.HighPart, - io_stack->Parameters.Read.ByteOffset.LowPart, - io_stack->Parameters.Read.Length)); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = io_stack->Parameters.Read.Length; - - RtlCopyMemory(system_buffer, - device_extension->last_io_data + - io_stack->Parameters.Read.ByteOffset.QuadPart - - device_extension->last_io_offset, - Irp->IoStatus.Information); - - if (device_extension->byte_swap) - ImDiskByteSwapBuffer(system_buffer, - Irp->IoStatus.Information); - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset.QuadPart += - Irp->IoStatus.Information; - } - status = STATUS_SUCCESS; - } - } - } - - ImDiskReleaseLock(&lock_handle); - } - - // In-thread I/O using a FILE_OBJECT - if ((status == STATUS_PENDING) && - device_extension->parallel_io) - { - BOOLEAN set_zero_data = FALSE; - - if (device_extension->use_set_zero_data && - (io_stack->MajorFunction == IRP_MJ_WRITE) && - (io_stack->Parameters.Write.Length > sizeof(ULONGLONG))) - { - PUCHAR system_buffer = - (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, - NormalPagePriority); - - if (system_buffer == NULL) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - Irp->IoStatus.Information = 0; - status = STATUS_INSUFFICIENT_RESOURCES; - } - else if (ImDiskIsBufferZero(system_buffer, - io_stack->Parameters.Write.Length)) - { - set_zero_data = TRUE; - } - } - - if (!set_zero_data) - { - return ImDiskReadWriteLowerDevice(Irp, device_extension); - } - } - - if (status == STATUS_PENDING) - { - IoMarkIrpPending(Irp); - - ImDiskInterlockedInsertTailList(&device_extension->list_head, - &Irp->Tail.Overlay.ListEntry, - &device_extension->list_lock); - - KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); - - return STATUS_PENDING; - } - else - { - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } -} - -NTSTATUS -ImDiskDeviceControl(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - Irp->IoStatus.Information = 0; - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - io_stack = IoGetCurrentIrpStackLocation(Irp); - - KdPrint(("ImDisk: Device %i received IOCTL %#x IRP %p.\n", - device_extension->device_number, - io_stack->Parameters.DeviceIoControl.IoControlCode, - Irp)); - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: IOCTL attempt on device %i that is being removed.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - // The control device can only receive version queries, enumeration queries - // or device create requests. - if (DeviceObject == ImDiskCtlDevice) - { - switch (io_stack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_IMDISK_QUERY_VERSION: - case IOCTL_IMDISK_CREATE_DEVICE: - case IOCTL_IMDISK_REMOVE_DEVICE: - case IOCTL_IMDISK_QUERY_DRIVER: - case IOCTL_IMDISK_REFERENCE_HANDLE: - case IOCTL_IMDISK_GET_REFERENCED_HANDLE: - break; - - default: - KdPrint(("ImDisk: Invalid IOCTL %#x for control device.\n", - io_stack->Parameters.DeviceIoControl.IoControlCode)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - } - else - { - switch (io_stack->Parameters.DeviceIoControl.IoControlCode) - { - // Invalid IOCTL codes for this driver's disk devices. - case IOCTL_IMDISK_CREATE_DEVICE: - case IOCTL_IMDISK_REMOVE_DEVICE: - case IOCTL_IMDISK_REFERENCE_HANDLE: - case IOCTL_IMDISK_GET_REFERENCED_HANDLE: - KdPrint(("ImDisk: Invalid IOCTL %#x for disk device.\n", - io_stack->Parameters.DeviceIoControl.IoControlCode)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - } - - switch (io_stack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_IMDISK_SET_DEVICE_FLAGS: - KdPrint(("ImDisk: IOCTL_IMDISK_SET_DEVICE_FLAGS for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(IMDISK_SET_DEVICE_FLAGS)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - { - PIMDISK_SET_DEVICE_FLAGS device_flags = (PIMDISK_SET_DEVICE_FLAGS) - Irp->AssociatedIrp.SystemBuffer; - - KIRQL irql = KeGetCurrentIrql(); - - if (irql >= DISPATCH_LEVEL) - { - status = STATUS_ACCESS_DENIED; - break; - } - - status = STATUS_SUCCESS; - - if (IMDISK_READONLY(device_flags->FlagsToChange)) - if (DeviceObject->DeviceType == FILE_DEVICE_DISK) - { - if ((IMDISK_READONLY(device_flags->FlagValues) != 0) & - (device_extension->special_file_count <= 0)) - { - DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; - device_extension->read_only = TRUE; - - device_flags->FlagsToChange &= ~IMDISK_OPTION_RO; - } - else - // It is not possible to make a file- or proxy virtual disk - // writable on the fly. (A physical image file or the proxy - // comm channel might not be opened for writing.) - if (device_extension->vm_disk) - { - DeviceObject->Characteristics &= ~FILE_READ_ONLY_DEVICE; - device_extension->read_only = FALSE; - - device_flags->FlagsToChange &= ~IMDISK_OPTION_RO; - } - } - - if (IMDISK_REMOVABLE(device_flags->FlagsToChange)) - if (DeviceObject->DeviceType == FILE_DEVICE_DISK) - { - if (IMDISK_REMOVABLE(device_flags->FlagValues)) - DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; - else - DeviceObject->Characteristics &= ~FILE_REMOVABLE_MEDIA; - - device_flags->FlagsToChange &= ~IMDISK_OPTION_REMOVABLE; - } - - if (device_flags->FlagsToChange & IMDISK_IMAGE_MODIFIED) - { - if (device_flags->FlagValues & IMDISK_IMAGE_MODIFIED) - device_extension->image_modified = TRUE; - else - device_extension->image_modified = FALSE; - - device_flags->FlagsToChange &= ~IMDISK_IMAGE_MODIFIED; - } - - if (irql == PASSIVE_LEVEL) - { - if (IMDISK_SPARSE_FILE(device_flags->FlagsToChange) && - IMDISK_SPARSE_FILE(device_flags->FlagValues) && - (!device_extension->use_proxy) && - (!device_extension->vm_disk)) - { - IO_STATUS_BLOCK io_status; - status = ZwFsControlFile(device_extension->file_handle, - NULL, - NULL, - NULL, - &io_status, - FSCTL_SET_SPARSE, - NULL, - 0, - NULL, - 0); - - if (NT_SUCCESS(status)) - { - device_extension->use_set_zero_data = TRUE; - device_flags->FlagsToChange &= ~IMDISK_OPTION_SPARSE_FILE; - } - } - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - } - else - KdPrint - (("ImDisk: Some flags cannot be changed at this IRQL (%#x).\n", - irql)); - - if (device_flags->FlagsToChange == 0) - status = STATUS_SUCCESS; - else if (NT_SUCCESS(status)) - status = STATUS_INVALID_DEVICE_REQUEST; - } - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= - sizeof(IMDISK_SET_DEVICE_FLAGS)) - { - Irp->IoStatus.Information = sizeof(IMDISK_SET_DEVICE_FLAGS); - } - - break; - - case IOCTL_IMDISK_REFERENCE_HANDLE: - { - KLOCK_QUEUE_HANDLE lock_handle; - PREFERENCED_OBJECT record; - - KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE for device %i.\n", - device_extension->device_number)); - - // This IOCTL requires work that must be done at IRQL < DISPATCH_LEVEL - // but must be done in the thread context of the calling application and - // not by the worker thread so therefore this check is done. Also, the - // control device does not have a worker thread so that is another - // reason. - if (KeGetCurrentIrql() > PASSIVE_LEVEL) - { - KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE not accessible " - "at current IRQL (%i).", KeGetCurrentIrql())); - - status = STATUS_ACCESS_DENIED; - - break; - } - - if ((io_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) && - !SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_TCB_PRIVILEGE), - Irp->RequestorMode)) - { - KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE not accessible, " - "privilege not held by calling thread.\n")); - - status = STATUS_ACCESS_DENIED; - - break; - } - - if ((io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(HANDLE)) | - (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(PFILE_OBJECT))) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - record = (PREFERENCED_OBJECT) - ExAllocatePoolWithTag(NonPagedPool, - sizeof(REFERENCED_OBJECT), POOL_TAG); - - if (record == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - KdPrint(("ImDisk: Referencing handle %p.\n", - *(PHANDLE)Irp->AssociatedIrp.SystemBuffer)); - - status = ObReferenceObjectByHandle( - *(PHANDLE)Irp->AssociatedIrp.SystemBuffer, - FILE_READ_ATTRIBUTES | - FILE_READ_DATA | - FILE_WRITE_DATA, - *IoFileObjectType, - Irp->RequestorMode, - (PVOID*)&record->file_object, - NULL); - - KdPrint(("ImDisk: Status=%#x, PFILE_OBJECT %p.\n", - status, - record->file_object)); - - if (!NT_SUCCESS(status)) - { - ExFreePoolWithTag(record, POOL_TAG); - break; - } - - ImDiskAcquireLock(&ReferencedObjectsListLock, &lock_handle); - - InsertTailList(&ReferencedObjects, &record->list_entry); - - ImDiskReleaseLock(&lock_handle); - - *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer = record->file_object; - Irp->IoStatus.Information = sizeof(PFILE_OBJECT); - - break; - } - - case IOCTL_IMDISK_GET_REFERENCED_HANDLE: - { - KLOCK_QUEUE_HANDLE lock_handle; - PLIST_ENTRY list_entry; - - if (io_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) - { - status = STATUS_ACCESS_DENIED; - break; - } - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(PFILE_OBJECT)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - status = STATUS_OBJECT_NAME_NOT_FOUND; - - ImDiskAcquireLock(&ReferencedObjectsListLock, &lock_handle); - - for (list_entry = ReferencedObjects.Flink; - list_entry != &ReferencedObjects; - list_entry = list_entry->Flink) - { - PREFERENCED_OBJECT record = - CONTAINING_RECORD(list_entry, REFERENCED_OBJECT, list_entry); - - if (record->file_object == - *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer) - { - list_entry->Blink->Flink = list_entry->Flink; - list_entry->Flink->Blink = list_entry->Blink; - - ExFreePoolWithTag(record, POOL_TAG); - - status = STATUS_SUCCESS; - break; - } - } - - ImDiskReleaseLock(&lock_handle); - - if (NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Successfully claimed referenced object %p.\n", - *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer)); - } - else - { - DbgPrint("ImDisk Warning: Requested %p not in referenced objects list.\n", - *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer); - } - - break; - } - - case IOCTL_IMDISK_CREATE_DEVICE: - { - PIMDISK_CREATE_DATA create_data; - - KdPrint(("ImDisk: IOCTL_IMDISK_CREATE_DEVICE for device %i.\n", - device_extension->device_number)); - - // This IOCTL requires work that must be done at IRQL == PASSIVE_LEVEL - // but the control device has no worker thread (does not handle any - // other I/O) so therefore everything is done directly here. Therefore - // this IRQL check is necessary. - if (KeGetCurrentIrql() > PASSIVE_LEVEL) - { - status = STATUS_ACCESS_DENIED; - - break; - } - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(IMDISK_CREATE_DATA) - sizeof(*create_data->FileName)) - { - KdPrint(("ImDisk: Invalid input buffer size (1). " - "Got: %u Expected at least: %u.\n", - io_stack->Parameters.DeviceIoControl.InputBufferLength, - (int)(sizeof(IMDISK_CREATE_DATA) - - sizeof(*create_data->FileName)))); - - status = STATUS_INVALID_PARAMETER; - - break; - } - - create_data = (PIMDISK_CREATE_DATA)Irp->AssociatedIrp.SystemBuffer; - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(IMDISK_CREATE_DATA) + - create_data->FileNameLength - - sizeof(*create_data->FileName)) - { - KdPrint(("ImDisk: Invalid input buffer size (2). " - "Got: %u Expected at least: %u.\n", - io_stack->Parameters.DeviceIoControl.InputBufferLength, - (int)(sizeof(IMDISK_CREATE_DATA) + - create_data->FileNameLength - - sizeof(*create_data->FileName)))); - - status = STATUS_INVALID_PARAMETER; - - break; - } - - status = ImDiskAddVirtualDisk(DeviceObject->DriverObject, - (PIMDISK_CREATE_DATA) - Irp->AssociatedIrp.SystemBuffer, - Irp->Tail.Overlay.Thread); - - if (NT_SUCCESS(status) && - (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= - io_stack->Parameters.DeviceIoControl.InputBufferLength)) - { - Irp->IoStatus.Information = - io_stack->Parameters.DeviceIoControl.OutputBufferLength; - } - - break; - } - - case IOCTL_IMDISK_REMOVE_DEVICE: - { - ULONG device_number; - KLOCK_QUEUE_HANDLE lock_handle; - PDEVICE_OBJECT device_object = - ImDiskCtlDevice->DriverObject->DeviceObject; - - KdPrint(("ImDisk: IOCTL_IMDISK_REMOVE_DEVICE.\n")); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(ULONG)) - { - KdPrint(("ImDisk: Invalid input buffer size (1). " - "Got: %u Expected at least: %u.\n", - io_stack->Parameters.DeviceIoControl.InputBufferLength, - (int)sizeof(ULONG))); - - status = STATUS_INVALID_PARAMETER; - - break; - } - - device_number = *(PULONG)Irp->AssociatedIrp.SystemBuffer; - - KdPrint(("ImDisk: IOCTL_IMDISK_REMOVE_DEVICE for device %i.\n", - device_number)); - - status = STATUS_OBJECT_NAME_NOT_FOUND; - - ImDiskAcquireLock(&DeviceListLock, &lock_handle); - - while (device_object != NULL) - { - PDEVICE_EXTENSION device_extension = - (PDEVICE_EXTENSION)device_object->DeviceExtension; - - if (device_extension->device_number == device_number) - { - if (device_extension->special_file_count > 0) - { - status = STATUS_ACCESS_DENIED; - } - else - { - status = STATUS_SUCCESS; - } - break; - } - -#pragma warning(suppress: 28175) - device_object = device_object->NextDevice; - } - - ImDiskReleaseLock(&lock_handle); - - if (status == STATUS_SUCCESS) - { - ImDiskRemoveVirtualDisk(device_object); - } - - break; - } - - case IOCTL_DISK_EJECT_MEDIA: - case IOCTL_STORAGE_EJECT_MEDIA: - KdPrint(("ImDisk: IOCTL_DISK/STORAGE_EJECT_MEDIA for device %i.\n", - device_extension->device_number)); - - if (device_extension->special_file_count > 0) - { - status = STATUS_ACCESS_DENIED; - } - else - { - ImDiskRemoveVirtualDisk(DeviceObject); - - status = STATUS_SUCCESS; - } - - break; - - case IOCTL_IMDISK_QUERY_DRIVER: - { - KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_DRIVER for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength > - sizeof(ULONGLONG)) - { - KLOCK_QUEUE_HANDLE lock_handle; - ULONG max_items = - io_stack->Parameters.DeviceIoControl.OutputBufferLength >> 2; - ULONG current_item = 0; - PULONG device_list = (PULONG)Irp->AssociatedIrp.SystemBuffer; - PDEVICE_OBJECT device_object; - - KdPrint2(("ImDisk: Max number of items %i.\n", max_items)); - - ImDiskAcquireLock(&DeviceListLock, &lock_handle); - - for (device_object = DeviceObject->DriverObject->DeviceObject; - device_object != NULL; -#pragma warning(suppress: 28175) - device_object = device_object->NextDevice) - { - PDEVICE_EXTENSION this_device_extension = (PDEVICE_EXTENSION) - device_object->DeviceExtension; - - // Skip over control device - if (this_device_extension->device_number == -1) - continue; - - current_item++; - - KdPrint2(("ImDisk: Found device %i.\n", - this_device_extension->device_number)); - - if (current_item < max_items) - device_list[current_item] = - this_device_extension->device_number; - } - - ImDiskReleaseLock(&lock_handle); - - device_list[0] = current_item; - - KdPrint(("ImDisk: Found %i devices.\n", device_list[0])); - - if (current_item >= max_items) - { - Irp->IoStatus.Information = 1 << 2; - status = STATUS_SUCCESS; - } - else - { - Irp->IoStatus.Information = ((ULONG_PTR)current_item + 1) << 2; - status = STATUS_SUCCESS; - } - } - else - { - ULARGE_INTEGER DeviceList = { 0 }; - KLOCK_QUEUE_HANDLE lock_handle; - PDEVICE_OBJECT device_object; - ULONG HighestDeviceNumber = 0; - - ImDiskAcquireLock(&DeviceListLock, &lock_handle); - - for (device_object = DeviceObject->DriverObject->DeviceObject; - device_object != NULL; -#pragma warning(suppress: 28175) - device_object = device_object->NextDevice) - { - PDEVICE_EXTENSION this_device_extension = (PDEVICE_EXTENSION) - device_object->DeviceExtension; - - // Skip over control device - if (this_device_extension->device_number == -1) - continue; - - KdPrint2(("ImDisk: Found device %i.\n", - this_device_extension->device_number)); - - if (this_device_extension->device_number > HighestDeviceNumber) - HighestDeviceNumber = this_device_extension->device_number; - - if (this_device_extension->device_number < 64) - DeviceList.QuadPart |= - 1ULL << this_device_extension->device_number; - } - - ImDiskReleaseLock(&lock_handle); - - KdPrint(("ImDisk: 64 bit device list = 0x%.8x%.8x.\n", - DeviceList.HighPart, DeviceList.LowPart)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= - sizeof(ULONGLONG)) - { - *(PULONGLONG)Irp->AssociatedIrp.SystemBuffer = - DeviceList.QuadPart; - Irp->IoStatus.Information = sizeof(ULONGLONG); - - if (HighestDeviceNumber > 63) - status = STATUS_INVALID_PARAMETER; - else - status = STATUS_SUCCESS; - } - else if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= - sizeof(ULONG)) - { - *(PULONG)Irp->AssociatedIrp.SystemBuffer = DeviceList.LowPart; - Irp->IoStatus.Information = sizeof(ULONG); - - if (HighestDeviceNumber > 31) - status = STATUS_INVALID_PARAMETER; - else - status = STATUS_SUCCESS; - } - else - { - status = STATUS_INVALID_PARAMETER; - } - } - - break; - } - - case IOCTL_IMDISK_QUERY_DEVICE: - { - PIMDISK_CREATE_DATA create_data; - - KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_DEVICE for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(IMDISK_CREATE_DATA) + - device_extension->file_name.Length + - sizeof(*create_data->FileName)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - create_data = (PIMDISK_CREATE_DATA)Irp->AssociatedIrp.SystemBuffer; - - create_data->DeviceNumber = device_extension->device_number; - create_data->DiskGeometry = device_extension->disk_geometry; - - create_data->Flags = 0; - if (device_extension->read_only) - create_data->Flags |= IMDISK_OPTION_RO; - - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - create_data->Flags |= IMDISK_OPTION_REMOVABLE; - - if (DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN) - create_data->Flags |= IMDISK_DEVICE_TYPE_RAW; - else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) - create_data->Flags |= IMDISK_DEVICE_TYPE_CD | IMDISK_OPTION_RO; - else if (DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) - create_data->Flags |= IMDISK_DEVICE_TYPE_FD; - else - create_data->Flags |= IMDISK_DEVICE_TYPE_HD; - - if (device_extension->vm_disk) - create_data->Flags |= IMDISK_TYPE_VM; - else if (device_extension->use_proxy) - create_data->Flags |= IMDISK_TYPE_PROXY; - else - create_data->Flags |= IMDISK_TYPE_FILE; - - if (device_extension->awealloc_disk) - create_data->Flags |= IMDISK_FILE_TYPE_AWEALLOC; - else if (device_extension->parallel_io) - create_data->Flags |= IMDISK_FILE_TYPE_PARALLEL_IO; - - if (device_extension->image_modified) - create_data->Flags |= IMDISK_IMAGE_MODIFIED; - - if (device_extension->use_set_zero_data) - create_data->Flags |= IMDISK_OPTION_SPARSE_FILE; - - if (device_extension->shared_image) - create_data->Flags |= IMDISK_OPTION_SHARED_IMAGE; - - create_data->ImageOffset = device_extension->image_offset; - - create_data->DriveLetter = device_extension->drive_letter; - - create_data->FileNameLength = device_extension->file_name.Length; - - if (device_extension->file_name.Length > 0) - RtlCopyMemory(create_data->FileName, - device_extension->file_name.Buffer, - device_extension->file_name.Length); - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(IMDISK_CREATE_DATA) + - create_data->FileNameLength - - sizeof(*create_data->FileName); - - break; - } - - case IOCTL_DISK_CHECK_VERIFY: - case IOCTL_CDROM_CHECK_VERIFY: - case IOCTL_STORAGE_CHECK_VERIFY: - case IOCTL_STORAGE_CHECK_VERIFY2: - { - KdPrint(("ImDisk: IOCTL_DISK/CDROM/STORAGE_CHECK_VERIFY/2 for " - "device %i.\n", device_extension->device_number)); - - if (device_extension->vm_disk) - { - KdPrint(("ImDisk: Faked verify ok on vm device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= - sizeof(ULONG)) - { - *(PULONG)Irp->AssociatedIrp.SystemBuffer = - device_extension->media_change_count; - - Irp->IoStatus.Information = sizeof(ULONG); - } - - status = STATUS_SUCCESS; - } - else - status = STATUS_PENDING; - - break; - } - - case IOCTL_IMDISK_QUERY_VERSION: - { - KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_VERSION for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(ULONG)) - status = STATUS_INVALID_PARAMETER; - else - { - *(PULONG)Irp->AssociatedIrp.SystemBuffer = IMDISK_DRIVER_VERSION; - Irp->IoStatus.Information = sizeof(ULONG); - status = STATUS_SUCCESS; - } - - break; - } - - case IOCTL_IMDISK_IOCTL_PASS_THROUGH: - case IOCTL_IMDISK_FSCTL_PASS_THROUGH: - { - KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL/FSCTL_PASS_THROUGH for device %i.\n", - device_extension->device_number)); - - if (device_extension->file_handle == NULL) - { - status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(ULONG)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - if (device_extension->parallel_io && - (METHOD_FROM_CTL_CODE( - io_stack->Parameters.DeviceIoControl.IoControlCode) == - METHOD_BUFFERED)) - { - return ImDiskDeviceControlLowerDevice(Irp, device_extension); - } - - status = STATUS_PENDING; - break; - } - - case IOCTL_DISK_FORMAT_TRACKS: - case IOCTL_DISK_FORMAT_TRACKS_EX: - // Only several checks are done here - // Actual operation is done by the device thread - { - PFORMAT_PARAMETERS param; - PDISK_GEOMETRY geometry; - - KdPrint(("ImDisk: IOCTL_DISK_FORMAT_TRACKS for device %i.\n", - device_extension->device_number)); - - /* - if (~DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) - { - status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - */ - - // Media is writable? - - if (device_extension->read_only) - { - KdPrint(("ImDisk: Attempt to format write-protected image.\n")); - - status = STATUS_MEDIA_WRITE_PROTECTED; - break; - } - - // Check input parameter size - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(FORMAT_PARAMETERS)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - // Input parameter sanity check - - param = (PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; - geometry = (PDISK_GEOMETRY) - ExAllocatePoolWithTag(NonPagedPool, - sizeof(DISK_GEOMETRY), - POOL_TAG); - if (geometry == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - RtlCopyMemory(geometry, &device_extension->disk_geometry, - sizeof(DISK_GEOMETRY)); - - geometry->Cylinders.QuadPart /= geometry->TracksPerCylinder; - geometry->Cylinders.QuadPart /= geometry->SectorsPerTrack; - geometry->Cylinders.QuadPart /= geometry->BytesPerSector; - - if ((param->StartHeadNumber > geometry->TracksPerCylinder - 1) || - (param->EndHeadNumber > geometry->TracksPerCylinder - 1) || - ((LONGLONG)param->StartCylinderNumber > - geometry->Cylinders.QuadPart) || - ((LONGLONG)param->EndCylinderNumber > - geometry->Cylinders.QuadPart) || - (param->EndCylinderNumber < param->StartCylinderNumber)) - { - ExFreePoolWithTag(geometry, POOL_TAG); - status = STATUS_INVALID_PARAMETER; - break; - } - - if ((param->StartCylinderNumber * geometry->TracksPerCylinder * - geometry->BytesPerSector * geometry->SectorsPerTrack + - param->StartHeadNumber * geometry->BytesPerSector * - geometry->SectorsPerTrack >= - device_extension->disk_geometry.Cylinders.QuadPart) | - (param->EndCylinderNumber * geometry->TracksPerCylinder * - geometry->BytesPerSector * geometry->SectorsPerTrack + - param->EndHeadNumber * geometry->BytesPerSector * - geometry->SectorsPerTrack >= - device_extension->disk_geometry.Cylinders.QuadPart)) - { - ExFreePoolWithTag(geometry, POOL_TAG); - status = STATUS_INVALID_PARAMETER; - break; - } - - // If this is an EX request then make a couple of extra checks - - if (io_stack->Parameters.DeviceIoControl.IoControlCode == - IOCTL_DISK_FORMAT_TRACKS_EX) - { - PFORMAT_EX_PARAMETERS exparam; - ULONG paramsize; - - KdPrint(("ImDisk: IOCTL_DISK_FORMAT_TRACKS_EX for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(FORMAT_EX_PARAMETERS)) - { - ExFreePoolWithTag(geometry, POOL_TAG); - status = STATUS_INVALID_PARAMETER; - break; - } - - exparam = (PFORMAT_EX_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; - - paramsize = sizeof(FORMAT_EX_PARAMETERS) - + exparam->SectorsPerTrack * sizeof(USHORT) - - sizeof(USHORT); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - paramsize || - exparam->FormatGapLength > geometry->SectorsPerTrack || - exparam->SectorsPerTrack != geometry->SectorsPerTrack) - { - ExFreePoolWithTag(geometry, POOL_TAG); - status = STATUS_INVALID_PARAMETER; - break; - } - } - - ExFreePoolWithTag(geometry, POOL_TAG); - status = STATUS_PENDING; - break; - } - - case IOCTL_DISK_GROW_PARTITION: - { - PDISK_GROW_PARTITION grow_partition; - - KdPrint(("ImDisk: IOCTL_DISK_GROW_PARTITION for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength != - sizeof(DISK_GROW_PARTITION)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - if (device_extension->read_only) - { - status = STATUS_MEDIA_WRITE_PROTECTED; - break; - } - - grow_partition = (PDISK_GROW_PARTITION) - Irp->AssociatedIrp.SystemBuffer; - - // Check so we don't get a smaller disk with these parameters - if ((grow_partition->PartitionNumber != 1) | - (device_extension->disk_geometry.Cylinders.QuadPart + - grow_partition->BytesToGrow.QuadPart < - device_extension->disk_geometry.Cylinders.QuadPart)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - status = STATUS_PENDING; - break; - } - - case IOCTL_DISK_UPDATE_PROPERTIES: - { - status = STATUS_SUCCESS; - break; - } - - case IOCTL_DISK_GET_MEDIA_TYPES: - case IOCTL_STORAGE_GET_MEDIA_TYPES: - case IOCTL_DISK_GET_DRIVE_GEOMETRY: - case IOCTL_CDROM_GET_DRIVE_GEOMETRY: - case IOCTL_DISK_UPDATE_DRIVE_SIZE: - { - PDISK_GEOMETRY geometry; - - KdPrint(("ImDisk: IOCTL_DISK/STORAGE_GET_MEDIA_TYPES/DRIVE_GEOMETRY " - "for device %i.\n", device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(device_extension->disk_geometry)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer; - *geometry = device_extension->disk_geometry; - geometry->Cylinders.QuadPart /= geometry->TracksPerCylinder; - geometry->Cylinders.QuadPart /= geometry->SectorsPerTrack; - geometry->Cylinders.QuadPart /= geometry->BytesPerSector; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); - break; - } - - case IOCTL_DISK_GET_LENGTH_INFO: - { - KdPrint(("ImDisk: IOCTL_DISK_GET_LENGTH_INFO for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(GET_LENGTH_INFORMATION)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - ((PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer)-> - Length.QuadPart = - device_extension->disk_geometry.Cylinders.QuadPart; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); - - break; - } - - case IOCTL_DISK_GET_PARTITION_INFO: - { - PPARTITION_INFORMATION partition_information; - - KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(PARTITION_INFORMATION)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - partition_information = - (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - partition_information->StartingOffset.QuadPart = - (LONGLONG)device_extension->disk_geometry.BytesPerSector * - device_extension->disk_geometry.SectorsPerTrack; - partition_information->PartitionLength = - device_extension->disk_geometry.Cylinders; - partition_information->HiddenSectors = 1; - partition_information->PartitionNumber = 1; - partition_information->PartitionType = PARTITION_HUGE; - partition_information->BootIndicator = FALSE; - partition_information->RecognizedPartition = FALSE; - partition_information->RewritePartition = FALSE; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); - - break; - } - - case IOCTL_DISK_GET_PARTITION_INFO_EX: - { - PPARTITION_INFORMATION_EX partition_information_ex; - - KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO_EX for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(PARTITION_INFORMATION_EX)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - partition_information_ex = - (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer; - - partition_information_ex->PartitionStyle = PARTITION_STYLE_MBR; - partition_information_ex->StartingOffset.QuadPart = - (LONGLONG)device_extension->disk_geometry.BytesPerSector * - device_extension->disk_geometry.SectorsPerTrack; - partition_information_ex->PartitionLength = - device_extension->disk_geometry.Cylinders; - partition_information_ex->PartitionNumber = 1; - partition_information_ex->RewritePartition = FALSE; - partition_information_ex->Mbr.PartitionType = PARTITION_HUGE; - partition_information_ex->Mbr.BootIndicator = FALSE; - partition_information_ex->Mbr.RecognizedPartition = FALSE; - partition_information_ex->Mbr.HiddenSectors = 1; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); - - break; - } - - case IOCTL_DISK_IS_WRITABLE: - { - KdPrint(("ImDisk: IOCTL_DISK_IS_WRITABLE for device %i.\n", - device_extension->device_number)); - - if (!device_extension->read_only) - status = STATUS_SUCCESS; - else - status = STATUS_MEDIA_WRITE_PROTECTED; - - break; - } - - case IOCTL_DISK_MEDIA_REMOVAL: - case IOCTL_STORAGE_MEDIA_REMOVAL: - { - KdPrint(("ImDisk: IOCTL_DISK/STORAGE_MEDIA_REMOVAL for device %i.\n", - device_extension->device_number)); - - status = STATUS_SUCCESS; - break; - } - - case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: - { - PDEVICE_MANAGE_DATA_SET_ATTRIBUTES attrs = - (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES) - Irp->AssociatedIrp.SystemBuffer; - - if ((io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) || - (io_stack->Parameters.DeviceIoControl.InputBufferLength < - (attrs->DataSetRangesOffset + attrs->DataSetRangesLength))) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - if (attrs->Action != DeviceDsmAction_Trim) - { - status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - - status = STATUS_SUCCESS; - - int items = attrs->DataSetRangesLength / - sizeof(DEVICE_DATA_SET_RANGE); - - if (items <= 0) - { - break; - } - - if ((device_extension->use_proxy && !device_extension->proxy_unmap) || - device_extension->vm_disk || - device_extension->awealloc_disk) - { - break; - } - - if (device_extension->use_proxy) - { - status = STATUS_PENDING; - break; - } - - FILE_ZERO_DATA_INFORMATION zerodata = { 0 }; - - PDEVICE_DATA_SET_RANGE range = (PDEVICE_DATA_SET_RANGE) - ((PUCHAR)attrs + attrs->DataSetRangesOffset); - - for (int i = 0; i < items; i++) - { - KdPrint(("ImDisk: Trim request 0x%I64X bytes at 0x%I64X\n", - range[i].LengthInBytes, range[i].StartingOffset)); - - zerodata.FileOffset.QuadPart = range[i].StartingOffset + - device_extension->image_offset.QuadPart; - - zerodata.BeyondFinalZero.QuadPart = zerodata.FileOffset.QuadPart + - range[i].LengthInBytes; - - status = ZwFsControlFile( - device_extension->file_handle, - NULL, - NULL, - NULL, - &Irp->IoStatus, - FSCTL_SET_ZERO_DATA, - &zerodata, - sizeof(zerodata), - NULL, - 0); - - KdPrint(("ImDisk: FSCTL_SET_ZERO_DATA result: 0x%#X\n", status)); - - if (!NT_SUCCESS(status)) - { - break; - } - } - - if (!device_extension->no_file_level_trim) - { - IO_STATUS_BLOCK io_status; - ULONG fltrim_size = FIELD_OFFSET(FILE_LEVEL_TRIM, Ranges) + - (items * sizeof(FILE_LEVEL_TRIM_RANGE)); - - PFILE_LEVEL_TRIM fltrim = (PFILE_LEVEL_TRIM) - ExAllocatePoolWithTag(PagedPool, fltrim_size, POOL_TAG); - - if (fltrim == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - RtlZeroMemory(fltrim, fltrim_size); - - fltrim->NumRanges = items; - for (int i = 0; i < items; i++) - { - KdPrint(("ImDisk: Trim request 0x%I64X bytes at 0x%I64X\n", - range[i].LengthInBytes, range[i].StartingOffset)); - - fltrim->Ranges[i].Length = range[i].LengthInBytes; - fltrim->Ranges[i].Offset = range[i].StartingOffset + - device_extension->image_offset.QuadPart; - } - - status = ZwFsControlFile( - device_extension->file_handle, - NULL, - NULL, - NULL, - &io_status, - FSCTL_FILE_LEVEL_TRIM, - fltrim, - fltrim_size, - NULL, - 0); - - ExFreePoolWithTag(fltrim, POOL_TAG); - - KdPrint(("ImDisk: FSCTL_FILE_LEVEL_TRIM result: %#x\n", status)); - - if (!NT_SUCCESS(status)) - { - device_extension->no_file_level_trim = TRUE; - status = STATUS_SUCCESS; - } - } - - break; - } - - case IOCTL_CDROM_GET_LAST_SESSION: - { - status = STATUS_SUCCESS; - break; - } - - case IOCTL_CDROM_READ_TOC: - { - PCDROM_TOC cdrom_toc; - - KdPrint(("ImDisk: IOCTL_CDROM_READ_TOC for device %i.\n", - device_extension->device_number)); - - if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) - { - status = STATUS_INVALID_DEVICE_REQUEST; - break; - } - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(CDROM_TOC)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - cdrom_toc = (PCDROM_TOC)Irp->AssociatedIrp.SystemBuffer; - - RtlZeroMemory(cdrom_toc, sizeof(CDROM_TOC)); - - cdrom_toc->FirstTrack = 1; - cdrom_toc->LastTrack = 1; - cdrom_toc->TrackData[0].Control = TOC_DATA_TRACK; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(CDROM_TOC); - - break; - } - - case IOCTL_DISK_SET_PARTITION_INFO: - { - KdPrint(("ImDisk: IOCTL_DISK_SET_PARTITION_INFO for device %i.\n", - device_extension->device_number)); - - if (device_extension->read_only) - { - KdPrint(("ImDisk: Attempt to partition read-only image.\n")); - - status = STATUS_MEDIA_WRITE_PROTECTED; - break; - } - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(SET_PARTITION_INFORMATION)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - status = STATUS_SUCCESS; - break; - } - - case IOCTL_DISK_SET_PARTITION_INFO_EX: - { - PSET_PARTITION_INFORMATION_EX partition_information_ex; - - KdPrint(("ImDisk: IOCTL_DISK_SET_PARTITION_INFO_EX for device %i.\n", - device_extension->device_number)); - - if (device_extension->read_only) - { - KdPrint(("ImDisk: Attempt to partition read-only image.\n")); - - status = STATUS_MEDIA_WRITE_PROTECTED; - break; - } - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(SET_PARTITION_INFORMATION_EX)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - partition_information_ex = (PSET_PARTITION_INFORMATION_EX) - Irp->AssociatedIrp.SystemBuffer; - - if (partition_information_ex->PartitionStyle != PARTITION_STYLE_MBR) - { - status = STATUS_UNSUCCESSFUL; - } - else - { - status = STATUS_SUCCESS; - } - - break; - } - - case IOCTL_DISK_VERIFY: - { - PVERIFY_INFORMATION verify_information; - - KdPrint(("ImDisk: IOCTL_DISK_VERIFY for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.InputBufferLength < - sizeof(VERIFY_INFORMATION)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - verify_information = (PVERIFY_INFORMATION) - Irp->AssociatedIrp.SystemBuffer; - - if (device_extension->read_only) - { - KdPrint(("ImDisk: Attempt to verify read-only media.\n")); - - status = STATUS_MEDIA_WRITE_PROTECTED; - break; - } - - if (verify_information->StartingOffset.QuadPart + - verify_information->Length > - device_extension->disk_geometry.Cylinders.QuadPart) - { - KdPrint(("ImDisk: Attempt to verify beyond image size.\n")); - - status = STATUS_NONEXISTENT_SECTOR; - break; - } - - status = STATUS_SUCCESS; - break; - } - - // Ver 1.0.2 does no longer handle IOCTL_STORAGE_GET_DEVICE_NUMBER. - // It was a very ugly attempt to workaround some problems that do not - // seem to exist any longer anyway. The data returned here made no sense - // actually so in order to not risk breaking more things in the future I - // have removed it completely. - /* - case IOCTL_STORAGE_GET_DEVICE_NUMBER: - { - PSTORAGE_DEVICE_NUMBER device_number; - - KdPrint(("ImDisk: IOCTL_STORAGE_GET_DEVICE_NUMBER for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(STORAGE_DEVICE_NUMBER)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - device_number = (PSTORAGE_DEVICE_NUMBER) - Irp->AssociatedIrp.SystemBuffer; - - device_number->DeviceType = DeviceObject->DeviceType; - device_number->DeviceNumber = (ULONG) DeviceObject; - device_number->PartitionNumber = (ULONG) -1; - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); - - break; - } - */ - - case IOCTL_STORAGE_GET_HOTPLUG_INFO: - { - PSTORAGE_HOTPLUG_INFO hotplug_info; - - KdPrint(("ImDisk: IOCTL_STORAGE_GET_HOTPLUG_INFO for device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(STORAGE_HOTPLUG_INFO)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - hotplug_info = (PSTORAGE_HOTPLUG_INFO) - Irp->AssociatedIrp.SystemBuffer; - - hotplug_info->Size = sizeof(STORAGE_HOTPLUG_INFO); - if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - { - hotplug_info->MediaRemovable = TRUE; - hotplug_info->MediaHotplug = TRUE; - hotplug_info->DeviceHotplug = TRUE; - hotplug_info->WriteCacheEnableOverride = FALSE; - } - else - { - hotplug_info->MediaRemovable = FALSE; - hotplug_info->MediaHotplug = FALSE; - hotplug_info->DeviceHotplug = FALSE; - hotplug_info->WriteCacheEnableOverride = FALSE; - } - - status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO); - - break; - } - - /* case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: */ - /* { */ - /* PMOUNTDEV_NAME mountdev_name = Irp->AssociatedIrp.SystemBuffer; */ - /* int chars; */ - - /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME for device %i.\n", */ - /* device_extension->device_number)); */ - - /* if ((io_stack->Parameters.DeviceIoControl.OutputBufferLength == 4) & */ - /* (device_extension->drive_letter != 0)) */ - /* { */ - /* mountdev_name->Name[0] = device_extension->drive_letter; */ - /* mountdev_name->Name[1] = L':'; */ - /* chars = 2; */ - /* } */ - /* else */ - /* chars = */ - /* _snwprintf(mountdev_name->Name, */ - /* (io_stack-> */ - /* Parameters.DeviceIoControl.OutputBufferLength - */ - /* FIELD_OFFSET(MOUNTDEV_NAME, Name)) >> 1, */ - /* IMDISK_DEVICE_BASE_NAME L"%u", */ - /* device_extension->device_number); */ - /* // else */ - /* // chars = */ - /* // _snwprintf(mountdev_name->Name, */ - /* // (io_stack-> */ - /* // Parameters.DeviceIoControl.OutputBufferLength - */ - /* // FIELD_OFFSET(MOUNTDEV_NAME, Name)) >> 1, */ - /* // L"\\DosDevices\\%wc:", */ - /* // device_extension->drive_letter); */ - - /* if (chars < 0) */ - /* { */ - /* if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= */ - /* FIELD_OFFSET(MOUNTDEV_NAME, Name) + */ - /* sizeof(mountdev_name->NameLength)) */ - /* mountdev_name->NameLength = sizeof(IMDISK_DEVICE_BASE_NAME) + */ - /* 20; */ - - /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME overflow, " */ - /* "buffer length %u, returned %i.\n", */ - /* io_stack->Parameters.DeviceIoControl.OutputBufferLength, */ - /* chars)); */ - - /* status = STATUS_BUFFER_OVERFLOW; */ - - /* if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= */ - /* sizeof(MOUNTDEV_NAME)) */ - /* Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); */ - - /* break; */ - /* } */ - - /* mountdev_name->NameLength = (USHORT) chars << 1; */ - - /* status = STATUS_SUCCESS; */ - /* Irp->IoStatus.Information = */ - /* FIELD_OFFSET(MOUNTDEV_NAME, Name) + mountdev_name->NameLength; */ - - /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returning %ws, " */ - /* "length %u total %u.\n", */ - /* mountdev_name->Name, mountdev_name->NameLength, */ - /* Irp->IoStatus.Information)); */ - - /* break; */ - /* } */ - - default: - { - KdPrint(("ImDisk: Unknown IOCTL %#x.\n", - io_stack->Parameters.DeviceIoControl.IoControlCode)); - - status = STATUS_INVALID_DEVICE_REQUEST; - } - } - - if (status == STATUS_PENDING) - { - IoMarkIrpPending(Irp); - - ImDiskInterlockedInsertTailList(&device_extension->list_head, - &Irp->Tail.Overlay.ListEntry, - &device_extension->list_lock); - - KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); - } - else - { - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - } - - return status; -} - -NTSTATUS -ImDiskFlushBuffers(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - io_stack = IoGetCurrentIrpStackLocation(Irp); - - // The control device cannot receive flush dispatch. - if (DeviceObject == ImDiskCtlDevice) - { - KdPrint(("ImDisk: flush function %#x invalid for control device.\n", - io_stack->MinorFunction)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - return status; - } - - KdPrint(("ImDisk: Device %i received flush function %#x IRP %p.\n", - device_extension->device_number, - io_stack->MinorFunction, - Irp)); - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: flush dispatch on device %i that is being removed.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if (device_extension->read_only) - { - status = STATUS_MEDIA_WRITE_PROTECTED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - if (device_extension->use_proxy | device_extension->vm_disk) - { - Irp->IoStatus.Status = STATUS_SUCCESS; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return STATUS_SUCCESS; - } - else if (device_extension->parallel_io) - { - return ImDiskReadWriteLowerDevice(Irp, device_extension); - } - else - { - IoMarkIrpPending(Irp); - - ImDiskInterlockedInsertTailList(&device_extension->list_head, - &Irp->Tail.Overlay.ListEntry, - &device_extension->list_lock); - - KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); - - return STATUS_PENDING; - } -} - -NTSTATUS -ImDiskQueryInformation(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - io_stack = IoGetCurrentIrpStackLocation(Irp); - - KdPrint2(("ImDisk: QueryInformation: %u.\n", - io_stack->Parameters.QueryFile.FileInformationClass)); - - // The control device cannot receive PnP dispatch. - if (DeviceObject == ImDiskCtlDevice) - { - KdPrint(("ImDisk: QueryInformation function %#x invalid for control device.\n", - io_stack->Parameters.QueryFile.FileInformationClass)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - return status; - } - - KdPrint(("ImDisk: Device %i received QueryInformation function %#x IRP %p.\n", - device_extension->device_number, - io_stack->Parameters.QueryFile.FileInformationClass, - Irp)); - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: QueryInformation dispatch on device %i that is being removed.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, - io_stack->Parameters.QueryFile.Length); - - switch (io_stack->Parameters.QueryFile.FileInformationClass) - { - case FileStandardInformation: - { - PFILE_STANDARD_INFORMATION standard_info = - (PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - if (io_stack->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) - { - Irp->IoStatus.Information = 0; - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - standard_info->AllocationSize = - standard_info->EndOfFile = device_extension->disk_geometry.Cylinders; - - Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION); - status = STATUS_SUCCESS; - break; - } - - case FilePositionInformation: - { - PFILE_POSITION_INFORMATION position_info = - (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - if (io_stack->Parameters.QueryFile.Length < - sizeof(FILE_POSITION_INFORMATION)) - { - status = STATUS_BUFFER_TOO_SMALL; - break; - } - - if (io_stack->FileObject != NULL) - { - position_info->CurrentByteOffset = - io_stack->FileObject->CurrentByteOffset; - } - - Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION); - - status = STATUS_SUCCESS; - break; - } - - default: - KdPrint(("ImDisk: Unknown QueryInformation function %#x.\n", - io_stack->Parameters.QueryFile.FileInformationClass)); - - status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Information = 0; - } - - if (status == STATUS_PENDING) - { - IoMarkIrpPending(Irp); - - ImDiskInterlockedInsertTailList(&device_extension->list_head, - &Irp->Tail.Overlay.ListEntry, - &device_extension->list_lock); - - KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); - } - else - { - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - } - - return status; -} - -NTSTATUS -ImDiskSetInformation(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - io_stack = IoGetCurrentIrpStackLocation(Irp); - - KdPrint2(("ImDisk: SetInformation: %u.\n", - io_stack->Parameters.SetFile.FileInformationClass)); - - // The control device cannot receive PnP dispatch. - if (DeviceObject == ImDiskCtlDevice) - { - KdPrint(("ImDisk: SetInformation function %#x invalid for control device.\n", - io_stack->Parameters.SetFile.FileInformationClass)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - return status; - } - - KdPrint(("ImDisk: Device %i received SetInformation function %#x IRP %p.\n", - device_extension->device_number, - io_stack->Parameters.SetFile.FileInformationClass, - Irp)); - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: SetInformation dispatch on device %i that is being removed.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - switch (io_stack->Parameters.SetFile.FileInformationClass) - { - case FilePositionInformation: - { - PFILE_POSITION_INFORMATION position_info = - (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - - if (io_stack->Parameters.SetFile.Length < - sizeof(FILE_POSITION_INFORMATION)) - { - status = STATUS_INVALID_PARAMETER; - break; - } - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset = - position_info->CurrentByteOffset; - } - - status = STATUS_SUCCESS; - break; - } - - case FileBasicInformation: - case FileDispositionInformation: - case FileValidDataLengthInformation: - status = STATUS_SUCCESS; - break; - - default: - { - KdPrint(("ImDisk: Unknown SetInformation function %#x.\n", - io_stack->Parameters.SetFile.FileInformationClass)); - - status = STATUS_INVALID_DEVICE_REQUEST; - } - } - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - KdPrint2(("ImDisk: SetFile.FileInformationClass %u result status %#x\n", - io_stack->Parameters.SetFile.FileInformationClass, status)); - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; -} - -NTSTATUS -ImDiskDispatchPnP(IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp) -{ - PDEVICE_EXTENSION device_extension; - PIO_STACK_LOCATION io_stack; - NTSTATUS status; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - - io_stack = IoGetCurrentIrpStackLocation(Irp); - - // The control device cannot receive PnP dispatch. - if (DeviceObject == ImDiskCtlDevice) - { - KdPrint(("ImDisk: PnP function %#x invalid for control device.\n", - io_stack->MinorFunction)); - - status = STATUS_INVALID_DEVICE_REQUEST; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - KdPrint(("ImDisk: Device %i received PnP function %#x IRP %p.\n", - device_extension->device_number, - io_stack->MinorFunction, - Irp)); - - if (KeReadStateEvent(&device_extension->terminate_thread) != 0) - { - KdPrint(("ImDisk: PnP dispatch on device %i that is being removed.\n", - device_extension->device_number)); - - status = STATUS_DEVICE_REMOVED; - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = 0; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - - return status; - } - - switch (io_stack->MinorFunction) - { - case IRP_MN_DEVICE_USAGE_NOTIFICATION: - KdPrint(("ImDisk: Device %i got IRP_MN_DEVICE_USAGE_NOTIFICATION.\n", - device_extension->device_number)); - - switch (io_stack->Parameters.UsageNotification.Type) - { - case DeviceUsageTypePaging: - case DeviceUsageTypeDumpFile: - if (device_extension->read_only) - status = STATUS_MEDIA_WRITE_PROTECTED; - else - if (io_stack->Parameters.UsageNotification.InPath == TRUE) - { - (VOID)MmLockPagableCodeSection((PVOID)(ULONG_PTR)ImDiskDeviceThread); - } - - IoAdjustPagingPathCount - (&device_extension->special_file_count, - io_stack->Parameters.UsageNotification.InPath); - status = STATUS_SUCCESS; - - break; - - default: - status = STATUS_NOT_SUPPORTED; - } - - break; - - default: - KdPrint(("ImDisk: Unknown PnP function %#x.\n", - io_stack->MinorFunction)); - - status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Information = 0; - } - - if (status == STATUS_PENDING) - { - IoMarkIrpPending(Irp); - - ImDiskInterlockedInsertTailList(&device_extension->list_head, - &Irp->Tail.Overlay.ListEntry, - &device_extension->list_lock); - - KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); - } - else - { - Irp->IoStatus.Status = status; - - IoCompleteRequest(Irp, IO_NO_INCREMENT); - } - - return status; -} - -VOID -ImDiskDeviceThread(IN PVOID Context) -{ - PDEVICE_THREAD_DATA device_thread_data; - PDEVICE_OBJECT device_object; - PDEVICE_EXTENSION device_extension; - LARGE_INTEGER time_out; - BOOLEAN system_drive_letter; - - ASSERT(Context != NULL); - - KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); - - device_thread_data = (PDEVICE_THREAD_DATA)Context; - - system_drive_letter = !device_thread_data->caller_waiting; - - // This is in case this thread is created by - // ImDiskAddVirtualDiskAfterInitialization() when called from DriverEntry(). - // That indicates that no-one is waiting for us to return any status - // in the device_thread_data members and that there is no-one freeing the - // init structures after we are finished with them. - // It also means that we need to wait for the control device to get ready for - // I/O (in case a proxy or something need to call this driver during device - // initialization). - while (ImDiskCtlDevice->Flags & DO_DEVICE_INITIALIZING) - { - LARGE_INTEGER wait_time; - - KdPrint2(("ImDisk: Driver still initializing, waiting 100 ms...\n")); - - wait_time.QuadPart = -1000000; - KeDelayExecutionThread(KernelMode, FALSE, &wait_time); - } - - device_thread_data->status = ImDiskCreateDevice( - device_thread_data->driver_object, - device_thread_data->create_data, - device_thread_data->client_thread, - &device_object); - - if (!NT_SUCCESS(device_thread_data->status)) - { - if (device_thread_data->caller_waiting) - KeSetEvent(&device_thread_data->created_event, (KPRIORITY)0, FALSE); - else - { - ExFreePoolWithTag(device_thread_data->create_data, POOL_TAG); - ExFreePoolWithTag(device_thread_data, POOL_TAG); - } - - ImDiskLogError((device_thread_data->driver_object, - 0, - 0, - NULL, - 0, - 1000, - device_thread_data->status, - 102, - device_thread_data->status, - 0, - 0, - NULL, - L"Error creating virtual disk.")); - - PsTerminateSystemThread(STATUS_SUCCESS); - } - - // Now we are done with initialization. Let the one that asks us to create - // this device now that, or if no-one left there, clean up init structures - // here. - if (device_thread_data->caller_waiting) - KeSetEvent(&device_thread_data->created_event, (KPRIORITY)0, FALSE); - else - { - ImDiskCreateDriveLetter(device_thread_data->create_data->DriveLetter, - device_thread_data->create_data->DeviceNumber); - - ExFreePoolWithTag(device_thread_data->create_data, POOL_TAG); - ExFreePoolWithTag(device_thread_data, POOL_TAG); - } - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - KdPrint(("ImDisk: Device thread initialized. (flags=%#x)\n", - device_object->Flags)); - - device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension; - - time_out.QuadPart = -1000000; - - // If this is a VM backed disk that should be pre-loaded with an image file - // we have to load the contents of that file now before entering the service - // loop. - if (device_extension->vm_disk && (device_extension->file_handle != NULL)) - { - LARGE_INTEGER byte_offset = device_extension->image_offset; - IO_STATUS_BLOCK io_status; - NTSTATUS status; -#ifdef _WIN64 - SIZE_T disk_size = device_extension->disk_geometry.Cylinders.QuadPart; -#else - SIZE_T disk_size = device_extension->disk_geometry.Cylinders.LowPart; -#endif - - KdPrint(("ImDisk: Reading image file into vm disk buffer.\n")); - - status = - ImDiskSafeReadFile(device_extension->file_handle, - &io_status, - device_extension->image_buffer, - disk_size, - &byte_offset); - - ZwClose(device_extension->file_handle); - device_extension->file_handle = NULL; - - // Failure to read pre-load image is now considered a fatal error - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Failed to read image file (%#x).\n", status)); - - ImDiskRemoveVirtualDisk(device_object); - } - else - { - KdPrint(("ImDisk: Image loaded successfully.\n")); - - if (device_extension->byte_swap) - ImDiskByteSwapBuffer(device_extension->image_buffer, - disk_size); - } - } - - for (;;) - { - PIRP irp; - PIO_STACK_LOCATION io_stack; - PLIST_ENTRY request; - - KeClearEvent(&device_extension->request_event); - - request = - ImDiskInterlockedRemoveHeadList(&device_extension->list_head, - &device_extension->list_lock); - - if (request == NULL) - { - PWCHAR symlink_name_buffer; - NTSTATUS status; - PKEVENT wait_objects[] = { - &device_extension->request_event, - &device_extension->terminate_thread - }; - - KdPrint2(("ImDisk: No pending requests. Waiting.\n")); - - status = KeWaitForMultipleObjects(sizeof(wait_objects) / - sizeof(*wait_objects), - (PVOID*)wait_objects, - WaitAny, - Executive, - KernelMode, - FALSE, - NULL, - NULL); - - // While pending requests in queue, service them before terminating - // thread. - if (KeReadStateEvent(&device_extension->request_event)) - continue; - - KdPrint(("ImDisk: Device %i thread is shutting down.\n", - device_extension->device_number)); - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - if (device_extension->drive_letter != 0) - if (system_drive_letter) - ImDiskRemoveDriveLetter(device_extension->drive_letter); - - ImDiskCloseProxy(&device_extension->proxy); - - if (device_extension->last_io_data != NULL) - { - ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); - device_extension->last_io_data = NULL; - } - - if (device_extension->vm_disk) - { - SIZE_T free_size = 0; - if (device_extension->image_buffer != NULL) - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&device_extension->image_buffer, - &free_size, MEM_RELEASE); - - device_extension->image_buffer = NULL; - } - else - { - if (device_extension->file_handle != NULL) - ZwClose(device_extension->file_handle); - - device_extension->file_handle = NULL; - - if (device_extension->file_object != NULL) - ObDereferenceObject(device_extension->file_object); - - device_extension->file_object = NULL; - } - - if (device_extension->file_name.Buffer != NULL) - { - ExFreePoolWithTag(device_extension->file_name.Buffer, POOL_TAG); - device_extension->file_name.Buffer = NULL; - device_extension->file_name.Length = 0; - device_extension->file_name.MaximumLength = 0; - } - - // If ReferenceCount is not zero, this device may have outstanding - // IRP-s or otherwise unfinished things to do. Let IRP-s be done by - // continuing this dispatch loop until ReferenceCount is zero. -#pragma warning(suppress: 28175) - if (device_object->ReferenceCount != 0) - { -#if DBG -#pragma warning(suppress: 28175) - LONG ref_count = device_object->ReferenceCount; - DbgPrint("ImDisk: Device %i has %i references. Waiting.\n", - device_extension->device_number, - ref_count); -#endif - - KeDelayExecutionThread(KernelMode, FALSE, &time_out); - - time_out.LowPart <<= 4; - continue; - } - - KdPrint(("ImDisk: Deleting symlink for device %i.\n", - device_extension->device_number)); - - symlink_name_buffer = (PWCHAR) - ExAllocatePoolWithTag(PagedPool, - MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR), POOL_TAG); - - if (symlink_name_buffer == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - UNICODE_STRING symlink_name; - - _snwprintf(symlink_name_buffer, MAXIMUM_FILENAME_LENGTH - 1, - IMDISK_SYMLNK_NATIVE_BASE_NAME L"%u", device_extension->device_number); - symlink_name_buffer[MAXIMUM_FILENAME_LENGTH - 1] = 0; - - RtlInitUnicodeString(&symlink_name, symlink_name_buffer); - - KdPrint(("ImDisk: Deleting symlink '%ws'.\n", symlink_name_buffer)); - - status = IoDeleteSymbolicLink(&symlink_name); - - ExFreePoolWithTag(symlink_name_buffer, POOL_TAG); - } - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk: Cannot delete symlink. (%#x)\n", status)); - } - - KdPrint(("ImDisk: Deleting device object %i.\n", - device_extension->device_number)); - - IoDeleteDevice(device_object); - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - PsTerminateSystemThread(STATUS_SUCCESS); - } - - irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry); - - io_stack = IoGetCurrentIrpStackLocation(irp); - - switch (io_stack->MajorFunction) - { - case IRP_MJ_FLUSH_BUFFERS: - { - NTSTATUS status; - PIRP image_irp; - KEVENT io_complete_event; - PIO_STACK_LOCATION image_io_stack; - - if (device_extension->file_object == NULL) - { - status = ObReferenceObjectByHandle( - device_extension->file_handle, - FILE_WRITE_ATTRIBUTES | - FILE_WRITE_DATA | - SYNCHRONIZE, - *IoFileObjectType, - KernelMode, - (PVOID*)&device_extension->file_object, - NULL); - - if (!NT_SUCCESS(status)) - { - irp->IoStatus.Status = status; - irp->IoStatus.Information = 0; - - KdPrint(("ImDisk: ObReferenceObjectByHandle failed on image handle: %#x\n", - status)); - - break; - } - - device_extension->dev_object = IoGetRelatedDeviceObject( - device_extension->file_object); - } - - KeInitializeEvent(&io_complete_event, - NotificationEvent, - FALSE); - - image_irp = IoBuildSynchronousFsdRequest( - IRP_MJ_FLUSH_BUFFERS, - device_extension->dev_object, - NULL, - 0, - NULL, - &io_complete_event, - &irp->IoStatus); - - if (image_irp == NULL) - { - KdPrint(("ImDisk: IoBuildSynchronousFsdRequest failed for image object.\n")); - - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - image_io_stack = IoGetNextIrpStackLocation(image_irp); - image_io_stack->FileObject = device_extension->file_object; - - status = IoCallDriver(device_extension->dev_object, image_irp); - - if (status == STATUS_PENDING) - { - KdPrint(("ImDisk: Waiting for IoCallDriver to complete.\n")); - - KeWaitForSingleObject(&io_complete_event, - Executive, - KernelMode, - FALSE, - NULL); - } - else - { - irp->IoStatus.Status = status; - } - - KdPrint(("ImDisk: IoCallDriver result for flush request: %#x\n", - status)); - - break; - } - - case IRP_MJ_READ: - { - PUCHAR system_buffer = - (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, - NormalPagePriority); - LARGE_INTEGER offset; - KLOCK_QUEUE_HANDLE lock_handle; - - if (system_buffer == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - if (device_extension->vm_disk) - { -#ifdef _WIN64 - ULONG_PTR vm_offset = - io_stack->Parameters.Read.ByteOffset.QuadPart; -#else - ULONG_PTR vm_offset = - io_stack->Parameters.Read.ByteOffset.LowPart; -#endif - - RtlCopyMemory(system_buffer, - device_extension->image_buffer + - vm_offset, - io_stack->Parameters.Read.Length); - - irp->IoStatus.Status = STATUS_SUCCESS; - irp->IoStatus.Information = io_stack->Parameters.Read.Length; - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset.QuadPart += - irp->IoStatus.Information; - } - - break; - } - - offset.QuadPart = io_stack->Parameters.Read.ByteOffset.QuadPart + - device_extension->image_offset.QuadPart; - - ImDiskAcquireLock(&device_extension->last_io_lock, &lock_handle); - - if ((device_extension->last_io_data != NULL) & - (device_extension->last_io_length < - io_stack->Parameters.Read.Length)) - { - ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); - device_extension->last_io_data = NULL; - } - - device_extension->last_io_offset = 0; - device_extension->last_io_length = 0; - - ImDiskReleaseLock(&lock_handle); - - if (device_extension->last_io_data == NULL) - device_extension->last_io_data = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, - io_stack->Parameters.Read.Length, - POOL_TAG); - - if (device_extension->last_io_data == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - if (device_extension->use_proxy) - { - irp->IoStatus.Status = - ImDiskReadProxy(&device_extension->proxy, - &irp->IoStatus, - &device_extension->terminate_thread, - device_extension->last_io_data, - io_stack->Parameters.Read.Length, - &offset); - - if (!NT_SUCCESS(irp->IoStatus.Status)) - { - KdPrint(("ImDisk: Read failed on device %i: %#x.\n", - device_extension->device_number, - irp->IoStatus.Status)); - - // If indicating that proxy connection died we can do - // nothing else but remove this device. - // if (irp->IoStatus.Status == STATUS_CONNECTION_RESET) - ImDiskRemoveVirtualDisk(device_object); - - irp->IoStatus.Status = STATUS_DEVICE_REMOVED; - irp->IoStatus.Information = 0; - } - } - else - { - irp->IoStatus.Status = - NtReadFile(device_extension->file_handle, - NULL, - NULL, - NULL, - &irp->IoStatus, - device_extension->last_io_data, - io_stack->Parameters.Read.Length, - &offset, - NULL); - } - - if (NT_SUCCESS(irp->IoStatus.Status)) - { - RtlCopyMemory(system_buffer, device_extension->last_io_data, - irp->IoStatus.Information); - - if (device_extension->byte_swap) - ImDiskByteSwapBuffer(system_buffer, - irp->IoStatus.Information); - - device_extension->last_io_offset = - io_stack->Parameters.Read.ByteOffset.QuadPart; - - device_extension->last_io_length = - (ULONG)irp->IoStatus.Information; - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset.QuadPart += - irp->IoStatus.Information; - } - } - else - { - ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); - device_extension->last_io_data = NULL; - } - - break; - } - - case IRP_MJ_WRITE: - { - PUCHAR system_buffer = - (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, - NormalPagePriority); - LARGE_INTEGER offset; - BOOLEAN set_zero_data = FALSE; - KLOCK_QUEUE_HANDLE lock_handle; - - if (system_buffer == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - if (!device_extension->image_modified) - { - device_extension->image_modified = TRUE; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - } - - if (device_extension->vm_disk) - { -#ifdef _WIN64 - ULONG_PTR vm_offset = - io_stack->Parameters.Write.ByteOffset.QuadPart; -#else - ULONG_PTR vm_offset = - io_stack->Parameters.Write.ByteOffset.LowPart; -#endif - - RtlCopyMemory(device_extension->image_buffer + - vm_offset, - system_buffer, - io_stack->Parameters.Write.Length); - - irp->IoStatus.Status = STATUS_SUCCESS; - irp->IoStatus.Information = io_stack->Parameters.Write.Length; - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset.QuadPart += - irp->IoStatus.Information; - } - - break; - } - - offset.QuadPart = io_stack->Parameters.Write.ByteOffset.QuadPart + - device_extension->image_offset.QuadPart; - - ImDiskAcquireLock(&device_extension->last_io_lock, &lock_handle); - - if ((device_extension->last_io_data != NULL) & - (device_extension->last_io_length < - io_stack->Parameters.Write.Length)) - { - ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); - device_extension->last_io_data = NULL; - } - - device_extension->last_io_offset = 0; - device_extension->last_io_length = 0; - - ImDiskReleaseLock(&lock_handle); - - if (device_extension->last_io_data == NULL) - { - device_extension->last_io_data = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, - io_stack->Parameters.Write.Length, - POOL_TAG); - } - - if (device_extension->last_io_data == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - RtlCopyMemory(device_extension->last_io_data, system_buffer, - io_stack->Parameters.Write.Length); - - if ((device_extension->use_set_zero_data || - (device_extension->use_proxy && - device_extension->proxy_zero)) && - ImDiskIsBufferZero(device_extension->last_io_data, - io_stack->Parameters.Write.Length)) - { - set_zero_data = TRUE; - } - - if ((!set_zero_data) && device_extension->byte_swap) - { - ImDiskByteSwapBuffer(device_extension->last_io_data, - irp->IoStatus.Information); - } - - if (device_extension->use_proxy) - { - if (set_zero_data && device_extension->proxy_zero) - { - DEVICE_DATA_SET_RANGE range; - range.StartingOffset = offset.QuadPart; - range.LengthInBytes = io_stack->Parameters.Write.Length; - - irp->IoStatus.Status = - ImDiskUnmapOrZeroProxy(&device_extension->proxy, - IMDPROXY_REQ_ZERO, - &irp->IoStatus, - &device_extension->terminate_thread, - 1, - &range); - - if (NT_SUCCESS(irp->IoStatus.Status)) - { - irp->IoStatus.Information = - io_stack->Parameters.Write.Length; - } - } - else - { - irp->IoStatus.Status = - ImDiskWriteProxy(&device_extension->proxy, - &irp->IoStatus, - &device_extension->terminate_thread, - device_extension->last_io_data, - io_stack->Parameters.Write.Length, - &offset); - } - - if (!NT_SUCCESS(irp->IoStatus.Status)) - { - KdPrint(("ImDisk: Write failed on device %i: %#x.\n", - device_extension->device_number, - irp->IoStatus.Status)); - - // If indicating that proxy connection died we can do - // nothing else but remove this device. - if (irp->IoStatus.Status == STATUS_CONNECTION_RESET) - ImDiskRemoveVirtualDisk(device_object); - - irp->IoStatus.Status = STATUS_DEVICE_REMOVED; - irp->IoStatus.Information = 0; - } - } - else - { - if (set_zero_data) - { - FILE_ZERO_DATA_INFORMATION zero_data; - zero_data.FileOffset = offset; - zero_data.BeyondFinalZero.QuadPart = offset.QuadPart + - io_stack->Parameters.Write.Length; - - irp->IoStatus.Status = - NtFsControlFile(device_extension->file_handle, - NULL, - NULL, - NULL, - &irp->IoStatus, - FSCTL_SET_ZERO_DATA, - &zero_data, - sizeof(zero_data), - NULL, - 0); - - if (NT_SUCCESS(irp->IoStatus.Status)) - { - KdPrint2(("ImDisk: Zero block set.\n")); - irp->IoStatus.Information = - io_stack->Parameters.Write.Length; - } - else - { - KdPrint(("ImDisk: Volume does not support " - "FSCTL_SET_ZERO_DATA: 0x%#X\n", - irp->IoStatus.Status)); - - irp->IoStatus.Information = 0; - set_zero_data = FALSE; - device_extension->use_set_zero_data = FALSE; - } - } - - if (!set_zero_data) - { - irp->IoStatus.Status = NtWriteFile( - device_extension->file_handle, - NULL, - NULL, - NULL, - &irp->IoStatus, - device_extension->last_io_data, - io_stack->Parameters.Write.Length, - &offset, - NULL); - } - } - - if (NT_SUCCESS(irp->IoStatus.Status)) - { - device_extension->last_io_offset = - io_stack->Parameters.Write.ByteOffset.QuadPart; - device_extension->last_io_length = - io_stack->Parameters.Write.Length; - - if (io_stack->FileObject != NULL) - { - io_stack->FileObject->CurrentByteOffset.QuadPart += - irp->IoStatus.Information; - } - } - else - { - ExFreePoolWithTag(device_extension->last_io_data, POOL_TAG); - device_extension->last_io_data = NULL; - } - - break; - } - - case IRP_MJ_DEVICE_CONTROL: - switch (io_stack->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_DISK_CHECK_VERIFY: - case IOCTL_CDROM_CHECK_VERIFY: - case IOCTL_STORAGE_CHECK_VERIFY: - case IOCTL_STORAGE_CHECK_VERIFY2: - { - PUCHAR buffer; - - buffer = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, 1, POOL_TAG); - - if (buffer == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - if (device_extension->use_proxy) - { - irp->IoStatus.Status = - ImDiskReadProxy(&device_extension->proxy, - &irp->IoStatus, - &device_extension->terminate_thread, - buffer, - 0, - &device_extension->image_offset); - } - else - { - irp->IoStatus.Status = - NtReadFile(device_extension->file_handle, - NULL, - NULL, - NULL, - &irp->IoStatus, - buffer, - 0, - &device_extension->image_offset, - NULL); - } - - ExFreePoolWithTag(buffer, POOL_TAG); - - if (!NT_SUCCESS(irp->IoStatus.Status)) - { - KdPrint(("ImDisk: Verify failed on device %i.\n", - device_extension->device_number)); - - // If indicating that proxy connection died we can do - // nothing else but remove this device. - if (irp->IoStatus.Status == STATUS_CONNECTION_RESET) - ImDiskRemoveVirtualDisk(device_object); - - irp->IoStatus.Status = STATUS_DEVICE_REMOVED; - irp->IoStatus.Information = 0; - break; - } - - KdPrint(("ImDisk: Verify ok on device %i.\n", - device_extension->device_number)); - - if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < - sizeof(ULONG)) - irp->IoStatus.Information = 0; - else - { - *(PULONG)irp->AssociatedIrp.SystemBuffer = - device_extension->media_change_count; - - irp->IoStatus.Information = sizeof(ULONG); - } - - irp->IoStatus.Status = STATUS_SUCCESS; - break; - } - - case IOCTL_IMDISK_IOCTL_PASS_THROUGH: - case IOCTL_IMDISK_FSCTL_PASS_THROUGH: - { - NTSTATUS status; - ULONG ctl_code = *(PULONG)irp->AssociatedIrp.SystemBuffer; - PVOID in_buffer = (PUCHAR)irp->AssociatedIrp.SystemBuffer + - sizeof(ULONG); - PVOID out_buffer = irp->AssociatedIrp.SystemBuffer; - ULONG in_size = - io_stack->Parameters.DeviceIoControl.InputBufferLength - - sizeof(ULONG); - ULONG out_size = - io_stack->Parameters.DeviceIoControl.OutputBufferLength; - - UCHAR func; - PIRP lower_irp; - KEVENT event; - PIO_STACK_LOCATION lower_io_stack; - - if (device_extension->file_object == NULL) - { - status = ObReferenceObjectByHandle( - device_extension->file_handle, - FILE_WRITE_ATTRIBUTES | - FILE_WRITE_DATA | - SYNCHRONIZE, - *IoFileObjectType, - KernelMode, - (PVOID*)&device_extension->file_object, - NULL); - - if (!NT_SUCCESS(status)) - { - irp->IoStatus.Status = status; - irp->IoStatus.Information = 0; - - KdPrint(("ImDisk: ObReferenceObjectByHandle failed on image handle: %#x\n", - status)); - - break; - } - - device_extension->dev_object = IoGetRelatedDeviceObject( - device_extension->file_object); - } - - if (io_stack->MajorFunction == IOCTL_IMDISK_FSCTL_PASS_THROUGH) - { - KdPrint(("ImDisk: IOCTL_IMDISK_FSCTL_PASS_THROUGH for device %i control code %#x.\n", - device_extension->device_number, ctl_code)); - - func = IRP_MJ_FILE_SYSTEM_CONTROL; - } - else - { - KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL_PASS_THROUGH for device %i control code %#x.\n", - device_extension->device_number, ctl_code)); - - func = IRP_MJ_DEVICE_CONTROL; - } - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - lower_irp = IoBuildDeviceIoControlRequest( - ctl_code, - device_extension->dev_object, - in_buffer, - in_size, - out_buffer, - out_size, - FALSE, - &event, - &irp->IoStatus); - - if (lower_irp == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - irp->IoStatus.Information = 0; - break; - } - - lower_irp->RequestorMode = irp->RequestorMode; - - lower_io_stack = IoGetNextIrpStackLocation(lower_irp); - lower_io_stack->FileObject = device_extension->file_object; - lower_io_stack->MajorFunction = func; - - status = IoCallDriver(device_extension->dev_object, - lower_irp); - - if (status == STATUS_PENDING) - { - KeWaitForSingleObject(&event, Executive, KernelMode, - FALSE, NULL); - } - else - { - irp->IoStatus.Status = status; - } - - KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL/FSCTL_PASS_THROUGH for device %i control code %#x result status %#x.\n", - device_extension->device_number, ctl_code, status)); - - break; - } - -#ifdef INCLUDE_VFD_ORIGIN - - case IOCTL_DISK_FORMAT_TRACKS: - case IOCTL_DISK_FORMAT_TRACKS_EX: - { - NTSTATUS status = - ImDiskFloppyFormat(device_extension, irp); - - if (!NT_SUCCESS(status)) - { - // If indicating that proxy connection died we can do - // nothing else but remove this device. - if (status == STATUS_CONNECTION_RESET) - ImDiskRemoveVirtualDisk(device_object); - - irp->IoStatus.Status = STATUS_DEVICE_REMOVED; - irp->IoStatus.Information = 0; - break; - } - - irp->IoStatus.Information = 0; - irp->IoStatus.Status = status; - break; - } - -#endif // INCLUDE_VFD_ORIGIN - - case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: - { - irp->IoStatus.Information = 0; - - PDEVICE_MANAGE_DATA_SET_ATTRIBUTES attrs = - (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES) - irp->AssociatedIrp.SystemBuffer; - - irp->IoStatus.Status = STATUS_SUCCESS; - - int items = attrs->DataSetRangesLength / - sizeof(DEVICE_DATA_SET_RANGE); - - PDEVICE_DATA_SET_RANGE range = (PDEVICE_DATA_SET_RANGE) - ExAllocatePoolWithTag(PagedPool, - items * sizeof(DEVICE_DATA_SET_RANGE), POOL_TAG); - - if (range == NULL) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - RtlCopyMemory(range, (PUCHAR)attrs + attrs->DataSetRangesOffset, - items * sizeof(DEVICE_DATA_SET_RANGE)); - - if (device_extension->image_offset.QuadPart > 0) - { - for (int i = 0; i < items; i++) - { - range[i].StartingOffset += - device_extension->image_offset.QuadPart; - } - } - - irp->IoStatus.Status = ImDiskUnmapOrZeroProxy( - &device_extension->proxy, - IMDPROXY_REQ_UNMAP, - &irp->IoStatus, - &device_extension->terminate_thread, - items, - range); - - ExFreePoolWithTag(range, POOL_TAG); - - KdPrint(("ImDisk: Unmap result on device %i: %#x.\n", - device_extension->device_number, - irp->IoStatus.Status)); - - break; - } - - case IOCTL_DISK_GROW_PARTITION: - { - NTSTATUS status; - FILE_END_OF_FILE_INFORMATION new_size; - FILE_STANDARD_INFORMATION file_standard_information; - - new_size.EndOfFile.QuadPart = - device_extension->disk_geometry.Cylinders.QuadPart + - ((PDISK_GROW_PARTITION)irp->AssociatedIrp.SystemBuffer)-> - BytesToGrow.QuadPart; - - if (device_extension->vm_disk) - { - PVOID new_image_buffer = NULL; - SIZE_T free_size = 0; -#ifdef _WIN64 - ULONG_PTR old_size = - device_extension->disk_geometry.Cylinders.QuadPart; - SIZE_T max_size = new_size.EndOfFile.QuadPart; -#else - ULONG_PTR old_size = - device_extension->disk_geometry.Cylinders.LowPart; - SIZE_T max_size = new_size.EndOfFile.LowPart; - - // A vm type disk cannot be extended to a larger size than - // 2 GB. - if (new_size.EndOfFile.QuadPart & 0xFFFFFFFF80000000) - { - irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - irp->IoStatus.Information = 0; - break; - } -#endif // _WIN64 - - status = ZwAllocateVirtualMemory(NtCurrentProcess(), - &new_image_buffer, - 0, - &max_size, - MEM_COMMIT, - PAGE_READWRITE); - - if (!NT_SUCCESS(status)) - { - status = STATUS_NO_MEMORY; - irp->IoStatus.Status = status; - irp->IoStatus.Information = 0; - break; - } - - RtlCopyMemory(new_image_buffer, - device_extension->image_buffer, - old_size); - - ZwFreeVirtualMemory(NtCurrentProcess(), - (PVOID*)&device_extension->image_buffer, - &free_size, - MEM_RELEASE); - - device_extension->image_buffer = (PUCHAR)new_image_buffer; - device_extension->disk_geometry.Cylinders = - new_size.EndOfFile; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - irp->IoStatus.Information = 0; - irp->IoStatus.Status = STATUS_SUCCESS; - break; - } - - // For proxy-type disks the new size is just accepted and - // that's it. - if (device_extension->use_proxy) - { - device_extension->disk_geometry.Cylinders = - new_size.EndOfFile; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - irp->IoStatus.Information = 0; - irp->IoStatus.Status = STATUS_SUCCESS; - break; - } - - // Image file backed disks left to do. - - // For disks with offset, refuse to extend size. Otherwise we - // could break compatibility with the header data we have - // skipped and we don't know about. - if (device_extension->image_offset.QuadPart != 0) - { - irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - irp->IoStatus.Information = 0; - break; - } - - status = - ZwQueryInformationFile(device_extension->file_handle, - &irp->IoStatus, - &file_standard_information, - sizeof file_standard_information, - FileStandardInformation); - - if (!NT_SUCCESS(status)) - { - irp->IoStatus.Status = status; - irp->IoStatus.Information = 0; - break; - } - - if (file_standard_information.EndOfFile.QuadPart >= - new_size.EndOfFile.QuadPart) - { - device_extension->disk_geometry.Cylinders = - new_size.EndOfFile; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - - irp->IoStatus.Information = 0; - irp->IoStatus.Status = STATUS_SUCCESS; - break; - } - - // For other, fixed file-backed disks we need to adjust the - // physical file size. - - status = ZwSetInformationFile(device_extension->file_handle, - &irp->IoStatus, - &new_size, - sizeof new_size, - FileEndOfFileInformation); - - if (NT_SUCCESS(status)) - { - device_extension->disk_geometry.Cylinders = - new_size.EndOfFile; - - // Fire refresh event - if (RefreshEvent != NULL) - KePulseEvent(RefreshEvent, 0, FALSE); - } - - irp->IoStatus.Information = 0; - irp->IoStatus.Status = status; - break; - } - - default: - irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR; - } - break; - - default: - irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR; - } - - IoCompleteRequest(irp, - NT_SUCCESS(irp->IoStatus.Status) ? - IO_DISK_INCREMENT : IO_NO_INCREMENT); - } -} - -// -// Reads in a loop up to "Length" or until eof reached. -// -NTSTATUS -ImDiskSafeReadFile(IN HANDLE FileHandle, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID Buffer, - IN SIZE_T Length, - IN PLARGE_INTEGER Offset) -{ - NTSTATUS status = STATUS_SUCCESS; - SIZE_T length_done = 0; - PUCHAR intermediate_buffer = NULL; - ULONG request_length; - - ASSERT(FileHandle != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(Buffer != NULL); - - if (Length > (8UL << 20)) - { - request_length = (8UL << 20); - } - else - { - request_length = (ULONG)Length; - } - - while (length_done < Length) - { - SIZE_T LongRequestLength = Length - length_done; - if (LongRequestLength < request_length) - { - request_length = (ULONG)LongRequestLength; - } - - for (;;) - { - LARGE_INTEGER current_file_offset; - - current_file_offset.QuadPart = Offset->QuadPart + length_done; - - if (intermediate_buffer == NULL) - { - intermediate_buffer = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, - request_length, - POOL_TAG); - - if (intermediate_buffer == NULL) - { - DbgPrint("ImDisk: ImDiskSafeReadFile: Insufficient paged pool to allocate " - "intermediate buffer (%u bytes).\n", request_length); - - IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - } - - status = ZwReadFile(FileHandle, - NULL, - NULL, - NULL, - IoStatusBlock, - intermediate_buffer, - request_length, - ¤t_file_offset, - NULL); - - if (((status == STATUS_INSUFFICIENT_RESOURCES) | - (status == STATUS_INVALID_BUFFER_SIZE) | - (status == STATUS_INVALID_PARAMETER)) & - (request_length >= 2048)) - { - ExFreePoolWithTag(intermediate_buffer, POOL_TAG); - intermediate_buffer = NULL; - - DbgPrint("ImDisk: ImDiskSafeReadFile: ZwReadFile error reading " - "%u bytes. Retrying with smaller read size. (Status 0x%X)\n", - request_length, - status); - - request_length >>= 2; - - continue; - } - - if (!NT_SUCCESS(status)) - { - DbgPrint("ImDisk: ImDiskSafeReadFile: ZwReadFile error reading " - "%u bytes. (Status 0x%X)\n", - request_length, - status); - - break; - } - - RtlCopyMemory((PUCHAR)Buffer + length_done, intermediate_buffer, - IoStatusBlock->Information); - - break; - } - - if (!NT_SUCCESS(status)) - { - IoStatusBlock->Information = length_done; - break; - } - - if (IoStatusBlock->Information == 0) - { - DbgPrint("ImDisk: ImDiskSafeReadFile: IoStatusBlock->Information == 0, " - "returning STATUS_CONNECTION_RESET.\n"); - - status = STATUS_CONNECTION_RESET; - break; - } - - KdPrint(("ImDisk: ImDiskSafeReadFile: Done %u bytes.\n", - (ULONG)IoStatusBlock->Information)); - - length_done += IoStatusBlock->Information; - } - - if (intermediate_buffer != NULL) - { - ExFreePoolWithTag(intermediate_buffer, POOL_TAG); - intermediate_buffer = NULL; - } - - if (!NT_SUCCESS(status)) - { - DbgPrint("ImDisk: ImDiskSafeReadFile: Error return " - "(Status 0x%X)\n", status); - } - else - { - KdPrint(("ImDisk: ImDiskSafeReadFile: Successful.\n")); - } - - IoStatusBlock->Status = status; - IoStatusBlock->Information = length_done; - return status; -} - -NTSTATUS -ImDiskSafeIOStream(IN PFILE_OBJECT FileObject, - IN UCHAR MajorFunction, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent, - IN OUT PVOID Buffer, - IN ULONG Length) -{ - NTSTATUS status; - ULONG length_done = 0; - KEVENT io_complete_event; - PIO_STACK_LOCATION io_stack; - LARGE_INTEGER offset = { 0 }; - PKEVENT wait_object[] = { - &io_complete_event, - CancelEvent - }; - ULONG number_of_wait_objects = CancelEvent != NULL ? 2 : 1; - - KdPrint2(("ImDiskSafeIOStream: FileObject=%#x, MajorFunction=%#x, " - "IoStatusBlock=%#x, Buffer=%#x, Length=%#x.\n", - FileObject, MajorFunction, IoStatusBlock, Buffer, Length)); - - ASSERT(FileObject != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(Buffer != NULL); - - KeInitializeEvent(&io_complete_event, - NotificationEvent, - FALSE); - - while (length_done < Length) - { - ULONG RequestLength = Length - length_done; - - do - { - PIRP irp; - PDEVICE_OBJECT device_object = IoGetRelatedDeviceObject(FileObject); - - KdPrint2(("ImDiskSafeIOStream: Building IRP...\n")); - -#pragma warning(suppress: 6102) - irp = IoBuildSynchronousFsdRequest(MajorFunction, - device_object, - (PUCHAR)Buffer + length_done, - RequestLength, - &offset, - &io_complete_event, - IoStatusBlock); - - if (irp == NULL) - { - KdPrint(("ImDiskSafeIOStream: Error building IRP.\n")); - - IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - KdPrint2(("ImDiskSafeIOStream: Built IRP=%#x.\n", irp)); - - io_stack = IoGetNextIrpStackLocation(irp); - io_stack->FileObject = FileObject; - - KdPrint2(("ImDiskSafeIOStream: MajorFunction=%#x, Length=%#x\n", - io_stack->MajorFunction, - io_stack->Parameters.Read.Length)); - - KeClearEvent(&io_complete_event); - - status = IoCallDriver(device_object, irp); - - if (status == STATUS_PENDING) - { - status = KeWaitForMultipleObjects(number_of_wait_objects, - (PVOID*)wait_object, - WaitAny, - Executive, - KernelMode, - FALSE, - NULL, - NULL); - - if (KeReadStateEvent(&io_complete_event) == 0) - { - IoCancelIrp(irp); - KeWaitForSingleObject(&io_complete_event, - Executive, - KernelMode, - FALSE, - NULL); - } - } - else if (!NT_SUCCESS(status)) - break; - - status = IoStatusBlock->Status; - - KdPrint2(("ImDiskSafeIOStream: IRP %#x completed. Status=%#x.\n", - irp, IoStatusBlock->Status)); - - RequestLength >>= 1; - } while ((status == STATUS_INVALID_BUFFER_SIZE) | - (status == STATUS_INVALID_PARAMETER)); - - if (!NT_SUCCESS(status)) - { - KdPrint2(("ImDiskSafeIOStream: I/O failed. Status=%#x.\n", status)); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - KdPrint2(("ImDiskSafeIOStream: I/O done. Status=%#x. Length=%#x\n", - status, IoStatusBlock->Information)); - - if (IoStatusBlock->Information == 0) - { - IoStatusBlock->Status = STATUS_CONNECTION_RESET; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - length_done += (ULONG)IoStatusBlock->Information; - } - - KdPrint2(("ImDiskSafeIOStream: I/O complete.\n")); - - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; -} - -VOID -ImDiskCloseProxy(IN PPROXY_CONNECTION Proxy) -{ - ASSERT(Proxy != NULL); - - switch (Proxy->connection_type) - { - case PROXY_CONNECTION::PROXY_CONNECTION_DEVICE: - if (Proxy->device != NULL) - ObDereferenceObject(Proxy->device); - - Proxy->device = NULL; - break; - - case PROXY_CONNECTION::PROXY_CONNECTION_SHM: - if ((Proxy->request_event != NULL) & - (Proxy->response_event != NULL) & - (Proxy->shared_memory != NULL)) - { - *(ULONGLONG*)Proxy->shared_memory = IMDPROXY_REQ_CLOSE; - KeSetEvent(Proxy->request_event, (KPRIORITY)0, FALSE); - } - - if (Proxy->request_event_handle != NULL) - { - ZwClose(Proxy->request_event_handle); - Proxy->request_event_handle = NULL; - } - - if (Proxy->response_event_handle != NULL) - { - ZwClose(Proxy->response_event_handle); - Proxy->response_event_handle = NULL; - } - - if (Proxy->request_event != NULL) - { - ObDereferenceObject(Proxy->request_event); - Proxy->request_event = NULL; - } - - if (Proxy->response_event != NULL) - { - ObDereferenceObject(Proxy->response_event); - Proxy->response_event = NULL; - } - - if (Proxy->shared_memory != NULL) - { - ZwUnmapViewOfSection(NtCurrentProcess(), Proxy->shared_memory); - Proxy->shared_memory = NULL; - } - - break; - } -} - -NTSTATUS -ImDiskCallProxy(IN PPROXY_CONNECTION Proxy, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN PVOID RequestHeader, - IN ULONG RequestHeaderSize, - IN PVOID RequestData, - IN ULONG RequestDataSize, - IN OUT PVOID ResponseHeader, - IN ULONG ResponseHeaderSize, - IN OUT PVOID ResponseData, - IN ULONG ResponseDataBufferSize, - IN ULONG *ResponseDataSize) -{ - NTSTATUS status; - - ASSERT(Proxy != NULL); - - switch (Proxy->connection_type) - { - case PROXY_CONNECTION::PROXY_CONNECTION_DEVICE: - { - PUCHAR io_buffer = NULL; - PUCHAR temp_buffer = NULL; - ULONG io_size = RequestHeaderSize + RequestDataSize; - - if ((RequestHeaderSize > 0) && - (RequestDataSize > 0)) - { - temp_buffer = (PUCHAR) - ExAllocatePoolWithTag(NonPagedPool, io_size, POOL_TAG); - - if (temp_buffer == NULL) - { - KdPrint(("ImDisk Proxy Client: Memory allocation failed.\n.")); - - IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - if (RequestHeaderSize > 0) - { - RtlCopyMemory(temp_buffer, RequestHeader, RequestHeaderSize); - } - - if (RequestDataSize > 0) - { - RtlCopyMemory(temp_buffer + RequestHeaderSize, RequestData, RequestDataSize); - } - - io_buffer = temp_buffer; - } - else if (RequestHeaderSize > 0) - { - io_buffer = (PUCHAR)RequestHeader; - } - else if (RequestDataSize > 0) - { - io_buffer = (PUCHAR)RequestData; - } - - if (io_size > 0) - { - if (CancelEvent != NULL ? - KeReadStateEvent(CancelEvent) != 0 : - FALSE) - { - KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); - - if (temp_buffer != NULL) - { - ExFreePoolWithTag(temp_buffer, POOL_TAG); - } - - IoStatusBlock->Status = STATUS_CANCELLED; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - status = ImDiskSafeIOStream(Proxy->device, - IRP_MJ_WRITE, - IoStatusBlock, - CancelEvent, - io_buffer, - io_size); - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk Proxy Client: Request error %#x\n.", - status)); - - if (temp_buffer != NULL) - { - ExFreePoolWithTag(temp_buffer, POOL_TAG); - } - - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - } - - if (temp_buffer != NULL) - { - ExFreePoolWithTag(temp_buffer, POOL_TAG); - } - - if (ResponseHeaderSize > 0) - { - if (CancelEvent != NULL ? - KeReadStateEvent(CancelEvent) != 0 : - FALSE) - { - KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); - - IoStatusBlock->Status = STATUS_CANCELLED; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - status = ImDiskSafeIOStream(Proxy->device, - IRP_MJ_READ, - IoStatusBlock, - CancelEvent, - ResponseHeader, - ResponseHeaderSize); - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk Proxy Client: Response header error %#x\n.", - status)); - - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - } - - if (ResponseDataSize != NULL && *ResponseDataSize > 0) - { - if (*ResponseDataSize > ResponseDataBufferSize) - { - KdPrint(("ImDisk Proxy Client: Fatal: Request %u bytes, " - "receiving %u bytes.\n", - ResponseDataBufferSize, *ResponseDataSize)); - - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - if (CancelEvent != NULL ? - KeReadStateEvent(CancelEvent) != 0 : - FALSE) - { - KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); - - IoStatusBlock->Status = STATUS_CANCELLED; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - KdPrint2 - (("ImDisk Proxy Client: Got ok resp. Waiting for data.\n")); - - status = ImDiskSafeIOStream(Proxy->device, - IRP_MJ_READ, - IoStatusBlock, - CancelEvent, - ResponseData, - *ResponseDataSize); - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk Proxy Client: Response data error %#x\n.", - status)); - - KdPrint(("ImDisk Proxy Client: Response data %u bytes, " - "got %u bytes.\n", - *ResponseDataSize, - (ULONG)IoStatusBlock->Information)); - - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - KdPrint2 - (("ImDisk Proxy Client: Received %u byte data stream.\n", - IoStatusBlock->Information)); - } - - IoStatusBlock->Status = STATUS_SUCCESS; - - IoStatusBlock->Information = RequestDataSize; - - if (ResponseDataSize != NULL) - { - IoStatusBlock->Information += *ResponseDataSize; - } - - return IoStatusBlock->Status; - } - - case PROXY_CONNECTION::PROXY_CONNECTION_SHM: - { - PKEVENT wait_objects[] = { - Proxy->response_event, - CancelEvent - }; - - ULONG number_of_wait_objects = CancelEvent != NULL ? 2 : 1; - - // Some parameter sanity checks - if ((RequestHeaderSize > IMDPROXY_HEADER_SIZE) | - (ResponseHeaderSize > IMDPROXY_HEADER_SIZE) | - ((RequestDataSize + IMDPROXY_HEADER_SIZE) > - Proxy->shared_memory_size)) - { - KdPrint(("ImDisk Proxy Client: " - "Parameter values not supported.\n.")); - - IoStatusBlock->Status = STATUS_INVALID_BUFFER_SIZE; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - IoStatusBlock->Information = 0; - - if (RequestHeaderSize > 0) - RtlCopyMemory(Proxy->shared_memory, - RequestHeader, - RequestHeaderSize); - - if (RequestDataSize > 0) - RtlCopyMemory(Proxy->shared_memory + IMDPROXY_HEADER_SIZE, - RequestData, - RequestDataSize); - -#pragma warning(suppress: 28160) - KeSetEvent(Proxy->request_event, (KPRIORITY)0, TRUE); - - status = KeWaitForMultipleObjects(number_of_wait_objects, - (PVOID*)wait_objects, - WaitAny, - Executive, - KernelMode, - FALSE, - NULL, - NULL); - - if (status == STATUS_WAIT_1) - { - KdPrint(("ImDisk Proxy Client: Incomplete wait %#x.\n.", status)); - - IoStatusBlock->Status = STATUS_CANCELLED; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - if (ResponseHeaderSize > 0) - RtlCopyMemory(ResponseHeader, - Proxy->shared_memory, - ResponseHeaderSize); - - // If server end requests to send more data than we requested, we - // treat that as an unrecoverable device error and exit. - if (ResponseDataSize != NULL ? *ResponseDataSize > 0 : FALSE) - if ((*ResponseDataSize > ResponseDataBufferSize) | - ((*ResponseDataSize + IMDPROXY_HEADER_SIZE) > - Proxy->shared_memory_size)) - { - KdPrint(("ImDisk Proxy Client: Invalid response size %u.\n.", - *ResponseDataSize)); - - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - else - { - RtlCopyMemory(ResponseData, - Proxy->shared_memory + IMDPROXY_HEADER_SIZE, - *ResponseDataSize); - - IoStatusBlock->Information = *ResponseDataSize; - } - - IoStatusBlock->Status = STATUS_SUCCESS; - if ((RequestDataSize > 0) & (IoStatusBlock->Information == 0)) - IoStatusBlock->Information = RequestDataSize; - return IoStatusBlock->Status; - } - - default: - return STATUS_DRIVER_INTERNAL_ERROR; - } -} - -#pragma code_seg("PAGE") - -/// -/// Note that this function when successful replaces the Proxy->device pointer -/// to point to the connected device object instead of the proxy service pipe. -/// This means that the only reference to the proxy service pipe after calling -/// this function is the original handle to the pipe. -/// -NTSTATUS -ImDiskConnectProxy(IN OUT PPROXY_CONNECTION Proxy, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN ULONG Flags, - IN PWSTR ConnectionString, - IN USHORT ConnectionStringLength) -{ - IMDPROXY_CONNECT_REQ connect_req; - IMDPROXY_CONNECT_RESP connect_resp; - NTSTATUS status; - - PAGED_CODE(); - - ASSERT(Proxy != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(ConnectionString != NULL); - - if (IMDISK_PROXY_TYPE(Flags) == IMDISK_PROXY_TYPE_SHM) - { - OBJECT_ATTRIBUTES object_attributes; - UNICODE_STRING base_name = { 0 }; - UNICODE_STRING event_name = { 0 }; - base_name.Buffer = ConnectionString; - base_name.Length = ConnectionStringLength; - base_name.MaximumLength = ConnectionStringLength; - event_name.MaximumLength = ConnectionStringLength + 20; - event_name.Buffer = - (PWCHAR)ExAllocatePoolWithTag(PagedPool, - event_name.MaximumLength, - POOL_TAG); - if (event_name.Buffer == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - InitializeObjectAttributes(&object_attributes, - &event_name, - OBJ_CASE_INSENSITIVE, - NULL, - NULL); - - RtlCopyUnicodeString(&event_name, &base_name); - RtlAppendUnicodeToString(&event_name, L"_Request"); - - status = ZwOpenEvent(&Proxy->request_event_handle, - EVENT_ALL_ACCESS, - &object_attributes); - - if (!NT_SUCCESS(status)) - { - Proxy->request_event_handle = NULL; - ExFreePoolWithTag(event_name.Buffer, POOL_TAG); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - status = ObReferenceObjectByHandle(Proxy->request_event_handle, - EVENT_ALL_ACCESS, - *ExEventObjectType, - KernelMode, - (PVOID*)&Proxy->request_event, - NULL); - - if (!NT_SUCCESS(status)) - { - Proxy->request_event = NULL; - ExFreePoolWithTag(event_name.Buffer, POOL_TAG); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - RtlCopyUnicodeString(&event_name, &base_name); - RtlAppendUnicodeToString(&event_name, L"_Response"); - - status = ZwOpenEvent(&Proxy->response_event_handle, - EVENT_ALL_ACCESS, - &object_attributes); - - if (!NT_SUCCESS(status)) - { - Proxy->response_event_handle = NULL; - ExFreePoolWithTag(event_name.Buffer, POOL_TAG); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - status = ObReferenceObjectByHandle(Proxy->response_event_handle, - EVENT_ALL_ACCESS, - *ExEventObjectType, - KernelMode, - (PVOID*)&Proxy->response_event, - NULL); - - if (!NT_SUCCESS(status)) - { - Proxy->response_event = NULL; - ExFreePoolWithTag(event_name.Buffer, POOL_TAG); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } + sym_link_global_wchar[19] = DriveLetter; - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } + KdPrint(("ImDisk: Removing symlink '%ws'.\n", sym_link_global_wchar)); - connect_req.request_code = IMDPROXY_REQ_CONNECT; - connect_req.flags = Flags; - connect_req.length = ConnectionStringLength; - - KdPrint(("ImDisk Proxy Client: Sending IMDPROXY_CONNECT_REQ.\n")); - - status = ImDiskCallProxy(Proxy, - IoStatusBlock, - CancelEvent, - &connect_req, - sizeof(connect_req), - ConnectionString, - ConnectionStringLength, - &connect_resp, - sizeof(IMDPROXY_CONNECT_RESP), - NULL, - 0, - NULL); + RtlInitUnicodeString(&sym_link, sym_link_global_wchar); + status = IoDeleteSymbolicLink(&sym_link); if (!NT_SUCCESS(status)) { - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - if (connect_resp.error_code != 0) - { - IoStatusBlock->Status = STATUS_CONNECTION_REFUSED; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - // If the proxy gave us a reference to an object to use for direct connection - // to the server we have to change the active reference to use here. - if (connect_resp.object_ptr != 0) - { - // First check that connect_resp.object_ptr is really something we have - // referenced earlier. - - KEVENT event; - IO_STATUS_BLOCK io_status; - PIRP irp; - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - irp = IoBuildDeviceIoControlRequest( - IOCTL_IMDISK_GET_REFERENCED_HANDLE, - ImDiskCtlDevice, - &connect_resp.object_ptr, - sizeof(PFILE_OBJECT), - NULL, - 0, - TRUE, - &event, - &io_status); - - if (irp == NULL) - { - IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - status = IoCallDriver(ImDiskCtlDevice, irp); - - if (status == STATUS_PENDING) - { - KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); - } - - if (!NT_SUCCESS(status)) - { - DbgPrint("ImDisk: Failed claiming referenced object %p: %#x\n", - (PVOID)(ULONG_PTR)connect_resp.object_ptr, status); - - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - ObDereferenceObject(Proxy->device); - Proxy->device = (PFILE_OBJECT)(ULONG_PTR)connect_resp.object_ptr; + KdPrint + (("ImDisk: Cannot remove symlink '%ws'. (%#x)\n", + sym_link_global_wchar, status)); } - KdPrint(("ImDisk Proxy Client: Got ok response IMDPROXY_CONNECT_RESP.\n")); - - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; -} - -NTSTATUS -ImDiskQueryInformationProxy(IN PPROXY_CONNECTION Proxy, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent, - OUT PIMDPROXY_INFO_RESP ProxyInfoResponse, - IN ULONG ProxyInfoResponseLength) -{ - ULONGLONG proxy_req = IMDPROXY_REQ_INFO; - NTSTATUS status; - - PAGED_CODE(); - - ASSERT(Proxy != NULL); - ASSERT(IoStatusBlock != NULL); - - if ((ProxyInfoResponse == NULL) | - (ProxyInfoResponseLength < sizeof(IMDPROXY_INFO_RESP))) - { - IoStatusBlock->Status = STATUS_BUFFER_OVERFLOW; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } + sym_link_wchar[12] = DriveLetter; - KdPrint(("ImDisk Proxy Client: Sending IMDPROXY_REQ_INFO.\n")); + KdPrint(("ImDisk: Removing symlink '%ws'.\n", sym_link_wchar)); - status = ImDiskCallProxy(Proxy, - IoStatusBlock, - CancelEvent, - &proxy_req, - sizeof(proxy_req), - NULL, - 0, - ProxyInfoResponse, - sizeof(IMDPROXY_INFO_RESP), - NULL, - 0, - NULL); + RtlInitUnicodeString(&sym_link, sym_link_wchar); + status = IoDeleteSymbolicLink(&sym_link); if (!NT_SUCCESS(status)) { - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; - } - - KdPrint(("ImDisk Proxy Client: Got ok response IMDPROXY_INFO_RESP.\n")); - - if (ProxyInfoResponse->req_alignment - 1 > FILE_512_BYTE_ALIGNMENT) - { -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk IMDPROXY_INFO_RESP: Unsupported sizes. " - "Got 0x%.8x%.8x size and 0x%.8x%.8x alignment.\n", - ProxyInfoResponse->file_size, - ProxyInfoResponse->req_alignment)); - - IoStatusBlock->Status = STATUS_INVALID_PARAMETER; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; + KdPrint(("ImDisk: Cannot remove symlink '%ws'. (%#x)\n", + sym_link_wchar, status)); } - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; + return status; } #pragma code_seg() -NTSTATUS -ImDiskReadProxy(IN PPROXY_CONNECTION Proxy, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent, - OUT PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset) -{ - IMDPROXY_READ_REQ read_req; - IMDPROXY_READ_RESP read_resp; - NTSTATUS status; - ULONG_PTR max_transfer_size; - ULONG length_done; - - ASSERT(Proxy != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(Buffer != NULL); - ASSERT(ByteOffset != NULL); - - if (Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) - max_transfer_size = Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE; - else - max_transfer_size = Length; - - length_done = 0; - status = STATUS_SUCCESS; - - while (length_done < Length) - { - ULONG length_to_do = Length - length_done; - - KdPrint2(("ImDisk Proxy Client: " - "IMDPROXY_REQ_READ 0x%.8x done 0x%.8x left to do.\n", - length_done, length_to_do)); - - read_req.request_code = IMDPROXY_REQ_READ; - read_req.offset = ByteOffset->QuadPart + length_done; - read_req.length = - length_to_do <= max_transfer_size ? - length_to_do : max_transfer_size; - - KdPrint2(("ImDisk Proxy Client: " - "IMDPROXY_REQ_READ 0x%.8x%.8x bytes at 0x%.8x%.8x.\n", - ((PLARGE_INTEGER)&read_req.length)->HighPart, - ((PLARGE_INTEGER)&read_req.length)->LowPart, - ((PLARGE_INTEGER)&read_req.offset)->HighPart, - ((PLARGE_INTEGER)&read_req.offset)->LowPart)); - - status = ImDiskCallProxy(Proxy, - IoStatusBlock, - CancelEvent, - &read_req, - sizeof(read_req), - NULL, - 0, - &read_resp, - sizeof(read_resp), - (PUCHAR)Buffer + length_done, - (ULONG)read_req.length, - (PULONG)&read_resp.length); - - if (!NT_SUCCESS(status)) - { - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - length_done += (ULONG)read_resp.length; - - if (read_resp.errorno != 0) - { -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", - read_resp.errorno)); - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - KdPrint2(("ImDisk Proxy Client: Server sent 0x%.8x%.8x bytes.\n", - ((PLARGE_INTEGER)&read_resp.length)->HighPart, - ((PLARGE_INTEGER)&read_resp.length)->LowPart)); - - if (read_resp.length == 0) - break; - } - - IoStatusBlock->Status = status; - IoStatusBlock->Information = length_done; - - return status; -} - -NTSTATUS -ImDiskWriteProxy(IN PPROXY_CONNECTION Proxy, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent, - IN PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset) +#if DEBUG_LEVEL >= 1 +VOID +ImDiskLogDbgError(IN PVOID Object, + IN UCHAR MajorFunctionCode, + IN UCHAR RetryCount, + IN PULONG DumpData, + IN USHORT DumpDataSize, + IN USHORT EventCategory, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN ULONG SequenceNumber, + IN ULONG IoControlCode, + IN PLARGE_INTEGER DeviceOffset, + IN PWCHAR Message) { - IMDPROXY_WRITE_REQ write_req; - IMDPROXY_WRITE_RESP write_resp; - NTSTATUS status; - ULONG_PTR max_transfer_size; - ULONG length_done; + ULONG_PTR string_byte_size; + ULONG_PTR packet_size; + PIO_ERROR_LOG_PACKET error_log_packet; - ASSERT(Proxy != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(Buffer != NULL); - ASSERT(ByteOffset != NULL); + if (KeGetCurrentIrql() > DISPATCH_LEVEL) + return; - if (Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) - max_transfer_size = Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE; - else - max_transfer_size = Length; + string_byte_size = (wcslen(Message) + 1) << 1; - length_done = 0; - status = STATUS_SUCCESS; + packet_size = + sizeof(IO_ERROR_LOG_PACKET) + DumpDataSize + string_byte_size; - while (length_done < Length) + if (packet_size > ERROR_LOG_MAXIMUM_SIZE) { - ULONG length_to_do = Length - length_done; - - KdPrint2(("ImDisk Proxy Client: " - "IMDPROXY_REQ_WRITE 0x%.8x done 0x%.8x left to do.\n", - length_done, length_to_do)); - - write_req.request_code = IMDPROXY_REQ_WRITE; - write_req.offset = ByteOffset->QuadPart + length_done; - write_req.length = - length_to_do <= max_transfer_size ? - length_to_do : max_transfer_size; - - KdPrint2(("ImDisk Proxy Client: " - "IMDPROXY_REQ_WRITE 0x%.8x%.8x bytes at 0x%.8x%.8x.\n", - ((PLARGE_INTEGER)&write_req.length)->HighPart, - ((PLARGE_INTEGER)&write_req.length)->LowPart, - ((PLARGE_INTEGER)&write_req.offset)->HighPart, - ((PLARGE_INTEGER)&write_req.offset)->LowPart)); - - status = ImDiskCallProxy(Proxy, - IoStatusBlock, - CancelEvent, - &write_req, - sizeof(write_req), - (PUCHAR)Buffer + length_done, - (ULONG)write_req.length, - &write_resp, - sizeof(write_resp), - NULL, - 0, - NULL); - - if (!NT_SUCCESS(status)) - { - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - if (write_resp.errorno != 0) - { -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", - write_resp.errorno)); - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - if (write_resp.length != write_req.length) - { - KdPrint(("ImDisk Proxy Client: IMDPROXY_REQ_WRITE %u bytes, " - "IMDPROXY_RESP_WRITE %u bytes.\n", - Length, - (ULONG)write_resp.length)); - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; - } - - KdPrint2(("ImDisk Proxy Client: Server replied OK.\n")); - - length_done += (ULONG)write_req.length; + KdPrint(("ImDisk: Warning: Too large error log packet.\n")); + return; } - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = length_done; - return IoStatusBlock->Status; -} - -NTSTATUS -ImDiskUnmapOrZeroProxy(IN PPROXY_CONNECTION Proxy, - IN ULONGLONG RequestCode, - IN OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PKEVENT CancelEvent OPTIONAL, - IN ULONG Items, - IN PDEVICE_DATA_SET_RANGE Ranges) -{ - IMDPROXY_UNMAP_REQ unmap_req; - IMDPROXY_UNMAP_RESP unmap_resp; - NTSTATUS status; - ULONG byte_size = (ULONG)(Items * sizeof(DEVICE_DATA_SET_RANGE)); - - ASSERT(Proxy != NULL); - ASSERT(IoStatusBlock != NULL); - ASSERT(Ranges != NULL); + error_log_packet = + (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Object, + (UCHAR)packet_size); - if ((Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) && - (byte_size >= (Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE))) + if (error_log_packet == NULL) { - status = STATUS_BUFFER_OVERFLOW; - IoStatusBlock->Information = 0; - IoStatusBlock->Status = status; - return status; + KdPrint(("ImDisk: Warning: IoAllocateErrorLogEntry() returned NULL.\n")); + return; } - status = STATUS_SUCCESS; - - unmap_req.request_code = RequestCode; - unmap_req.length = byte_size; - -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk Proxy Client: Unmap/Zero 0x%.8x%.8x\n", RequestCode)); - - status = ImDiskCallProxy(Proxy, - IoStatusBlock, - CancelEvent, - &unmap_req, - sizeof(unmap_req), - (PUCHAR)Ranges, - (ULONG)unmap_req.length, - &unmap_resp, - sizeof(unmap_resp), - NULL, - 0, - NULL); + error_log_packet->MajorFunctionCode = MajorFunctionCode; + error_log_packet->RetryCount = RetryCount; + error_log_packet->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + DumpDataSize; + error_log_packet->EventCategory = EventCategory; + error_log_packet->ErrorCode = ErrorCode; + error_log_packet->UniqueErrorValue = UniqueErrorValue; + error_log_packet->FinalStatus = FinalStatus; + error_log_packet->SequenceNumber = SequenceNumber; + error_log_packet->IoControlCode = IoControlCode; + if (DeviceOffset != NULL) + error_log_packet->DeviceOffset = *DeviceOffset; + error_log_packet->DumpDataSize = DumpDataSize; - if (!NT_SUCCESS(status)) - { - IoStatusBlock->Status = status; - IoStatusBlock->Information = 0; - return status; - } + if (DumpDataSize != 0) + memcpy(error_log_packet->DumpData, DumpData, DumpDataSize); - if (unmap_resp.errorno != 0) + if (Message == NULL) + error_log_packet->NumberOfStrings = 0; + else { -#pragma warning(suppress: 6064) -#pragma warning(suppress: 6328) - KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", - unmap_resp.errorno)); - IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; + error_log_packet->NumberOfStrings = 1; + memcpy((PUCHAR)error_log_packet + error_log_packet->StringOffset, + Message, + string_byte_size); } - KdPrint(("ImDisk Proxy Client: Server replied OK.\n")); - - IoStatusBlock->Status = STATUS_SUCCESS; - IoStatusBlock->Information = 0; - return IoStatusBlock->Status; + IoWriteErrorLogEntry(error_log_packet); } +#endif -#pragma code_seg("PAGE") - -#ifdef INCLUDE_VFD_ORIGIN - -// -// Format tracks -// Actually, just fills specified range of tracks with fill characters -// -NTSTATUS -ImDiskFloppyFormat(IN PDEVICE_EXTENSION Extension, - IN PIRP Irp) +VOID +ImDiskRemoveVirtualDisk(IN PDEVICE_OBJECT DeviceObject) { - PFORMAT_PARAMETERS param; - ULONG track_length; - PUCHAR format_buffer; - LARGE_INTEGER start_offset; - LARGE_INTEGER end_offset; - NTSTATUS status; - - PAGED_CODE(); - - ASSERT(Extension != NULL); - ASSERT(Irp != NULL); - - param = (PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; - - track_length = - Extension->disk_geometry.BytesPerSector * - Extension->disk_geometry.SectorsPerTrack; - - start_offset.QuadPart = - param->StartCylinderNumber * Extension->disk_geometry.TracksPerCylinder * - track_length + param->StartHeadNumber * track_length; - - end_offset.QuadPart = - param->EndCylinderNumber * Extension->disk_geometry.TracksPerCylinder * - track_length + param->EndHeadNumber * track_length; - - if (Extension->vm_disk) - { - LARGE_INTEGER wait_time; - - RtlFillMemory(((PUCHAR)Extension->image_buffer) + start_offset.LowPart, - end_offset.LowPart - start_offset.LowPart + track_length, - MEDIA_FORMAT_FILL_DATA); - - wait_time.QuadPart = -1; - KeDelayExecutionThread(KernelMode, FALSE, &wait_time); - - Irp->IoStatus.Information = 0; - return STATUS_SUCCESS; - } - - start_offset.QuadPart += Extension->image_offset.QuadPart; - end_offset.QuadPart += Extension->image_offset.QuadPart; - - format_buffer = (PUCHAR) - ExAllocatePoolWithTag(PagedPool, track_length, POOL_TAG); - - if (format_buffer == NULL) - { - Irp->IoStatus.Information = 0; - return STATUS_INSUFFICIENT_RESOURCES; - } - - RtlFillMemory(format_buffer, track_length, MEDIA_FORMAT_FILL_DATA); - - do - { - if (Extension->use_proxy) - { - status = ImDiskWriteProxy(&Extension->proxy, - &Irp->IoStatus, - &Extension->terminate_thread, - format_buffer, - track_length, - &start_offset); - } - else - { - status = NtWriteFile(Extension->file_handle, - NULL, - NULL, - NULL, - &Irp->IoStatus, - format_buffer, - track_length, - &start_offset, - NULL); - } - - if (!NT_SUCCESS(status)) - { - KdPrint(("ImDisk Format failed: Write failed with status %#x.\n", - status)); + PDEVICE_EXTENSION device_extension; - break; - } + ASSERT(DeviceObject != NULL); - start_offset.QuadPart += track_length; - } while (start_offset.QuadPart <= end_offset.QuadPart); + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; - ExFreePoolWithTag(format_buffer, POOL_TAG); + KdPrint(("ImDisk: Request to shutdown device %i.\n", + device_extension->device_number)); - Irp->IoStatus.Information = 0; + if (device_extension->drive_letter != 0) + if (KeGetCurrentIrql() == PASSIVE_LEVEL) + ImDiskRemoveDriveLetter(device_extension->drive_letter); - return status; + KeSetEvent(&device_extension->terminate_thread, (KPRIORITY)0, FALSE); } -#endif // INCLUDE_VFD_ORIGIN diff --git a/sys/imdsksys.h b/sys/imdsksys.h new file mode 100644 index 0000000..b8b7bdf --- /dev/null +++ b/sys/imdsksys.h @@ -0,0 +1,489 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// Definitions and imports are now in the "sources" file and managed by the +/// build utility. +/// + +#ifndef DEBUG_LEVEL +#define DEBUG_LEVEL 0 +#endif + +#if DEBUG_LEVEL >= 2 +#define KdPrint2(x) DbgPrint x +#else +#define KdPrint2(x) +#endif + +#if DEBUG_LEVEL >= 1 +#undef KdPrint +#define KdPrint(x) DbgPrint x +#define ImDiskLogError(x) ImDiskLogDbgError x +#else +#define ImDiskLogError(x) +#endif + +#define POOL_TAG 'iDmI' + +#include "..\inc\ntkmapi.h" +#include "..\inc\imdisk.h" +#include "..\inc\imdproxy.h" +#include "..\inc\wkmem.hpp" + +#pragma warning(disable: 28719) + +#define IMDISK_DEFAULT_LOAD_DEVICES 0 +#define IMDISK_DEFAULT_MAX_DEVICES 64000 + +/// +/// Constants for synthetic geometry of the virtual disks +/// + +// For hard drive partition-style devices +#define SECTOR_SIZE_HDD 512 +#define HEAD_SIZE_HDD 63 + +// For CD-ROM/DVD-style devices +#define SECTOR_SIZE_CD_ROM 2048 +#define SECTORS_PER_TRACK_CD_ROM 32 +#define TRACKS_PER_CYLINDER_CD_ROM 64 + +// +// TOC Data Track returned for virtual CD/DVD +// +#define TOC_DATA_TRACK 0x04 + +// +// Fill character for formatting virtual floppy media +// +#define MEDIA_FORMAT_FILL_DATA 0xf6 + +// This structure is used when a new device is about to be created. It is sent +// to the created device dispatch thread which also creates the device object. +typedef struct _DEVICE_THREAD_DATA +{ + PDRIVER_OBJECT driver_object; + PIMDISK_CREATE_DATA create_data; + PETHREAD client_thread; // The client thread that device should impersonate + KEVENT created_event; // Set when device is created (or creation failed) + BOOLEAN caller_waiting; // If there is a caller waiting to free this data + NTSTATUS status; // Set after device creation attempt +} DEVICE_THREAD_DATA, *PDEVICE_THREAD_DATA; + +typedef struct _PROXY_CONNECTION +{ + enum PROXY_CONNECTION_TYPE + { + PROXY_CONNECTION_DEVICE, + PROXY_CONNECTION_SHM + } connection_type; // Connection type + + union + { + // Valid if connection_type is PROXY_CONNECTION_DEVICE + PFILE_OBJECT device; // Pointer to proxy communication object + + // Valid if connection_type is PROXY_CONNECTION_SHM + struct + { + HANDLE request_event_handle; + PKEVENT request_event; + HANDLE response_event_handle; + PKEVENT response_event; + PUCHAR shared_memory; + ULONG_PTR shared_memory_size; + }; + }; +} PROXY_CONNECTION, *PPROXY_CONNECTION; + +typedef struct _DEVICE_EXTENSION +{ + LIST_ENTRY list_head; + KSPIN_LOCK list_lock; + KEVENT request_event; + KEVENT terminate_thread; + + ULONG device_number; + + HANDLE file_handle; // For file or proxy type + PDEVICE_OBJECT dev_object; // Pointer to image I/O DEVICE_OBJECT + PFILE_OBJECT file_object; // Pointer to image I/O FILE_OBJECT + BOOLEAN parallel_io; // TRUE if image I/O is done in dispatcher thread + PUCHAR image_buffer; // For vm type + BOOLEAN byte_swap; // If image I/O should swap each pair of bytes + BOOLEAN shared_image; // Image opened for shared writing + PROXY_CONNECTION proxy; // Proxy connection data + UNICODE_STRING file_name; // Name of image file, if any + WCHAR drive_letter; // Drive letter if maintained by the driver + DISK_GEOMETRY disk_geometry; // Virtual C/H/S geometry (Cylinders=Total size) + LARGE_INTEGER image_offset; // Offset in bytes in the image file + ULONG media_change_count; + BOOLEAN read_only; + BOOLEAN vm_disk; // TRUE if this device is a virtual memory disk + BOOLEAN awealloc_disk; // TRUE if this device is a physical memory disk + // through AWEAlloc driver + BOOLEAN use_proxy; // TRUE if this device uses proxy device for I/O + BOOLEAN proxy_unmap; // TRUE if proxy supports UNMAP operations + BOOLEAN proxy_zero; // TRUE if proxy supports ZERO operations + BOOLEAN image_modified; // TRUE if this device has been written to + LONG special_file_count; // Number of swapfiles/hiberfiles on device + BOOLEAN use_set_zero_data; // TRUE if FSCTL_SET_ZERO_DATA is used to write + // all zeros blocks + BOOLEAN no_file_level_trim; // TRUE if last file level trim failed + + PKTHREAD device_thread; // Pointer to the worker thread object + + KSPIN_LOCK last_io_lock; // Last I/O buffer for fast re-reads + PUCHAR last_io_data; + LONGLONG last_io_offset; + ULONG last_io_length; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +typedef struct _REFERENCED_OBJECT +{ + LIST_ENTRY list_entry; + PFILE_OBJECT file_object; +} REFERENCED_OBJECT, *PREFERENCED_OBJECT; + +// Prototypes for functions defined in this driver + +EXTERN_C DRIVER_INITIALIZE DriverEntry; + +DRIVER_UNLOAD ImDiskUnload; + +VOID +ImDiskFindFreeDeviceNumber(PDRIVER_OBJECT DriverObject, + PULONG DeviceNumber); + +VOID +ImDiskLogDbgError(IN PVOID Object, + IN UCHAR MajorFunctionCode, + IN UCHAR RetryCount, + IN PULONG DumpData, + IN USHORT DumpDataSize, + IN USHORT EventCategory, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN ULONG SequenceNumber, + IN ULONG IoControlCode, + IN PLARGE_INTEGER DeviceOffset, + IN PWCHAR Message); + +NTSTATUS +ImDiskAddVirtualDisk(IN PDRIVER_OBJECT DriverObject, + IN OUT PIMDISK_CREATE_DATA CreateData, + IN PETHREAD ClientThread); + +NTSTATUS +ImDiskAddVirtualDiskAfterInitialization(IN PDRIVER_OBJECT DriverObject, + IN HANDLE ParameterKey, + IN ULONG DeviceNumber); + +NTSTATUS +ImDiskCreateDriveLetter(IN WCHAR DriveLetter, + IN ULONG DeviceNumber); + +NTSTATUS +ImDiskRemoveDriveLetter(IN WCHAR DriveLetter); + +VOID +ImDiskRemoveVirtualDisk(IN PDEVICE_OBJECT DeviceObject); + +__drv_dispatchType(IRP_MJ_CREATE) +__drv_dispatchType(IRP_MJ_CLOSE) +DRIVER_DISPATCH ImDiskDispatchCreateClose; + +__drv_dispatchType(IRP_MJ_READ) +__drv_dispatchType(IRP_MJ_WRITE) +DRIVER_DISPATCH ImDiskDispatchReadWrite; + +__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) +__drv_dispatchType(IRP_MJ_INTERNAL_DEVICE_CONTROL) +DRIVER_DISPATCH ImDiskDispatchDeviceControl; + +__drv_dispatchType(IRP_MJ_PNP) +DRIVER_DISPATCH ImDiskDispatchPnP; + +__drv_dispatchType(IRP_MJ_QUERY_INFORMATION) +DRIVER_DISPATCH ImDiskDispatchQueryInformation; + +__drv_dispatchType(IRP_MJ_SET_INFORMATION) +DRIVER_DISPATCH ImDiskDispatchSetInformation; + +__drv_dispatchType(IRP_MJ_FLUSH_BUFFERS) +DRIVER_DISPATCH ImDiskDispatchFlushBuffers; + +KSTART_ROUTINE ImDiskDeviceThread; + +IO_COMPLETION_ROUTINE ImDiskReadWriteLowerDeviceCompletion; + +NTSTATUS +ImDiskCreateDevice(__in PDRIVER_OBJECT DriverObject, + __inout __deref PIMDISK_CREATE_DATA CreateData, + __in PETHREAD ClientThread, + __out PDEVICE_OBJECT *DeviceObject); + +NTSTATUS +ImDiskSafeIOStream(IN PFILE_OBJECT FileObject, + IN UCHAR MajorFunction, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent, + IN OUT PVOID Buffer, + IN ULONG Length); + +NTSTATUS +ImDiskConnectProxy(IN OUT PPROXY_CONNECTION Proxy, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN ULONG Flags, + IN PWSTR ConnectionString, + IN USHORT ConnectionStringLength); + +VOID +ImDiskCloseProxy(IN PPROXY_CONNECTION Proxy); + +NTSTATUS +ImDiskQueryInformationProxy(IN PPROXY_CONNECTION Proxy, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + OUT PIMDPROXY_INFO_RESP ProxyInfoResponse, + IN ULONG ProxyInfoResponseLength); + +NTSTATUS +ImDiskReadProxy(IN PPROXY_CONNECTION Proxy, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + OUT PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset); + +NTSTATUS +ImDiskWriteProxy(IN PPROXY_CONNECTION Proxy, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset); + +NTSTATUS +ImDiskUnmapOrZeroProxy(IN PPROXY_CONNECTION Proxy, + IN ULONGLONG RequestCode, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN ULONG Items, + IN PDEVICE_DATA_SET_RANGE Ranges); + +// +// Reads in a loop up to "Length" or until eof reached. +// +NTSTATUS +ImDiskSafeReadFile(IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN SIZE_T Length, + IN PLARGE_INTEGER Offset); + +NTSTATUS +ImDiskReadWriteLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension); + +NTSTATUS +ImDiskDeviceControlLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension); + +#ifdef INCLUDE_VFD_ORIGIN + +NTSTATUS +ImDiskFloppyFormat(IN PDEVICE_EXTENSION Extension, + IN PIRP Irp); + +#endif // INCLUDE_VFD_ORIGIN + +#ifdef _AMD64_ + +#define ImDiskAcquireLock KeAcquireInStackQueuedSpinLock + +#define ImDiskReleaseLock KeReleaseInStackQueuedSpinLock + +FORCEINLINE +VOID +ImDiskInterlockedInsertTailList( + PLIST_ENTRY ListHead, + PLIST_ENTRY ListEntry, + PKSPIN_LOCK SpinLock) +{ + KLOCK_QUEUE_HANDLE lock_handle; + + KeAcquireInStackQueuedSpinLock(SpinLock, &lock_handle); + + InsertTailList(ListHead, ListEntry); + + KeReleaseInStackQueuedSpinLock(&lock_handle); +} + +FORCEINLINE +PLIST_ENTRY +ImDiskInterlockedRemoveHeadList( + PLIST_ENTRY ListHead, + PKSPIN_LOCK SpinLock) +{ + KLOCK_QUEUE_HANDLE lock_handle; + PLIST_ENTRY item; + + KeAcquireInStackQueuedSpinLock(SpinLock, &lock_handle); + + item = RemoveHeadList(ListHead); + + if (item == ListHead) + { + item = NULL; + } + + KeReleaseInStackQueuedSpinLock(&lock_handle); + + return item; +} + +#else + +#define ImDiskAcquireLock(SpinLock, LockHandle) \ + { \ + (LockHandle)->LockQueue.Lock = (SpinLock); \ + KeAcquireSpinLock((LockHandle)->LockQueue.Lock, &(LockHandle)->OldIrql); \ + } + +#define ImDiskReleaseLock(LockHandle) \ + { \ + KeReleaseSpinLock((LockHandle)->LockQueue.Lock, (LockHandle)->OldIrql); \ + } + +#define ImDiskInterlockedInsertTailList (VOID)ExInterlockedInsertTailList + +#define ImDiskInterlockedRemoveHeadList ExInterlockedRemoveHeadList + +#endif + +FORCEINLINE +BOOLEAN +ImDiskIsBufferZero(PVOID Buffer, ULONG Length) +{ + PULONGLONG ptr; + + if (Length < sizeof(ULONGLONG)) + return FALSE; + + for (ptr = (PULONGLONG)Buffer; + (ptr <= (PULONGLONG)((PUCHAR)Buffer + Length - sizeof(ULONGLONG))) && + (*ptr == 0); ptr++); + + return (BOOLEAN)(ptr == (PULONGLONG)((PUCHAR)Buffer + Length)); +} + +FORCEINLINE +VOID +ImDiskByteSwapBuffer(IN OUT PUCHAR Buffer, + IN ULONG_PTR Length) +{ + PUCHAR ptr; + + for (ptr = Buffer; + (ULONG_PTR)(ptr - Buffer) < Length; + ptr += 2) + { + UCHAR b1 = ptr[1]; + ptr[1] = ptr[0]; + ptr[0] = b1; + } +} + +// +// Pointer to the controller device object. +// +extern PDEVICE_OBJECT ImDiskCtlDevice; + +// +// Allocation bitmap with currently configured device numbers. +// (No device list bit field is maintained anymore. Device list is always +// enumerated "live" directly from current device objects.) +// +//extern volatile ULONGLONG DeviceList = 0; + +// +// Max number of devices that can be dynamically created by IOCTL calls +// to the control device. +// +extern ULONG MaxDevices; + +// +// Device list lock +// +extern KSPIN_LOCK DeviceListLock; + +// +// Device list lock +// +extern KSPIN_LOCK ReferencedObjectsListLock; + +// +// List of objects referenced using +// IOCTL_IMDISK_REFERENCE_HANDLE +// +extern LIST_ENTRY ReferencedObjects; + +// +// Handle to global refresh event +// +extern PKEVENT RefreshEvent; + diff --git a/sys/iodisp.cpp b/sys/iodisp.cpp new file mode 100644 index 0000000..acdea58 --- /dev/null +++ b/sys/iodisp.cpp @@ -0,0 +1,2307 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +NTSTATUS +ImDiskDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PIO_STACK_LOCATION io_stack; + PDEVICE_EXTENSION device_extension; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + KdPrint(("ImDisk: Entering ImDiskDispatchCreateClose.\n")); + + io_stack = IoGetCurrentIrpStackLocation(Irp); + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + if (io_stack->FileObject->FileName.Length != 0) + { + KdPrint(("ImDisk: Attempt to open '%wZ' on device %i.\n", + &io_stack->FileObject->FileName, + device_extension->device_number)); + + status = STATUS_OBJECT_NAME_NOT_FOUND; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if ((io_stack->MajorFunction == IRP_MJ_CREATE) && + KeReadStateEvent(&device_extension->terminate_thread)) + { + KdPrint(("ImDisk: Attempt to open device %i when shut down.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + KdPrint(("ImDisk: Successfully created/closed a handle for device %i.\n", + device_extension->device_number)); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return status; +} + +NTSTATUS +ImDiskDispatchReadWrite(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + if ((io_stack->Parameters.Read.ByteOffset.QuadPart < 0) || + ((io_stack->Parameters.Read.ByteOffset.QuadPart + + io_stack->Parameters.Read.Length) < 0)) + { + KdPrint(("ImDisk: Read/write attempt on negative offset.\n")); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + } + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + if (DeviceObject == ImDiskCtlDevice) + { + KdPrint(("ImDisk: Read/write attempt on ctl device.\n")); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: Read/write attempt on device %i while removing.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if ((io_stack->MajorFunction == IRP_MJ_WRITE) && + device_extension->read_only) + { + KdPrint(("ImDisk: Attempt to write to write-protected device %i.\n", + device_extension->device_number)); + + status = STATUS_MEDIA_WRITE_PROTECTED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if ((io_stack->Parameters.Read.ByteOffset.QuadPart + + io_stack->Parameters.Read.Length) > + (device_extension->disk_geometry.Cylinders.QuadPart)) + { + KdPrint(("ImDisk: Read/write beyond eof on device %i.\n", + device_extension->device_number)); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return status; + } + + if (io_stack->Parameters.Read.Length == 0) + { + KdPrint(("ImDisk: Read/write zero bytes on device %i.\n", + device_extension->device_number)); + + status = STATUS_SUCCESS; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return status; + } + + KdPrint2(("ImDisk: Device %i got r/w request Offset=0x%.8x%.8x Len=%#x.\n", + device_extension->device_number, + io_stack->Parameters.Read.ByteOffset.HighPart, + io_stack->Parameters.Read.ByteOffset.LowPart, + io_stack->Parameters.Read.Length)); + + status = STATUS_PENDING; + + if (io_stack->MajorFunction == IRP_MJ_READ) + { + KLOCK_QUEUE_HANDLE lock_handle; + + ImDiskAcquireLock(&device_extension->last_io_lock, &lock_handle); + + if (device_extension->last_io_data != NULL) + { + if ((io_stack->Parameters.Read.ByteOffset.QuadPart >= + device_extension->last_io_offset) & + ((io_stack->Parameters.Read.ByteOffset.QuadPart + + io_stack->Parameters.Read.Length) <= + (device_extension->last_io_offset + + device_extension->last_io_length))) + { + PUCHAR system_buffer = + (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, + NormalPagePriority); + if (system_buffer == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + KdPrint(("ImDisk: Device %i read Offset=0x%.8x%.8x Len=%#x, " + "intermediate cache hit.\n", + device_extension->device_number, + io_stack->Parameters.Read.ByteOffset.HighPart, + io_stack->Parameters.Read.ByteOffset.LowPart, + io_stack->Parameters.Read.Length)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = io_stack->Parameters.Read.Length; + + RtlCopyMemory(system_buffer, + device_extension->last_io_data + + io_stack->Parameters.Read.ByteOffset.QuadPart - + device_extension->last_io_offset, + Irp->IoStatus.Information); + + if (device_extension->byte_swap) + ImDiskByteSwapBuffer(system_buffer, + Irp->IoStatus.Information); + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset.QuadPart += + Irp->IoStatus.Information; + } + status = STATUS_SUCCESS; + } + } + } + + ImDiskReleaseLock(&lock_handle); + } + + // In-thread I/O using a FILE_OBJECT + if ((status == STATUS_PENDING) && + device_extension->parallel_io) + { + BOOLEAN set_zero_data = FALSE; + + if (device_extension->use_set_zero_data && + (io_stack->MajorFunction == IRP_MJ_WRITE) && + (io_stack->Parameters.Write.Length > sizeof(ULONGLONG))) + { + PUCHAR system_buffer = + (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, + NormalPagePriority); + + if (system_buffer == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + status = STATUS_INSUFFICIENT_RESOURCES; + } + else if (ImDiskIsBufferZero(system_buffer, + io_stack->Parameters.Write.Length)) + { + set_zero_data = TRUE; + } + } + + if (!set_zero_data) + { + return ImDiskReadWriteLowerDevice(Irp, device_extension); + } + } + + if (status == STATUS_PENDING) + { + IoMarkIrpPending(Irp); + + ImDiskInterlockedInsertTailList(&device_extension->list_head, + &Irp->Tail.Overlay.ListEntry, + &device_extension->list_lock); + + KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); + + return STATUS_PENDING; + } + else + { + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return status; + } +} + +NTSTATUS +ImDiskDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + Irp->IoStatus.Information = 0; + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + io_stack = IoGetCurrentIrpStackLocation(Irp); + + KdPrint(("ImDisk: Device %i received IOCTL %#x IRP %p.\n", + device_extension->device_number, + io_stack->Parameters.DeviceIoControl.IoControlCode, + Irp)); + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: IOCTL attempt on device %i that is being removed.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + // The control device can only receive version queries, enumeration queries + // or device create requests. + if (DeviceObject == ImDiskCtlDevice) + { + switch (io_stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_IMDISK_QUERY_VERSION: + case IOCTL_IMDISK_CREATE_DEVICE: + case IOCTL_IMDISK_REMOVE_DEVICE: + case IOCTL_IMDISK_QUERY_DRIVER: + case IOCTL_IMDISK_REFERENCE_HANDLE: + case IOCTL_IMDISK_GET_REFERENCED_HANDLE: + break; + + default: + KdPrint(("ImDisk: Invalid IOCTL %#x for control device.\n", + io_stack->Parameters.DeviceIoControl.IoControlCode)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + } + else + { + switch (io_stack->Parameters.DeviceIoControl.IoControlCode) + { + // Invalid IOCTL codes for this driver's disk devices. + case IOCTL_IMDISK_CREATE_DEVICE: + case IOCTL_IMDISK_REMOVE_DEVICE: + case IOCTL_IMDISK_REFERENCE_HANDLE: + case IOCTL_IMDISK_GET_REFERENCED_HANDLE: + KdPrint(("ImDisk: Invalid IOCTL %#x for disk device.\n", + io_stack->Parameters.DeviceIoControl.IoControlCode)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + } + + switch (io_stack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_IMDISK_SET_DEVICE_FLAGS: + KdPrint(("ImDisk: IOCTL_IMDISK_SET_DEVICE_FLAGS for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IMDISK_SET_DEVICE_FLAGS)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + { + PIMDISK_SET_DEVICE_FLAGS device_flags = (PIMDISK_SET_DEVICE_FLAGS) + Irp->AssociatedIrp.SystemBuffer; + + KIRQL irql = KeGetCurrentIrql(); + + if (irql >= DISPATCH_LEVEL) + { + status = STATUS_ACCESS_DENIED; + break; + } + + status = STATUS_SUCCESS; + + if (IMDISK_READONLY(device_flags->FlagsToChange)) + if (DeviceObject->DeviceType == FILE_DEVICE_DISK) + { + if ((IMDISK_READONLY(device_flags->FlagValues) != 0) & + (device_extension->special_file_count <= 0)) + { + DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; + device_extension->read_only = TRUE; + + device_flags->FlagsToChange &= ~IMDISK_OPTION_RO; + } + else + // It is not possible to make a file- or proxy virtual disk + // writable on the fly. (A physical image file or the proxy + // comm channel might not be opened for writing.) + if (device_extension->vm_disk) + { + DeviceObject->Characteristics &= ~FILE_READ_ONLY_DEVICE; + device_extension->read_only = FALSE; + + device_flags->FlagsToChange &= ~IMDISK_OPTION_RO; + } + } + + if (IMDISK_REMOVABLE(device_flags->FlagsToChange)) + if (DeviceObject->DeviceType == FILE_DEVICE_DISK) + { + if (IMDISK_REMOVABLE(device_flags->FlagValues)) + DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; + else + DeviceObject->Characteristics &= ~FILE_REMOVABLE_MEDIA; + + device_flags->FlagsToChange &= ~IMDISK_OPTION_REMOVABLE; + } + + if (device_flags->FlagsToChange & IMDISK_IMAGE_MODIFIED) + { + if (device_flags->FlagValues & IMDISK_IMAGE_MODIFIED) + device_extension->image_modified = TRUE; + else + device_extension->image_modified = FALSE; + + device_flags->FlagsToChange &= ~IMDISK_IMAGE_MODIFIED; + } + + if (irql == PASSIVE_LEVEL) + { + if (IMDISK_SPARSE_FILE(device_flags->FlagsToChange) && + IMDISK_SPARSE_FILE(device_flags->FlagValues) && + (!device_extension->use_proxy) && + (!device_extension->vm_disk)) + { + IO_STATUS_BLOCK io_status; + status = ZwFsControlFile(device_extension->file_handle, + NULL, + NULL, + NULL, + &io_status, + FSCTL_SET_SPARSE, + NULL, + 0, + NULL, + 0); + + if (NT_SUCCESS(status)) + { + device_extension->use_set_zero_data = TRUE; + device_flags->FlagsToChange &= ~IMDISK_OPTION_SPARSE_FILE; + } + } + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + } + else + KdPrint + (("ImDisk: Some flags cannot be changed at this IRQL (%#x).\n", + irql)); + + if (device_flags->FlagsToChange == 0) + status = STATUS_SUCCESS; + else if (NT_SUCCESS(status)) + status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(IMDISK_SET_DEVICE_FLAGS)) + { + Irp->IoStatus.Information = sizeof(IMDISK_SET_DEVICE_FLAGS); + } + + break; + + case IOCTL_IMDISK_REFERENCE_HANDLE: + { + KLOCK_QUEUE_HANDLE lock_handle; + PREFERENCED_OBJECT record; + + KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE for device %i.\n", + device_extension->device_number)); + + // This IOCTL requires work that must be done at IRQL < DISPATCH_LEVEL + // but must be done in the thread context of the calling application and + // not by the worker thread so therefore this check is done. Also, the + // control device does not have a worker thread so that is another + // reason. + if (KeGetCurrentIrql() > PASSIVE_LEVEL) + { + KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE not accessible " + "at current IRQL (%i).", KeGetCurrentIrql())); + + status = STATUS_ACCESS_DENIED; + + break; + } + + if ((io_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) && + !SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_TCB_PRIVILEGE), + Irp->RequestorMode)) + { + KdPrint(("ImDisk: IOCTL_IMDISK_REFERENCE_HANDLE not accessible, " + "privilege not held by calling thread.\n")); + + status = STATUS_ACCESS_DENIED; + + break; + } + + if ((io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(HANDLE)) | + (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PFILE_OBJECT))) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + record = (PREFERENCED_OBJECT) + ExAllocatePoolWithTag(NonPagedPool, + sizeof(REFERENCED_OBJECT), POOL_TAG); + + if (record == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + KdPrint(("ImDisk: Referencing handle %p.\n", + *(PHANDLE)Irp->AssociatedIrp.SystemBuffer)); + + status = ObReferenceObjectByHandle( + *(PHANDLE)Irp->AssociatedIrp.SystemBuffer, + FILE_READ_ATTRIBUTES | + FILE_READ_DATA | + FILE_WRITE_DATA, + *IoFileObjectType, + Irp->RequestorMode, + (PVOID*)&record->file_object, + NULL); + + KdPrint(("ImDisk: Status=%#x, PFILE_OBJECT %p.\n", + status, + record->file_object)); + + if (!NT_SUCCESS(status)) + { + ExFreePoolWithTag(record, POOL_TAG); + break; + } + + ImDiskAcquireLock(&ReferencedObjectsListLock, &lock_handle); + + InsertTailList(&ReferencedObjects, &record->list_entry); + + ImDiskReleaseLock(&lock_handle); + + *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer = record->file_object; + Irp->IoStatus.Information = sizeof(PFILE_OBJECT); + + break; + } + + case IOCTL_IMDISK_GET_REFERENCED_HANDLE: + { + KLOCK_QUEUE_HANDLE lock_handle; + PLIST_ENTRY list_entry; + + if (io_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) + { + status = STATUS_ACCESS_DENIED; + break; + } + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(PFILE_OBJECT)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + status = STATUS_OBJECT_NAME_NOT_FOUND; + + ImDiskAcquireLock(&ReferencedObjectsListLock, &lock_handle); + + for (list_entry = ReferencedObjects.Flink; + list_entry != &ReferencedObjects; + list_entry = list_entry->Flink) + { + PREFERENCED_OBJECT record = + CONTAINING_RECORD(list_entry, REFERENCED_OBJECT, list_entry); + + if (record->file_object == + *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer) + { + list_entry->Blink->Flink = list_entry->Flink; + list_entry->Flink->Blink = list_entry->Blink; + + ExFreePoolWithTag(record, POOL_TAG); + + status = STATUS_SUCCESS; + break; + } + } + + ImDiskReleaseLock(&lock_handle); + + if (NT_SUCCESS(status)) + { + KdPrint(("ImDisk: Successfully claimed referenced object %p.\n", + *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer)); + } + else + { + DbgPrint("ImDisk Warning: Requested %p not in referenced objects list.\n", + *(PFILE_OBJECT*)Irp->AssociatedIrp.SystemBuffer); + } + + break; + } + + case IOCTL_IMDISK_CREATE_DEVICE: + { + PIMDISK_CREATE_DATA create_data; + + KdPrint(("ImDisk: IOCTL_IMDISK_CREATE_DEVICE for device %i.\n", + device_extension->device_number)); + + // This IOCTL requires work that must be done at IRQL == PASSIVE_LEVEL + // but the control device has no worker thread (does not handle any + // other I/O) so therefore everything is done directly here. Therefore + // this IRQL check is necessary. + if (KeGetCurrentIrql() > PASSIVE_LEVEL) + { + status = STATUS_ACCESS_DENIED; + + break; + } + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IMDISK_CREATE_DATA) - sizeof(*create_data->FileName)) + { + KdPrint(("ImDisk: Invalid input buffer size (1). " + "Got: %u Expected at least: %u.\n", + io_stack->Parameters.DeviceIoControl.InputBufferLength, + (int)(sizeof(IMDISK_CREATE_DATA) - + sizeof(*create_data->FileName)))); + + status = STATUS_INVALID_PARAMETER; + + break; + } + + create_data = (PIMDISK_CREATE_DATA)Irp->AssociatedIrp.SystemBuffer; + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(IMDISK_CREATE_DATA) + + create_data->FileNameLength - + sizeof(*create_data->FileName)) + { + KdPrint(("ImDisk: Invalid input buffer size (2). " + "Got: %u Expected at least: %u.\n", + io_stack->Parameters.DeviceIoControl.InputBufferLength, + (int)(sizeof(IMDISK_CREATE_DATA) + + create_data->FileNameLength - + sizeof(*create_data->FileName)))); + + status = STATUS_INVALID_PARAMETER; + + break; + } + + status = ImDiskAddVirtualDisk(DeviceObject->DriverObject, + (PIMDISK_CREATE_DATA) + Irp->AssociatedIrp.SystemBuffer, + Irp->Tail.Overlay.Thread); + + if (NT_SUCCESS(status) && + (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= + io_stack->Parameters.DeviceIoControl.InputBufferLength)) + { + Irp->IoStatus.Information = + io_stack->Parameters.DeviceIoControl.OutputBufferLength; + } + + break; + } + + case IOCTL_IMDISK_REMOVE_DEVICE: + { + ULONG device_number; + KLOCK_QUEUE_HANDLE lock_handle; + PDEVICE_OBJECT device_object = + ImDiskCtlDevice->DriverObject->DeviceObject; + + KdPrint(("ImDisk: IOCTL_IMDISK_REMOVE_DEVICE.\n")); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(ULONG)) + { + KdPrint(("ImDisk: Invalid input buffer size (1). " + "Got: %u Expected at least: %u.\n", + io_stack->Parameters.DeviceIoControl.InputBufferLength, + (int)sizeof(ULONG))); + + status = STATUS_INVALID_PARAMETER; + + break; + } + + device_number = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + + KdPrint(("ImDisk: IOCTL_IMDISK_REMOVE_DEVICE for device %i.\n", + device_number)); + + status = STATUS_OBJECT_NAME_NOT_FOUND; + + ImDiskAcquireLock(&DeviceListLock, &lock_handle); + + while (device_object != NULL) + { + PDEVICE_EXTENSION device_extension = + (PDEVICE_EXTENSION)device_object->DeviceExtension; + + if (device_extension->device_number == device_number) + { + if (device_extension->special_file_count > 0) + { + status = STATUS_ACCESS_DENIED; + } + else + { + status = STATUS_SUCCESS; + } + break; + } + +#pragma warning(suppress: 28175) + device_object = device_object->NextDevice; + } + + ImDiskReleaseLock(&lock_handle); + + if (status == STATUS_SUCCESS) + { + ImDiskRemoveVirtualDisk(device_object); + } + + break; + } + + case IOCTL_DISK_EJECT_MEDIA: + case IOCTL_STORAGE_EJECT_MEDIA: + KdPrint(("ImDisk: IOCTL_DISK/STORAGE_EJECT_MEDIA for device %i.\n", + device_extension->device_number)); + + if (device_extension->special_file_count > 0) + { + status = STATUS_ACCESS_DENIED; + } + else + { + ImDiskRemoveVirtualDisk(DeviceObject); + + status = STATUS_SUCCESS; + } + + break; + + case IOCTL_IMDISK_QUERY_DRIVER: + { + KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_DRIVER for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength > + sizeof(ULONGLONG)) + { + KLOCK_QUEUE_HANDLE lock_handle; + ULONG max_items = + io_stack->Parameters.DeviceIoControl.OutputBufferLength >> 2; + ULONG current_item = 0; + PULONG device_list = (PULONG)Irp->AssociatedIrp.SystemBuffer; + PDEVICE_OBJECT device_object; + + KdPrint2(("ImDisk: Max number of items %i.\n", max_items)); + + ImDiskAcquireLock(&DeviceListLock, &lock_handle); + + for (device_object = DeviceObject->DriverObject->DeviceObject; + device_object != NULL; +#pragma warning(suppress: 28175) + device_object = device_object->NextDevice) + { + PDEVICE_EXTENSION this_device_extension = (PDEVICE_EXTENSION) + device_object->DeviceExtension; + + // Skip over control device + if (this_device_extension->device_number == -1) + continue; + + current_item++; + + KdPrint2(("ImDisk: Found device %i.\n", + this_device_extension->device_number)); + + if (current_item < max_items) + device_list[current_item] = + this_device_extension->device_number; + } + + ImDiskReleaseLock(&lock_handle); + + device_list[0] = current_item; + + KdPrint(("ImDisk: Found %i devices.\n", device_list[0])); + + if (current_item >= max_items) + { + Irp->IoStatus.Information = 1 << 2; + status = STATUS_SUCCESS; + } + else + { + Irp->IoStatus.Information = ((ULONG_PTR)current_item + 1) << 2; + status = STATUS_SUCCESS; + } + } + else + { + ULARGE_INTEGER DeviceList = { 0 }; + KLOCK_QUEUE_HANDLE lock_handle; + PDEVICE_OBJECT device_object; + ULONG HighestDeviceNumber = 0; + + ImDiskAcquireLock(&DeviceListLock, &lock_handle); + + for (device_object = DeviceObject->DriverObject->DeviceObject; + device_object != NULL; +#pragma warning(suppress: 28175) + device_object = device_object->NextDevice) + { + PDEVICE_EXTENSION this_device_extension = (PDEVICE_EXTENSION) + device_object->DeviceExtension; + + // Skip over control device + if (this_device_extension->device_number == -1) + continue; + + KdPrint2(("ImDisk: Found device %i.\n", + this_device_extension->device_number)); + + if (this_device_extension->device_number > HighestDeviceNumber) + HighestDeviceNumber = this_device_extension->device_number; + + if (this_device_extension->device_number < 64) + DeviceList.QuadPart |= + 1ULL << this_device_extension->device_number; + } + + ImDiskReleaseLock(&lock_handle); + + KdPrint(("ImDisk: 64 bit device list = 0x%.8x%.8x.\n", + DeviceList.HighPart, DeviceList.LowPart)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(ULONGLONG)) + { + *(PULONGLONG)Irp->AssociatedIrp.SystemBuffer = + DeviceList.QuadPart; + Irp->IoStatus.Information = sizeof(ULONGLONG); + + if (HighestDeviceNumber > 63) + status = STATUS_INVALID_PARAMETER; + else + status = STATUS_SUCCESS; + } + else if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(ULONG)) + { + *(PULONG)Irp->AssociatedIrp.SystemBuffer = DeviceList.LowPart; + Irp->IoStatus.Information = sizeof(ULONG); + + if (HighestDeviceNumber > 31) + status = STATUS_INVALID_PARAMETER; + else + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + + break; + } + + case IOCTL_IMDISK_QUERY_DEVICE: + { + PIMDISK_CREATE_DATA create_data; + + KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_DEVICE for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(IMDISK_CREATE_DATA) + + device_extension->file_name.Length + + sizeof(*create_data->FileName)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + create_data = (PIMDISK_CREATE_DATA)Irp->AssociatedIrp.SystemBuffer; + + create_data->DeviceNumber = device_extension->device_number; + create_data->DiskGeometry = device_extension->disk_geometry; + + create_data->Flags = 0; + if (device_extension->read_only) + create_data->Flags |= IMDISK_OPTION_RO; + + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + create_data->Flags |= IMDISK_OPTION_REMOVABLE; + + if (DeviceObject->DeviceType == FILE_DEVICE_UNKNOWN) + create_data->Flags |= IMDISK_DEVICE_TYPE_RAW; + else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) + create_data->Flags |= IMDISK_DEVICE_TYPE_CD | IMDISK_OPTION_RO; + else if (DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) + create_data->Flags |= IMDISK_DEVICE_TYPE_FD; + else + create_data->Flags |= IMDISK_DEVICE_TYPE_HD; + + if (device_extension->vm_disk) + create_data->Flags |= IMDISK_TYPE_VM; + else if (device_extension->use_proxy) + create_data->Flags |= IMDISK_TYPE_PROXY; + else + create_data->Flags |= IMDISK_TYPE_FILE; + + if (device_extension->awealloc_disk) + create_data->Flags |= IMDISK_FILE_TYPE_AWEALLOC; + else if (device_extension->parallel_io) + create_data->Flags |= IMDISK_FILE_TYPE_PARALLEL_IO; + + if (device_extension->image_modified) + create_data->Flags |= IMDISK_IMAGE_MODIFIED; + + if (device_extension->use_set_zero_data) + create_data->Flags |= IMDISK_OPTION_SPARSE_FILE; + + if (device_extension->shared_image) + create_data->Flags |= IMDISK_OPTION_SHARED_IMAGE; + + create_data->ImageOffset = device_extension->image_offset; + + create_data->DriveLetter = device_extension->drive_letter; + + create_data->FileNameLength = device_extension->file_name.Length; + + if (device_extension->file_name.Length > 0) + RtlCopyMemory(create_data->FileName, + device_extension->file_name.Buffer, + device_extension->file_name.Length); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(IMDISK_CREATE_DATA) + + create_data->FileNameLength - + sizeof(*create_data->FileName); + + break; + } + + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_CDROM_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY2: + { + KdPrint(("ImDisk: IOCTL_DISK/CDROM/STORAGE_CHECK_VERIFY/2 for " + "device %i.\n", device_extension->device_number)); + + if (device_extension->vm_disk) + { + KdPrint(("ImDisk: Faked verify ok on vm device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= + sizeof(ULONG)) + { + *(PULONG)Irp->AssociatedIrp.SystemBuffer = + device_extension->media_change_count; + + Irp->IoStatus.Information = sizeof(ULONG); + } + + status = STATUS_SUCCESS; + } + else + status = STATUS_PENDING; + + break; + } + + case IOCTL_IMDISK_QUERY_VERSION: + { + KdPrint(("ImDisk: IOCTL_IMDISK_QUERY_VERSION for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(ULONG)) + status = STATUS_INVALID_PARAMETER; + else + { + *(PULONG)Irp->AssociatedIrp.SystemBuffer = IMDISK_DRIVER_VERSION; + Irp->IoStatus.Information = sizeof(ULONG); + status = STATUS_SUCCESS; + } + + break; + } + + case IOCTL_IMDISK_IOCTL_PASS_THROUGH: + case IOCTL_IMDISK_FSCTL_PASS_THROUGH: + { + KdPrint(("ImDisk: IOCTL_IMDISK_IOCTL/FSCTL_PASS_THROUGH for device %i.\n", + device_extension->device_number)); + + if (device_extension->file_handle == NULL) + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(ULONG)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (device_extension->parallel_io && + (METHOD_FROM_CTL_CODE( + io_stack->Parameters.DeviceIoControl.IoControlCode) == + METHOD_BUFFERED)) + { + return ImDiskDeviceControlLowerDevice(Irp, device_extension); + } + + status = STATUS_PENDING; + break; + } + + case IOCTL_DISK_FORMAT_TRACKS: + case IOCTL_DISK_FORMAT_TRACKS_EX: + // Only several checks are done here + // Actual operation is done by the device thread + { + PFORMAT_PARAMETERS param; + PDISK_GEOMETRY geometry; + + KdPrint(("ImDisk: IOCTL_DISK_FORMAT_TRACKS for device %i.\n", + device_extension->device_number)); + + /* + if (~DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + */ + + // Media is writable? + + if (device_extension->read_only) + { + KdPrint(("ImDisk: Attempt to format write-protected image.\n")); + + status = STATUS_MEDIA_WRITE_PROTECTED; + break; + } + + // Check input parameter size + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(FORMAT_PARAMETERS)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + // Input parameter sanity check + + param = (PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; + geometry = (PDISK_GEOMETRY) + ExAllocatePoolWithTag(NonPagedPool, + sizeof(DISK_GEOMETRY), + POOL_TAG); + if (geometry == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(geometry, &device_extension->disk_geometry, + sizeof(DISK_GEOMETRY)); + + geometry->Cylinders.QuadPart /= geometry->TracksPerCylinder; + geometry->Cylinders.QuadPart /= geometry->SectorsPerTrack; + geometry->Cylinders.QuadPart /= geometry->BytesPerSector; + + if ((param->StartHeadNumber > geometry->TracksPerCylinder - 1) || + (param->EndHeadNumber > geometry->TracksPerCylinder - 1) || + ((LONGLONG)param->StartCylinderNumber > + geometry->Cylinders.QuadPart) || + ((LONGLONG)param->EndCylinderNumber > + geometry->Cylinders.QuadPart) || + (param->EndCylinderNumber < param->StartCylinderNumber)) + { + ExFreePoolWithTag(geometry, POOL_TAG); + status = STATUS_INVALID_PARAMETER; + break; + } + + if ((param->StartCylinderNumber * geometry->TracksPerCylinder * + geometry->BytesPerSector * geometry->SectorsPerTrack + + param->StartHeadNumber * geometry->BytesPerSector * + geometry->SectorsPerTrack >= + device_extension->disk_geometry.Cylinders.QuadPart) | + (param->EndCylinderNumber * geometry->TracksPerCylinder * + geometry->BytesPerSector * geometry->SectorsPerTrack + + param->EndHeadNumber * geometry->BytesPerSector * + geometry->SectorsPerTrack >= + device_extension->disk_geometry.Cylinders.QuadPart)) + { + ExFreePoolWithTag(geometry, POOL_TAG); + status = STATUS_INVALID_PARAMETER; + break; + } + + // If this is an EX request then make a couple of extra checks + + if (io_stack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_DISK_FORMAT_TRACKS_EX) + { + PFORMAT_EX_PARAMETERS exparam; + ULONG paramsize; + + KdPrint(("ImDisk: IOCTL_DISK_FORMAT_TRACKS_EX for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(FORMAT_EX_PARAMETERS)) + { + ExFreePoolWithTag(geometry, POOL_TAG); + status = STATUS_INVALID_PARAMETER; + break; + } + + exparam = (PFORMAT_EX_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; + + paramsize = sizeof(FORMAT_EX_PARAMETERS) + + exparam->SectorsPerTrack * sizeof(USHORT) + - sizeof(USHORT); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + paramsize || + exparam->FormatGapLength > geometry->SectorsPerTrack || + exparam->SectorsPerTrack != geometry->SectorsPerTrack) + { + ExFreePoolWithTag(geometry, POOL_TAG); + status = STATUS_INVALID_PARAMETER; + break; + } + } + + ExFreePoolWithTag(geometry, POOL_TAG); + status = STATUS_PENDING; + break; + } + + case IOCTL_DISK_GROW_PARTITION: + { + PDISK_GROW_PARTITION grow_partition; + + KdPrint(("ImDisk: IOCTL_DISK_GROW_PARTITION for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength != + sizeof(DISK_GROW_PARTITION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (device_extension->read_only) + { + status = STATUS_MEDIA_WRITE_PROTECTED; + break; + } + + grow_partition = (PDISK_GROW_PARTITION) + Irp->AssociatedIrp.SystemBuffer; + + // Check so we don't get a smaller disk with these parameters + if ((grow_partition->PartitionNumber != 1) | + (device_extension->disk_geometry.Cylinders.QuadPart + + grow_partition->BytesToGrow.QuadPart < + device_extension->disk_geometry.Cylinders.QuadPart)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + status = STATUS_PENDING; + break; + } + + case IOCTL_DISK_UPDATE_PROPERTIES: + { + status = STATUS_SUCCESS; + break; + } + + case IOCTL_DISK_GET_MEDIA_TYPES: + case IOCTL_STORAGE_GET_MEDIA_TYPES: + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + case IOCTL_CDROM_GET_DRIVE_GEOMETRY: + case IOCTL_DISK_UPDATE_DRIVE_SIZE: + { + PDISK_GEOMETRY geometry; + + KdPrint(("ImDisk: IOCTL_DISK/STORAGE_GET_MEDIA_TYPES/DRIVE_GEOMETRY " + "for device %i.\n", device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(device_extension->disk_geometry)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer; + *geometry = device_extension->disk_geometry; + geometry->Cylinders.QuadPart /= geometry->TracksPerCylinder; + geometry->Cylinders.QuadPart /= geometry->SectorsPerTrack; + geometry->Cylinders.QuadPart /= geometry->BytesPerSector; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); + break; + } + + case IOCTL_DISK_GET_LENGTH_INFO: + { + KdPrint(("ImDisk: IOCTL_DISK_GET_LENGTH_INFO for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(GET_LENGTH_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + ((PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer)-> + Length.QuadPart = + device_extension->disk_geometry.Cylinders.QuadPart; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); + + break; + } + + case IOCTL_DISK_GET_PARTITION_INFO: + { + PPARTITION_INFORMATION partition_information; + + KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + partition_information = + (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + partition_information->StartingOffset.QuadPart = + (LONGLONG)device_extension->disk_geometry.BytesPerSector * + device_extension->disk_geometry.SectorsPerTrack; + partition_information->PartitionLength = + device_extension->disk_geometry.Cylinders; + partition_information->HiddenSectors = 1; + partition_information->PartitionNumber = 1; + partition_information->PartitionType = PARTITION_HUGE; + partition_information->BootIndicator = FALSE; + partition_information->RecognizedPartition = FALSE; + partition_information->RewritePartition = FALSE; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); + + break; + } + + case IOCTL_DISK_GET_PARTITION_INFO_EX: + { + PPARTITION_INFORMATION_EX partition_information_ex; + + KdPrint(("ImDisk: IOCTL_DISK_GET_PARTITION_INFO_EX for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(PARTITION_INFORMATION_EX)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + partition_information_ex = + (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer; + + partition_information_ex->PartitionStyle = PARTITION_STYLE_MBR; + partition_information_ex->StartingOffset.QuadPart = + (LONGLONG)device_extension->disk_geometry.BytesPerSector * + device_extension->disk_geometry.SectorsPerTrack; + partition_information_ex->PartitionLength = + device_extension->disk_geometry.Cylinders; + partition_information_ex->PartitionNumber = 1; + partition_information_ex->RewritePartition = FALSE; + partition_information_ex->Mbr.PartitionType = PARTITION_HUGE; + partition_information_ex->Mbr.BootIndicator = FALSE; + partition_information_ex->Mbr.RecognizedPartition = FALSE; + partition_information_ex->Mbr.HiddenSectors = 1; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); + + break; + } + + case IOCTL_DISK_IS_WRITABLE: + { + KdPrint(("ImDisk: IOCTL_DISK_IS_WRITABLE for device %i.\n", + device_extension->device_number)); + + if (!device_extension->read_only) + status = STATUS_SUCCESS; + else + status = STATUS_MEDIA_WRITE_PROTECTED; + + break; + } + + case IOCTL_DISK_MEDIA_REMOVAL: + case IOCTL_STORAGE_MEDIA_REMOVAL: + { + KdPrint(("ImDisk: IOCTL_DISK/STORAGE_MEDIA_REMOVAL for device %i.\n", + device_extension->device_number)); + + status = STATUS_SUCCESS; + break; + } + + case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: + { + PDEVICE_MANAGE_DATA_SET_ATTRIBUTES attrs = + (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES) + Irp->AssociatedIrp.SystemBuffer; + + if ((io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) || + (io_stack->Parameters.DeviceIoControl.InputBufferLength < + (attrs->DataSetRangesOffset + attrs->DataSetRangesLength))) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (attrs->Action != DeviceDsmAction_Trim) + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + status = STATUS_SUCCESS; + + int items = attrs->DataSetRangesLength / + sizeof(DEVICE_DATA_SET_RANGE); + + if (items <= 0) + { + break; + } + + if ((device_extension->use_proxy && !device_extension->proxy_unmap) || + device_extension->vm_disk || + device_extension->awealloc_disk) + { + break; + } + + if (device_extension->use_proxy) + { + status = STATUS_PENDING; + break; + } + + FILE_ZERO_DATA_INFORMATION zerodata = { 0 }; + + PDEVICE_DATA_SET_RANGE range = (PDEVICE_DATA_SET_RANGE) + ((PUCHAR)attrs + attrs->DataSetRangesOffset); + + for (int i = 0; i < items; i++) + { + KdPrint(("ImDisk: Trim request 0x%I64X bytes at 0x%I64X\n", + range[i].LengthInBytes, range[i].StartingOffset)); + + zerodata.FileOffset.QuadPart = range[i].StartingOffset + + device_extension->image_offset.QuadPart; + + zerodata.BeyondFinalZero.QuadPart = zerodata.FileOffset.QuadPart + + range[i].LengthInBytes; + + status = ZwFsControlFile( + device_extension->file_handle, + NULL, + NULL, + NULL, + &Irp->IoStatus, + FSCTL_SET_ZERO_DATA, + &zerodata, + sizeof(zerodata), + NULL, + 0); + + KdPrint(("ImDisk: FSCTL_SET_ZERO_DATA result: 0x%#X\n", status)); + + if (!NT_SUCCESS(status)) + { + break; + } + } + + if (!device_extension->no_file_level_trim) + { + IO_STATUS_BLOCK io_status; + ULONG fltrim_size = FIELD_OFFSET(FILE_LEVEL_TRIM, Ranges) + + (items * sizeof(FILE_LEVEL_TRIM_RANGE)); + + PFILE_LEVEL_TRIM fltrim = (PFILE_LEVEL_TRIM) + ExAllocatePoolWithTag(PagedPool, fltrim_size, POOL_TAG); + + if (fltrim == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + fltrim->NumRanges = items; + for (int i = 0; i < items; i++) + { + KdPrint(("ImDisk: Trim request 0x%I64X bytes at 0x%I64X\n", + range[i].LengthInBytes, range[i].StartingOffset)); + + fltrim->Ranges[i].Length = range[i].LengthInBytes; + fltrim->Ranges[i].Offset = range[i].StartingOffset + + device_extension->image_offset.QuadPart; + } + + status = ZwFsControlFile( + device_extension->file_handle, + NULL, + NULL, + NULL, + &io_status, + FSCTL_FILE_LEVEL_TRIM, + fltrim, + fltrim_size, + NULL, + 0); + + ExFreePoolWithTag(fltrim, POOL_TAG); + + KdPrint(("ImDisk: FSCTL_FILE_LEVEL_TRIM result: %#x\n", status)); + + if (!NT_SUCCESS(status)) + { + device_extension->no_file_level_trim = TRUE; + status = STATUS_SUCCESS; + } + } + + break; + } + + case IOCTL_CDROM_GET_LAST_SESSION: + { + status = STATUS_SUCCESS; + break; + } + + case IOCTL_CDROM_READ_TOC: + { + PCDROM_TOC cdrom_toc; + + KdPrint(("ImDisk: IOCTL_CDROM_READ_TOC for device %i.\n", + device_extension->device_number)); + + if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) + { + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(CDROM_TOC)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + cdrom_toc = (PCDROM_TOC)Irp->AssociatedIrp.SystemBuffer; + + RtlZeroMemory(cdrom_toc, sizeof(CDROM_TOC)); + + cdrom_toc->FirstTrack = 1; + cdrom_toc->LastTrack = 1; + cdrom_toc->TrackData[0].Control = TOC_DATA_TRACK; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(CDROM_TOC); + + break; + } + + case IOCTL_DISK_SET_PARTITION_INFO: + { + KdPrint(("ImDisk: IOCTL_DISK_SET_PARTITION_INFO for device %i.\n", + device_extension->device_number)); + + if (device_extension->read_only) + { + KdPrint(("ImDisk: Attempt to partition read-only image.\n")); + + status = STATUS_MEDIA_WRITE_PROTECTED; + break; + } + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SET_PARTITION_INFORMATION)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + status = STATUS_SUCCESS; + break; + } + + case IOCTL_DISK_SET_PARTITION_INFO_EX: + { + PSET_PARTITION_INFORMATION_EX partition_information_ex; + + KdPrint(("ImDisk: IOCTL_DISK_SET_PARTITION_INFO_EX for device %i.\n", + device_extension->device_number)); + + if (device_extension->read_only) + { + KdPrint(("ImDisk: Attempt to partition read-only image.\n")); + + status = STATUS_MEDIA_WRITE_PROTECTED; + break; + } + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(SET_PARTITION_INFORMATION_EX)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + partition_information_ex = (PSET_PARTITION_INFORMATION_EX) + Irp->AssociatedIrp.SystemBuffer; + + if (partition_information_ex->PartitionStyle != PARTITION_STYLE_MBR) + { + status = STATUS_UNSUCCESSFUL; + } + else + { + status = STATUS_SUCCESS; + } + + break; + } + + case IOCTL_DISK_VERIFY: + { + PVERIFY_INFORMATION verify_information; + + KdPrint(("ImDisk: IOCTL_DISK_VERIFY for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(VERIFY_INFORMATION)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + verify_information = (PVERIFY_INFORMATION) + Irp->AssociatedIrp.SystemBuffer; + + if (device_extension->read_only) + { + KdPrint(("ImDisk: Attempt to verify read-only media.\n")); + + status = STATUS_MEDIA_WRITE_PROTECTED; + break; + } + + if (verify_information->StartingOffset.QuadPart + + verify_information->Length > + device_extension->disk_geometry.Cylinders.QuadPart) + { + KdPrint(("ImDisk: Attempt to verify beyond image size.\n")); + + status = STATUS_NONEXISTENT_SECTOR; + break; + } + + status = STATUS_SUCCESS; + break; + } + + // Ver 1.0.2 does no longer handle IOCTL_STORAGE_GET_DEVICE_NUMBER. + // It was a very ugly attempt to workaround some problems that do not + // seem to exist any longer anyway. The data returned here made no sense + // actually so in order to not risk breaking more things in the future I + // have removed it completely. + /* + case IOCTL_STORAGE_GET_DEVICE_NUMBER: + { + PSTORAGE_DEVICE_NUMBER device_number; + + KdPrint(("ImDisk: IOCTL_STORAGE_GET_DEVICE_NUMBER for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(STORAGE_DEVICE_NUMBER)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + device_number = (PSTORAGE_DEVICE_NUMBER) + Irp->AssociatedIrp.SystemBuffer; + + device_number->DeviceType = DeviceObject->DeviceType; + device_number->DeviceNumber = (ULONG) DeviceObject; + device_number->PartitionNumber = (ULONG) -1; + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER); + + break; + } + */ + + case IOCTL_STORAGE_GET_HOTPLUG_INFO: + { + PSTORAGE_HOTPLUG_INFO hotplug_info; + + KdPrint(("ImDisk: IOCTL_STORAGE_GET_HOTPLUG_INFO for device %i.\n", + device_extension->device_number)); + + if (io_stack->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(STORAGE_HOTPLUG_INFO)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + hotplug_info = (PSTORAGE_HOTPLUG_INFO) + Irp->AssociatedIrp.SystemBuffer; + + hotplug_info->Size = sizeof(STORAGE_HOTPLUG_INFO); + if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + { + hotplug_info->MediaRemovable = TRUE; + hotplug_info->MediaHotplug = TRUE; + hotplug_info->DeviceHotplug = TRUE; + hotplug_info->WriteCacheEnableOverride = FALSE; + } + else + { + hotplug_info->MediaRemovable = FALSE; + hotplug_info->MediaHotplug = FALSE; + hotplug_info->DeviceHotplug = FALSE; + hotplug_info->WriteCacheEnableOverride = FALSE; + } + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO); + + break; + } + + /* case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: */ + /* { */ + /* PMOUNTDEV_NAME mountdev_name = Irp->AssociatedIrp.SystemBuffer; */ + /* int chars; */ + + /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME for device %i.\n", */ + /* device_extension->device_number)); */ + + /* if ((io_stack->Parameters.DeviceIoControl.OutputBufferLength == 4) & */ + /* (device_extension->drive_letter != 0)) */ + /* { */ + /* mountdev_name->Name[0] = device_extension->drive_letter; */ + /* mountdev_name->Name[1] = L':'; */ + /* chars = 2; */ + /* } */ + /* else */ + /* chars = */ + /* _snwprintf(mountdev_name->Name, */ + /* (io_stack-> */ + /* Parameters.DeviceIoControl.OutputBufferLength - */ + /* FIELD_OFFSET(MOUNTDEV_NAME, Name)) >> 1, */ + /* IMDISK_DEVICE_BASE_NAME L"%u", */ + /* device_extension->device_number); */ + /* // else */ + /* // chars = */ + /* // _snwprintf(mountdev_name->Name, */ + /* // (io_stack-> */ + /* // Parameters.DeviceIoControl.OutputBufferLength - */ + /* // FIELD_OFFSET(MOUNTDEV_NAME, Name)) >> 1, */ + /* // L"\\DosDevices\\%wc:", */ + /* // device_extension->drive_letter); */ + + /* if (chars < 0) */ + /* { */ + /* if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= */ + /* FIELD_OFFSET(MOUNTDEV_NAME, Name) + */ + /* sizeof(mountdev_name->NameLength)) */ + /* mountdev_name->NameLength = sizeof(IMDISK_DEVICE_BASE_NAME) + */ + /* 20; */ + + /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME overflow, " */ + /* "buffer length %u, returned %i.\n", */ + /* io_stack->Parameters.DeviceIoControl.OutputBufferLength, */ + /* chars)); */ + + /* status = STATUS_BUFFER_OVERFLOW; */ + + /* if (io_stack->Parameters.DeviceIoControl.OutputBufferLength >= */ + /* sizeof(MOUNTDEV_NAME)) */ + /* Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); */ + + /* break; */ + /* } */ + + /* mountdev_name->NameLength = (USHORT) chars << 1; */ + + /* status = STATUS_SUCCESS; */ + /* Irp->IoStatus.Information = */ + /* FIELD_OFFSET(MOUNTDEV_NAME, Name) + mountdev_name->NameLength; */ + + /* KdPrint(("ImDisk: IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returning %ws, " */ + /* "length %u total %u.\n", */ + /* mountdev_name->Name, mountdev_name->NameLength, */ + /* Irp->IoStatus.Information)); */ + + /* break; */ + /* } */ + + default: + { + KdPrint(("ImDisk: Unknown IOCTL %#x.\n", + io_stack->Parameters.DeviceIoControl.IoControlCode)); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + if (status == STATUS_PENDING) + { + IoMarkIrpPending(Irp); + + ImDiskInterlockedInsertTailList(&device_extension->list_head, + &Irp->Tail.Overlay.ListEntry, + &device_extension->list_lock); + + KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); + } + else + { + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + } + + return status; +} + +NTSTATUS +ImDiskDispatchFlushBuffers(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + io_stack = IoGetCurrentIrpStackLocation(Irp); + + // The control device cannot receive flush dispatch. + if (DeviceObject == ImDiskCtlDevice) + { + KdPrint(("ImDisk: flush function %#x invalid for control device.\n", + io_stack->MinorFunction)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + return status; + } + + KdPrint(("ImDisk: Device %i received flush function %#x IRP %p.\n", + device_extension->device_number, + io_stack->MinorFunction, + Irp)); + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: flush dispatch on device %i that is being removed.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if (device_extension->read_only) + { + status = STATUS_MEDIA_WRITE_PROTECTED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + if (device_extension->use_proxy | device_extension->vm_disk) + { + Irp->IoStatus.Status = STATUS_SUCCESS; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return STATUS_SUCCESS; + } + else if (device_extension->parallel_io) + { + return ImDiskReadWriteLowerDevice(Irp, device_extension); + } + else + { + IoMarkIrpPending(Irp); + + ImDiskInterlockedInsertTailList(&device_extension->list_head, + &Irp->Tail.Overlay.ListEntry, + &device_extension->list_lock); + + KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); + + return STATUS_PENDING; + } +} + +NTSTATUS +ImDiskDispatchQueryInformation(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + io_stack = IoGetCurrentIrpStackLocation(Irp); + + KdPrint2(("ImDisk: QueryInformation: %u.\n", + io_stack->Parameters.QueryFile.FileInformationClass)); + + // The control device cannot receive PnP dispatch. + if (DeviceObject == ImDiskCtlDevice) + { + KdPrint(("ImDisk: QueryInformation function %#x invalid for control device.\n", + io_stack->Parameters.QueryFile.FileInformationClass)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + return status; + } + + KdPrint(("ImDisk: Device %i received QueryInformation function %#x IRP %p.\n", + device_extension->device_number, + io_stack->Parameters.QueryFile.FileInformationClass, + Irp)); + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: QueryInformation dispatch on device %i that is being removed.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, + io_stack->Parameters.QueryFile.Length); + + switch (io_stack->Parameters.QueryFile.FileInformationClass) + { + case FileStandardInformation: + { + PFILE_STANDARD_INFORMATION standard_info = + (PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (io_stack->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) + { + Irp->IoStatus.Information = 0; + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + standard_info->AllocationSize = + standard_info->EndOfFile = device_extension->disk_geometry.Cylinders; + + Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION); + status = STATUS_SUCCESS; + break; + } + + case FilePositionInformation: + { + PFILE_POSITION_INFORMATION position_info = + (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (io_stack->Parameters.QueryFile.Length < + sizeof(FILE_POSITION_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (io_stack->FileObject != NULL) + { + position_info->CurrentByteOffset = + io_stack->FileObject->CurrentByteOffset; + } + + Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION); + + status = STATUS_SUCCESS; + break; + } + + default: + KdPrint(("ImDisk: Unknown QueryInformation function %#x.\n", + io_stack->Parameters.QueryFile.FileInformationClass)); + + status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + } + + if (status == STATUS_PENDING) + { + IoMarkIrpPending(Irp); + + ImDiskInterlockedInsertTailList(&device_extension->list_head, + &Irp->Tail.Overlay.ListEntry, + &device_extension->list_lock); + + KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); + } + else + { + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + } + + return status; +} + +NTSTATUS +ImDiskDispatchSetInformation(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + io_stack = IoGetCurrentIrpStackLocation(Irp); + + KdPrint2(("ImDisk: SetInformation: %u.\n", + io_stack->Parameters.SetFile.FileInformationClass)); + + // The control device cannot receive PnP dispatch. + if (DeviceObject == ImDiskCtlDevice) + { + KdPrint(("ImDisk: SetInformation function %#x invalid for control device.\n", + io_stack->Parameters.SetFile.FileInformationClass)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + return status; + } + + KdPrint(("ImDisk: Device %i received SetInformation function %#x IRP %p.\n", + device_extension->device_number, + io_stack->Parameters.SetFile.FileInformationClass, + Irp)); + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: SetInformation dispatch on device %i that is being removed.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + switch (io_stack->Parameters.SetFile.FileInformationClass) + { + case FilePositionInformation: + { + PFILE_POSITION_INFORMATION position_info = + (PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (io_stack->Parameters.SetFile.Length < + sizeof(FILE_POSITION_INFORMATION)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (io_stack->FileObject != NULL) + { + io_stack->FileObject->CurrentByteOffset = + position_info->CurrentByteOffset; + } + + status = STATUS_SUCCESS; + break; + } + + case FileBasicInformation: + case FileDispositionInformation: + case FileValidDataLengthInformation: + status = STATUS_SUCCESS; + break; + + default: + { + KdPrint(("ImDisk: Unknown SetInformation function %#x.\n", + io_stack->Parameters.SetFile.FileInformationClass)); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + KdPrint2(("ImDisk: SetFile.FileInformationClass %u result status %#x\n", + io_stack->Parameters.SetFile.FileInformationClass, status)); + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + + return status; +} + +NTSTATUS +ImDiskDispatchPnP(IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp) +{ + PDEVICE_EXTENSION device_extension; + PIO_STACK_LOCATION io_stack; + NTSTATUS status; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; + + io_stack = IoGetCurrentIrpStackLocation(Irp); + + // The control device cannot receive PnP dispatch. + if (DeviceObject == ImDiskCtlDevice) + { + KdPrint(("ImDisk: PnP function %#x invalid for control device.\n", + io_stack->MinorFunction)); + + status = STATUS_INVALID_DEVICE_REQUEST; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + KdPrint(("ImDisk: Device %i received PnP function %#x IRP %p.\n", + device_extension->device_number, + io_stack->MinorFunction, + Irp)); + + if (KeReadStateEvent(&device_extension->terminate_thread) != 0) + { + KdPrint(("ImDisk: PnP dispatch on device %i that is being removed.\n", + device_extension->device_number)); + + status = STATUS_DEVICE_REMOVED; + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; + } + + switch (io_stack->MinorFunction) + { + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + KdPrint(("ImDisk: Device %i got IRP_MN_DEVICE_USAGE_NOTIFICATION.\n", + device_extension->device_number)); + + switch (io_stack->Parameters.UsageNotification.Type) + { + case DeviceUsageTypePaging: + case DeviceUsageTypeDumpFile: + if (device_extension->read_only) + { + status = STATUS_MEDIA_WRITE_PROTECTED; + } + else + { + if (io_stack->Parameters.UsageNotification.InPath == TRUE) + { + // Not needed anymore. Device thread routines are non-pageable. + + //(VOID)MmLockPagableCodeSection((PVOID)(ULONG_PTR)ImDiskDeviceThread); + } + } + + IoAdjustPagingPathCount + (&device_extension->special_file_count, + io_stack->Parameters.UsageNotification.InPath); + + status = STATUS_SUCCESS; + + break; + + default: + status = STATUS_NOT_SUPPORTED; + } + + break; + + default: + KdPrint(("ImDisk: Unknown PnP function %#x.\n", + io_stack->MinorFunction)); + + status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + } + + if (status == STATUS_PENDING) + { + IoMarkIrpPending(Irp); + + ImDiskInterlockedInsertTailList(&device_extension->list_head, + &Irp->Tail.Overlay.ListEntry, + &device_extension->list_lock); + + KeSetEvent(&device_extension->request_event, (KPRIORITY)0, FALSE); + } + else + { + Irp->IoStatus.Status = status; + + IoCompleteRequest(Irp, IO_DISK_INCREMENT); + } + + return status; +} + diff --git a/sys/lowerdev.cpp b/sys/lowerdev.cpp new file mode 100644 index 0000000..fb0a867 --- /dev/null +++ b/sys/lowerdev.cpp @@ -0,0 +1,331 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +typedef struct _LOWER_DEVICE_WORK_ITEM +{ + PIRP OriginalIrp; + PDEVICE_EXTENSION DeviceExtension; + LONGLONG OriginalOffset; + PUCHAR AllocatedBuffer; + PUCHAR SystemBuffer; + BOOLEAN CopyBack; +} LOWER_DEVICE_WORK_ITEM, *PLOWER_DEVICE_WORK_ITEM; + +VOID +ImDiskFreeIrpWithMdls(PIRP Irp) +{ + PMDL mdl; + PMDL nextMdl; + for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) + { + nextMdl = mdl->Next; + + if (mdl->MdlFlags & MDL_PAGES_LOCKED) + { + MmUnlockPages(mdl); + } + + IoFreeMdl(mdl); + } + + Irp->MdlAddress = NULL; + + IoFreeIrp(Irp); +} + +NTSTATUS +ImDiskReadWriteLowerDeviceCompletion(PDEVICE_OBJECT DeviceObject, + PIRP Irp, PVOID Context) +{ + PLOWER_DEVICE_WORK_ITEM item = (PLOWER_DEVICE_WORK_ITEM)Context; + + ASSERT(item != NULL); + + __analysis_assume(item != NULL); + + UNREFERENCED_PARAMETER(DeviceObject); + + item->OriginalIrp->IoStatus = Irp->IoStatus; + + if (!NT_SUCCESS(Irp->IoStatus.Status)) + { + KdPrint(("ImDiskReadWriteLowerDeviceCompletion: Parallel I/O failed with status %#x\n", + Irp->IoStatus.Status)); + } + else + { + if (item->CopyBack) + { + RtlCopyMemory(item->SystemBuffer, item->AllocatedBuffer, + Irp->IoStatus.Information); + } + + if (item->AllocatedBuffer != NULL) + { + KLOCK_QUEUE_HANDLE lock_handle; + + ImDiskAcquireLock(&item->DeviceExtension->last_io_lock, &lock_handle); + + if (item->DeviceExtension->last_io_data != NULL) + { + ExFreePoolWithTag(item->DeviceExtension->last_io_data, + POOL_TAG); + } + + item->DeviceExtension->last_io_data = item->AllocatedBuffer; + + item->DeviceExtension->last_io_offset = item->OriginalOffset; + item->DeviceExtension->last_io_length = + (ULONG)Irp->IoStatus.Information; + + ImDiskReleaseLock(&lock_handle); + } + } + + if (Irp->MdlAddress != item->OriginalIrp->MdlAddress) + { + ImDiskFreeIrpWithMdls(Irp); + } + else + { + IoFreeIrp(Irp); + } + + IoCompleteRequest(item->OriginalIrp, IO_DISK_INCREMENT); + + ExFreePoolWithTag(item, POOL_TAG); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +ImDiskDeviceControlLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension) +{ + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION lower_io_stack = IoGetNextIrpStackLocation(Irp); + + IoCopyCurrentIrpStackLocationToNext(Irp); + + if (io_stack->Parameters.DeviceIoControl.IoControlCode == + IOCTL_IMDISK_FSCTL_PASS_THROUGH) + { + lower_io_stack->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; + } + + lower_io_stack->Parameters.DeviceIoControl.IoControlCode = + *(PULONG)Irp->AssociatedIrp.SystemBuffer; + + lower_io_stack->Parameters.DeviceIoControl.InputBufferLength -= + sizeof(ULONG); + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, + (PUCHAR)Irp->AssociatedIrp.SystemBuffer + sizeof(ULONG), + lower_io_stack->Parameters.DeviceIoControl.InputBufferLength); + + lower_io_stack->FileObject = DeviceExtension->file_object; + + return IoCallDriver(DeviceExtension->dev_object, Irp); +} + +NTSTATUS +ImDiskReadWriteLowerDevice(PIRP Irp, PDEVICE_EXTENSION DeviceExtension) +{ + PIO_STACK_LOCATION io_stack = IoGetCurrentIrpStackLocation(Irp); + PIO_STACK_LOCATION lower_io_stack; + PIRP lower_irp; + PLOWER_DEVICE_WORK_ITEM item; + + // If image file is a direct I/O device, we simply forward the IRP with + // correct FILE_OBJECT and adjusted offset if needed. + if ((DeviceExtension->dev_object->Flags & DO_DIRECT_IO) == DO_DIRECT_IO) + { + IoCopyCurrentIrpStackLocationToNext(Irp); + + lower_io_stack = IoGetNextIrpStackLocation(Irp); + + lower_io_stack->Parameters.Read.ByteOffset.QuadPart += + DeviceExtension->image_offset.QuadPart; + + lower_io_stack->FileObject = DeviceExtension->file_object; + + if ((io_stack->MajorFunction == IRP_MJ_WRITE) && + !DeviceExtension->image_modified) + { + DeviceExtension->image_modified = TRUE; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + } + + return IoCallDriver(DeviceExtension->dev_object, Irp); + } + + // This goes for image files with DO_BUFFERED_IO or DO_NEITHER_IO. + // We allocate NP pool as buffer for a request to send down. A completion + // routine takes care of copying read operation data back to original IRP. + + item = (PLOWER_DEVICE_WORK_ITEM) + ExAllocatePoolWithTag(NonPagedPool, + sizeof(*item), POOL_TAG); + + if (item == NULL) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(item, sizeof(*item)); + + item->OriginalIrp = Irp; + item->DeviceExtension = DeviceExtension; + item->OriginalOffset = io_stack->Parameters.Read.ByteOffset.QuadPart; + + if ((io_stack->MajorFunction == IRP_MJ_READ) || + (io_stack->MajorFunction == IRP_MJ_WRITE)) + { + item->SystemBuffer = (PUCHAR) + MmGetSystemAddressForMdlSafe(Irp->MdlAddress, + NormalPagePriority); + + if (item->SystemBuffer == NULL) + { + ExFreePoolWithTag(item, POOL_TAG); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + lower_irp = IoAllocateIrp(DeviceExtension->dev_object->StackSize, FALSE); + + if (lower_irp == NULL) + { + ExFreePoolWithTag(item, POOL_TAG); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + lower_io_stack = IoGetNextIrpStackLocation(lower_irp); + + lower_io_stack->MajorFunction = io_stack->MajorFunction; + lower_io_stack->Parameters = io_stack->Parameters; + + if ((io_stack->MajorFunction == IRP_MJ_READ) || + (io_stack->MajorFunction == IRP_MJ_WRITE)) + { + lower_irp->AssociatedIrp.SystemBuffer = + lower_irp->UserBuffer = + item->AllocatedBuffer = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, + io_stack->Parameters.Read.Length, POOL_TAG); + + if (item->AllocatedBuffer == NULL) + { + ImDiskFreeIrpWithMdls(lower_irp); + ExFreePoolWithTag(item, POOL_TAG); + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (io_stack->MajorFunction == IRP_MJ_WRITE) + { + RtlCopyMemory(item->AllocatedBuffer, item->SystemBuffer, + io_stack->Parameters.Write.Length); + } + else if (io_stack->MajorFunction == IRP_MJ_READ) + { + item->CopyBack = TRUE; + } + } + + lower_irp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; + + if (io_stack->MajorFunction == IRP_MJ_READ) + { + lower_irp->Flags |= IRP_READ_OPERATION; + } + else if (io_stack->MajorFunction == IRP_MJ_WRITE) + { + lower_irp->Flags |= IRP_WRITE_OPERATION; + lower_io_stack->Flags |= SL_WRITE_THROUGH; + } + + lower_irp->Flags |= IRP_NOCACHE; + + lower_io_stack->Parameters.Read = io_stack->Parameters.Read; + lower_io_stack->Parameters.Read.ByteOffset.QuadPart += + DeviceExtension->image_offset.QuadPart; + + lower_io_stack->FileObject = DeviceExtension->file_object; + + if ((io_stack->MajorFunction == IRP_MJ_WRITE) && + (!DeviceExtension->image_modified)) + { + DeviceExtension->image_modified = TRUE; + + // Fire refresh event + if (RefreshEvent != NULL) + KePulseEvent(RefreshEvent, 0, FALSE); + } + + IoSetCompletionRoutine(lower_irp, ImDiskReadWriteLowerDeviceCompletion, + item, TRUE, TRUE, TRUE); + + IoMarkIrpPending(Irp); + + (void)IoCallDriver(DeviceExtension->dev_object, lower_irp); + + return STATUS_PENDING; +} + diff --git a/sys/proxy.cpp b/sys/proxy.cpp new file mode 100644 index 0000000..95fe82e --- /dev/null +++ b/sys/proxy.cpp @@ -0,0 +1,957 @@ +/* +ImDisk Virtual Disk Driver for Windows NT/2000/XP. +This driver emulates harddisk partitions, floppy drives and CD/DVD-ROM +drives from disk image files, in virtual memory or by redirecting I/O +requests somewhere else, possibly to another machine, through a +co-operating user-mode service, ImDskSvc. + +Copyright (C) 2005-2015 Olof Lagerkvist. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +This source file contains some GNU GPL licensed code: +- Parts related to floppy emulation based on VFD by Ken Kato. +http://chitchat.at.infoseek.co.jp/vmware/vfd.html +Copyright (C) Free Software Foundation, Inc. +Read gpl.txt for the full GNU GPL license. + +This source file may contain BSD licensed code: +- Some code ported to NT from the FreeBSD md driver by Olof Lagerkvist. +http://www.ltr-data.se +Copyright (C) The FreeBSD Project. +Copyright (C) The Regents of the University of California. +*/ + +#include "imdsksys.h" + +NTSTATUS +ImDiskCallProxy(IN PPROXY_CONNECTION Proxy, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN PVOID RequestHeader, + IN ULONG RequestHeaderSize, + IN PVOID RequestData, + IN ULONG RequestDataSize, + IN OUT PVOID ResponseHeader, + IN ULONG ResponseHeaderSize, + IN OUT PVOID ResponseData, + IN ULONG ResponseDataBufferSize, + IN ULONG *ResponseDataSize) +{ + NTSTATUS status; + + ASSERT(Proxy != NULL); + + switch (Proxy->connection_type) + { + case PROXY_CONNECTION::PROXY_CONNECTION_DEVICE: + { + PUCHAR io_buffer = NULL; + PUCHAR temp_buffer = NULL; + ULONG io_size = RequestHeaderSize + RequestDataSize; + + if ((RequestHeaderSize > 0) && + (RequestDataSize > 0)) + { + temp_buffer = (PUCHAR) + ExAllocatePoolWithTag(NonPagedPool, io_size, POOL_TAG); + + if (temp_buffer == NULL) + { + KdPrint(("ImDisk Proxy Client: Memory allocation failed.\n.")); + + IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + if (RequestHeaderSize > 0) + { + RtlCopyMemory(temp_buffer, RequestHeader, RequestHeaderSize); + } + + if (RequestDataSize > 0) + { + RtlCopyMemory(temp_buffer + RequestHeaderSize, RequestData, RequestDataSize); + } + + io_buffer = temp_buffer; + } + else if (RequestHeaderSize > 0) + { + io_buffer = (PUCHAR)RequestHeader; + } + else if (RequestDataSize > 0) + { + io_buffer = (PUCHAR)RequestData; + } + + if (io_size > 0) + { + if (CancelEvent != NULL ? + KeReadStateEvent(CancelEvent) != 0 : + FALSE) + { + KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); + + if (temp_buffer != NULL) + { + ExFreePoolWithTag(temp_buffer, POOL_TAG); + } + + IoStatusBlock->Status = STATUS_CANCELLED; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + status = ImDiskSafeIOStream(Proxy->device, + IRP_MJ_WRITE, + IoStatusBlock, + CancelEvent, + io_buffer, + io_size); + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk Proxy Client: Request error %#x\n.", + status)); + + if (temp_buffer != NULL) + { + ExFreePoolWithTag(temp_buffer, POOL_TAG); + } + + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + } + + if (temp_buffer != NULL) + { + ExFreePoolWithTag(temp_buffer, POOL_TAG); + } + + if (ResponseHeaderSize > 0) + { + if (CancelEvent != NULL ? + KeReadStateEvent(CancelEvent) != 0 : + FALSE) + { + KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); + + IoStatusBlock->Status = STATUS_CANCELLED; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + status = ImDiskSafeIOStream(Proxy->device, + IRP_MJ_READ, + IoStatusBlock, + CancelEvent, + ResponseHeader, + ResponseHeaderSize); + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk Proxy Client: Response header error %#x\n.", + status)); + + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + } + + if (ResponseDataSize != NULL && *ResponseDataSize > 0) + { + if (*ResponseDataSize > ResponseDataBufferSize) + { + KdPrint(("ImDisk Proxy Client: Fatal: Request %u bytes, " + "receiving %u bytes.\n", + ResponseDataBufferSize, *ResponseDataSize)); + + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + if (CancelEvent != NULL ? + KeReadStateEvent(CancelEvent) != 0 : + FALSE) + { + KdPrint(("ImDisk Proxy Client: Request cancelled.\n.")); + + IoStatusBlock->Status = STATUS_CANCELLED; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint2 + (("ImDisk Proxy Client: Got ok resp. Waiting for data.\n")); + + status = ImDiskSafeIOStream(Proxy->device, + IRP_MJ_READ, + IoStatusBlock, + CancelEvent, + ResponseData, + *ResponseDataSize); + + if (!NT_SUCCESS(status)) + { + KdPrint(("ImDisk Proxy Client: Response data error %#x\n.", + status)); + + KdPrint(("ImDisk Proxy Client: Response data %u bytes, " + "got %u bytes.\n", + *ResponseDataSize, + (ULONG)IoStatusBlock->Information)); + + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint2 + (("ImDisk Proxy Client: Received %u byte data stream.\n", + IoStatusBlock->Information)); + } + + IoStatusBlock->Status = STATUS_SUCCESS; + + IoStatusBlock->Information = RequestDataSize; + + if (ResponseDataSize != NULL) + { + IoStatusBlock->Information += *ResponseDataSize; + } + + return IoStatusBlock->Status; + } + + case PROXY_CONNECTION::PROXY_CONNECTION_SHM: + { + PKEVENT wait_objects[] = { + Proxy->response_event, + CancelEvent + }; + + ULONG number_of_wait_objects = CancelEvent != NULL ? 2 : 1; + + // Some parameter sanity checks + if ((RequestHeaderSize > IMDPROXY_HEADER_SIZE) | + (ResponseHeaderSize > IMDPROXY_HEADER_SIZE) | + ((RequestDataSize + IMDPROXY_HEADER_SIZE) > + Proxy->shared_memory_size)) + { + KdPrint(("ImDisk Proxy Client: " + "Parameter values not supported.\n.")); + + IoStatusBlock->Status = STATUS_INVALID_BUFFER_SIZE; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + IoStatusBlock->Information = 0; + + if (RequestHeaderSize > 0) + RtlCopyMemory(Proxy->shared_memory, + RequestHeader, + RequestHeaderSize); + + if (RequestDataSize > 0) + RtlCopyMemory(Proxy->shared_memory + IMDPROXY_HEADER_SIZE, + RequestData, + RequestDataSize); + +#pragma warning(suppress: 28160) + KeSetEvent(Proxy->request_event, (KPRIORITY)0, TRUE); + + status = KeWaitForMultipleObjects(number_of_wait_objects, + (PVOID*)wait_objects, + WaitAny, + Executive, + KernelMode, + FALSE, + NULL, + NULL); + + if (status == STATUS_WAIT_1) + { + KdPrint(("ImDisk Proxy Client: Incomplete wait %#x.\n.", status)); + + IoStatusBlock->Status = STATUS_CANCELLED; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + if (ResponseHeaderSize > 0) + RtlCopyMemory(ResponseHeader, + Proxy->shared_memory, + ResponseHeaderSize); + + // If server end requests to send more data than we requested, we + // treat that as an unrecoverable device error and exit. + if (ResponseDataSize != NULL ? *ResponseDataSize > 0 : FALSE) + if ((*ResponseDataSize > ResponseDataBufferSize) | + ((*ResponseDataSize + IMDPROXY_HEADER_SIZE) > + Proxy->shared_memory_size)) + { + KdPrint(("ImDisk Proxy Client: Invalid response size %u.\n.", + *ResponseDataSize)); + + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + else + { + RtlCopyMemory(ResponseData, + Proxy->shared_memory + IMDPROXY_HEADER_SIZE, + *ResponseDataSize); + + IoStatusBlock->Information = *ResponseDataSize; + } + + IoStatusBlock->Status = STATUS_SUCCESS; + if ((RequestDataSize > 0) & (IoStatusBlock->Information == 0)) + IoStatusBlock->Information = RequestDataSize; + return IoStatusBlock->Status; + } + + default: + return STATUS_DRIVER_INTERNAL_ERROR; + } +} + +VOID +ImDiskCloseProxy(IN PPROXY_CONNECTION Proxy) +{ + ASSERT(Proxy != NULL); + + switch (Proxy->connection_type) + { + case PROXY_CONNECTION::PROXY_CONNECTION_DEVICE: + if (Proxy->device != NULL) + ObDereferenceObject(Proxy->device); + + Proxy->device = NULL; + break; + + case PROXY_CONNECTION::PROXY_CONNECTION_SHM: + if ((Proxy->request_event != NULL) & + (Proxy->response_event != NULL) & + (Proxy->shared_memory != NULL)) + { + *(ULONGLONG*)Proxy->shared_memory = IMDPROXY_REQ_CLOSE; + KeSetEvent(Proxy->request_event, (KPRIORITY)0, FALSE); + } + + if (Proxy->request_event_handle != NULL) + { + ZwClose(Proxy->request_event_handle); + Proxy->request_event_handle = NULL; + } + + if (Proxy->response_event_handle != NULL) + { + ZwClose(Proxy->response_event_handle); + Proxy->response_event_handle = NULL; + } + + if (Proxy->request_event != NULL) + { + ObDereferenceObject(Proxy->request_event); + Proxy->request_event = NULL; + } + + if (Proxy->response_event != NULL) + { + ObDereferenceObject(Proxy->response_event); + Proxy->response_event = NULL; + } + + if (Proxy->shared_memory != NULL) + { + ZwUnmapViewOfSection(NtCurrentProcess(), Proxy->shared_memory); + Proxy->shared_memory = NULL; + } + + break; + } +} + +#pragma code_seg("PAGE") + +/// +/// Note that this function when successful replaces the Proxy->device pointer +/// to point to the connected device object instead of the proxy service pipe. +/// This means that the only reference to the proxy service pipe after calling +/// this function is the original handle to the pipe. +/// +NTSTATUS +ImDiskConnectProxy(IN OUT PPROXY_CONNECTION Proxy, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN ULONG Flags, + IN PWSTR ConnectionString, + IN USHORT ConnectionStringLength) +{ + IMDPROXY_CONNECT_REQ connect_req; + IMDPROXY_CONNECT_RESP connect_resp; + NTSTATUS status; + + PAGED_CODE(); + + ASSERT(Proxy != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(ConnectionString != NULL); + + if (IMDISK_PROXY_TYPE(Flags) == IMDISK_PROXY_TYPE_SHM) + { + OBJECT_ATTRIBUTES object_attributes; + UNICODE_STRING base_name = { 0 }; + UNICODE_STRING event_name = { 0 }; + base_name.Buffer = ConnectionString; + base_name.Length = ConnectionStringLength; + base_name.MaximumLength = ConnectionStringLength; + event_name.MaximumLength = ConnectionStringLength + 20; + event_name.Buffer = + (PWCHAR)ExAllocatePoolWithTag(PagedPool, + event_name.MaximumLength, + POOL_TAG); + if (event_name.Buffer == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + InitializeObjectAttributes(&object_attributes, + &event_name, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + RtlCopyUnicodeString(&event_name, &base_name); + RtlAppendUnicodeToString(&event_name, L"_Request"); + + status = ZwOpenEvent(&Proxy->request_event_handle, + EVENT_ALL_ACCESS, + &object_attributes); + + if (!NT_SUCCESS(status)) + { + Proxy->request_event_handle = NULL; + ExFreePoolWithTag(event_name.Buffer, POOL_TAG); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + status = ObReferenceObjectByHandle(Proxy->request_event_handle, + EVENT_ALL_ACCESS, + *ExEventObjectType, + KernelMode, + (PVOID*)&Proxy->request_event, + NULL); + + if (!NT_SUCCESS(status)) + { + Proxy->request_event = NULL; + ExFreePoolWithTag(event_name.Buffer, POOL_TAG); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + RtlCopyUnicodeString(&event_name, &base_name); + RtlAppendUnicodeToString(&event_name, L"_Response"); + + status = ZwOpenEvent(&Proxy->response_event_handle, + EVENT_ALL_ACCESS, + &object_attributes); + + if (!NT_SUCCESS(status)) + { + Proxy->response_event_handle = NULL; + ExFreePoolWithTag(event_name.Buffer, POOL_TAG); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + status = ObReferenceObjectByHandle(Proxy->response_event_handle, + EVENT_ALL_ACCESS, + *ExEventObjectType, + KernelMode, + (PVOID*)&Proxy->response_event, + NULL); + + if (!NT_SUCCESS(status)) + { + Proxy->response_event = NULL; + ExFreePoolWithTag(event_name.Buffer, POOL_TAG); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + connect_req.request_code = IMDPROXY_REQ_CONNECT; + connect_req.flags = Flags; + connect_req.length = ConnectionStringLength; + + KdPrint(("ImDisk Proxy Client: Sending IMDPROXY_CONNECT_REQ.\n")); + + status = ImDiskCallProxy(Proxy, + IoStatusBlock, + CancelEvent, + &connect_req, + sizeof(connect_req), + ConnectionString, + ConnectionStringLength, + &connect_resp, + sizeof(IMDPROXY_CONNECT_RESP), + NULL, + 0, + NULL); + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + if (connect_resp.error_code != 0) + { + IoStatusBlock->Status = STATUS_CONNECTION_REFUSED; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + // If the proxy gave us a reference to an object to use for direct connection + // to the server we have to change the active reference to use here. + if (connect_resp.object_ptr != 0) + { + // First check that connect_resp.object_ptr is really something we have + // referenced earlier. + + KEVENT event; + IO_STATUS_BLOCK io_status; + PIRP irp; + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest( + IOCTL_IMDISK_GET_REFERENCED_HANDLE, + ImDiskCtlDevice, + &connect_resp.object_ptr, + sizeof(PFILE_OBJECT), + NULL, + 0, + TRUE, + &event, + &io_status); + + if (irp == NULL) + { + IoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + status = IoCallDriver(ImDiskCtlDevice, irp); + + if (status == STATUS_PENDING) + { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + } + + if (!NT_SUCCESS(status)) + { + DbgPrint("ImDisk: Failed claiming referenced object %p: %#x\n", + (PVOID)(ULONG_PTR)connect_resp.object_ptr, status); + + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + ObDereferenceObject(Proxy->device); + Proxy->device = (PFILE_OBJECT)(ULONG_PTR)connect_resp.object_ptr; + } + + KdPrint(("ImDisk Proxy Client: Got ok response IMDPROXY_CONNECT_RESP.\n")); + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; +} + +NTSTATUS +ImDiskQueryInformationProxy(IN PPROXY_CONNECTION Proxy, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent, + OUT PIMDPROXY_INFO_RESP ProxyInfoResponse, + IN ULONG ProxyInfoResponseLength) +{ + ULONGLONG proxy_req = IMDPROXY_REQ_INFO; + NTSTATUS status; + + PAGED_CODE(); + + ASSERT(Proxy != NULL); + ASSERT(IoStatusBlock != NULL); + + if ((ProxyInfoResponse == NULL) | + (ProxyInfoResponseLength < sizeof(IMDPROXY_INFO_RESP))) + { + IoStatusBlock->Status = STATUS_BUFFER_OVERFLOW; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint(("ImDisk Proxy Client: Sending IMDPROXY_REQ_INFO.\n")); + + status = ImDiskCallProxy(Proxy, + IoStatusBlock, + CancelEvent, + &proxy_req, + sizeof(proxy_req), + NULL, + 0, + ProxyInfoResponse, + sizeof(IMDPROXY_INFO_RESP), + NULL, + 0, + NULL); + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint(("ImDisk Proxy Client: Got ok response IMDPROXY_INFO_RESP.\n")); + + if (ProxyInfoResponse->req_alignment - 1 > FILE_512_BYTE_ALIGNMENT) + { +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk IMDPROXY_INFO_RESP: Unsupported sizes. " + "Got 0x%.8x%.8x size and 0x%.8x%.8x alignment.\n", + ProxyInfoResponse->file_size, + ProxyInfoResponse->req_alignment)); + + IoStatusBlock->Status = STATUS_INVALID_PARAMETER; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; +} + +#pragma code_seg() + +NTSTATUS +ImDiskReadProxy(IN PPROXY_CONNECTION Proxy, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent, + OUT PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset) +{ + IMDPROXY_READ_REQ read_req; + IMDPROXY_READ_RESP read_resp; + NTSTATUS status; + ULONG_PTR max_transfer_size; + ULONG length_done; + + ASSERT(Proxy != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(Buffer != NULL); + ASSERT(ByteOffset != NULL); + + if (Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) + max_transfer_size = Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE; + else + max_transfer_size = Length; + + length_done = 0; + status = STATUS_SUCCESS; + + while (length_done < Length) + { + ULONG length_to_do = Length - length_done; + + KdPrint2(("ImDisk Proxy Client: " + "IMDPROXY_REQ_READ 0x%.8x done 0x%.8x left to do.\n", + length_done, length_to_do)); + + read_req.request_code = IMDPROXY_REQ_READ; + read_req.offset = ByteOffset->QuadPart + length_done; + read_req.length = + length_to_do <= max_transfer_size ? + length_to_do : max_transfer_size; + + KdPrint2(("ImDisk Proxy Client: " + "IMDPROXY_REQ_READ 0x%.8x%.8x bytes at 0x%.8x%.8x.\n", + ((PLARGE_INTEGER)&read_req.length)->HighPart, + ((PLARGE_INTEGER)&read_req.length)->LowPart, + ((PLARGE_INTEGER)&read_req.offset)->HighPart, + ((PLARGE_INTEGER)&read_req.offset)->LowPart)); + + status = ImDiskCallProxy(Proxy, + IoStatusBlock, + CancelEvent, + &read_req, + sizeof(read_req), + NULL, + 0, + &read_resp, + sizeof(read_resp), + (PUCHAR)Buffer + length_done, + (ULONG)read_req.length, + (PULONG)&read_resp.length); + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + length_done += (ULONG)read_resp.length; + + if (read_resp.errorno != 0) + { +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", + read_resp.errorno)); + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + KdPrint2(("ImDisk Proxy Client: Server sent 0x%.8x%.8x bytes.\n", + ((PLARGE_INTEGER)&read_resp.length)->HighPart, + ((PLARGE_INTEGER)&read_resp.length)->LowPart)); + + if (read_resp.length == 0) + break; + } + + IoStatusBlock->Status = status; + IoStatusBlock->Information = length_done; + + return status; +} + +NTSTATUS +ImDiskWriteProxy(IN PPROXY_CONNECTION Proxy, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent, + IN PVOID Buffer, + IN ULONG Length, + IN PLARGE_INTEGER ByteOffset) +{ + IMDPROXY_WRITE_REQ write_req; + IMDPROXY_WRITE_RESP write_resp; + NTSTATUS status; + ULONG_PTR max_transfer_size; + ULONG length_done; + + ASSERT(Proxy != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(Buffer != NULL); + ASSERT(ByteOffset != NULL); + + if (Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) + max_transfer_size = Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE; + else + max_transfer_size = Length; + + length_done = 0; + status = STATUS_SUCCESS; + + while (length_done < Length) + { + ULONG length_to_do = Length - length_done; + + KdPrint2(("ImDisk Proxy Client: " + "IMDPROXY_REQ_WRITE 0x%.8x done 0x%.8x left to do.\n", + length_done, length_to_do)); + + write_req.request_code = IMDPROXY_REQ_WRITE; + write_req.offset = ByteOffset->QuadPart + length_done; + write_req.length = + length_to_do <= max_transfer_size ? + length_to_do : max_transfer_size; + + KdPrint2(("ImDisk Proxy Client: " + "IMDPROXY_REQ_WRITE 0x%.8x%.8x bytes at 0x%.8x%.8x.\n", + ((PLARGE_INTEGER)&write_req.length)->HighPart, + ((PLARGE_INTEGER)&write_req.length)->LowPart, + ((PLARGE_INTEGER)&write_req.offset)->HighPart, + ((PLARGE_INTEGER)&write_req.offset)->LowPart)); + + status = ImDiskCallProxy(Proxy, + IoStatusBlock, + CancelEvent, + &write_req, + sizeof(write_req), + (PUCHAR)Buffer + length_done, + (ULONG)write_req.length, + &write_resp, + sizeof(write_resp), + NULL, + 0, + NULL); + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + if (write_resp.errorno != 0) + { +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", + write_resp.errorno)); + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + if (write_resp.length != write_req.length) + { + KdPrint(("ImDisk Proxy Client: IMDPROXY_REQ_WRITE %u bytes, " + "IMDPROXY_RESP_WRITE %u bytes.\n", + Length, + (ULONG)write_resp.length)); + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; + } + + KdPrint2(("ImDisk Proxy Client: Server replied OK.\n")); + + length_done += (ULONG)write_req.length; + } + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = length_done; + return IoStatusBlock->Status; +} + +NTSTATUS +ImDiskUnmapOrZeroProxy(IN PPROXY_CONNECTION Proxy, + IN ULONGLONG RequestCode, + IN OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PKEVENT CancelEvent OPTIONAL, + IN ULONG Items, + IN PDEVICE_DATA_SET_RANGE Ranges) +{ + IMDPROXY_UNMAP_REQ unmap_req; + IMDPROXY_UNMAP_RESP unmap_resp; + NTSTATUS status; + ULONG byte_size = (ULONG)(Items * sizeof(DEVICE_DATA_SET_RANGE)); + + ASSERT(Proxy != NULL); + ASSERT(IoStatusBlock != NULL); + ASSERT(Ranges != NULL); + + if ((Proxy->connection_type == PROXY_CONNECTION::PROXY_CONNECTION_SHM) && + (byte_size >= (Proxy->shared_memory_size - IMDPROXY_HEADER_SIZE))) + { + status = STATUS_BUFFER_OVERFLOW; + IoStatusBlock->Information = 0; + IoStatusBlock->Status = status; + return status; + } + + status = STATUS_SUCCESS; + + unmap_req.request_code = RequestCode; + unmap_req.length = byte_size; + +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk Proxy Client: Unmap/Zero 0x%.8x%.8x\n", RequestCode)); + + status = ImDiskCallProxy(Proxy, + IoStatusBlock, + CancelEvent, + &unmap_req, + sizeof(unmap_req), + (PUCHAR)Ranges, + (ULONG)unmap_req.length, + &unmap_resp, + sizeof(unmap_resp), + NULL, + 0, + NULL); + + if (!NT_SUCCESS(status)) + { + IoStatusBlock->Status = status; + IoStatusBlock->Information = 0; + return status; + } + + if (unmap_resp.errorno != 0) + { +#pragma warning(suppress: 6064) +#pragma warning(suppress: 6328) + KdPrint(("ImDisk Proxy Client: Server returned error 0x%.8x%.8x.\n", + unmap_resp.errorno)); + IoStatusBlock->Status = STATUS_IO_DEVICE_ERROR; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; + } + + KdPrint(("ImDisk Proxy Client: Server replied OK.\n")); + + IoStatusBlock->Status = STATUS_SUCCESS; + IoStatusBlock->Information = 0; + return IoStatusBlock->Status; +} + diff --git a/sys/sources b/sys/sources index 62965e3..34cabe5 100644 --- a/sys/sources +++ b/sys/sources @@ -1,7 +1,7 @@ TARGETNAME=imdisk TARGETPATH=. TARGETTYPE=DRIVER -SOURCES=imdisk.cpp imdisk.rc + MSC_WARNING_LEVEL=/W4 /WX /wd4201 /wd4204 /wd4221 !IF "$(NTDEBUG)" != "ntsd" MSC_OPTIMIZATION=/Ox /GF @@ -12,3 +12,14 @@ C_DEFINES=$(C_DEFINES) /DINCLUDE_VFD_ORIGIN LINKER_FLAGS=llmath.lib BUFFER_OVERFLOW_CHECKS=0 !ENDIF + +SOURCES=imdisk.cpp \ + commonio.cpp \ + createdev.cpp \ + devthrd.cpp \ + floppy.cpp \ + iodisp.cpp \ + lowerdev.cpp \ + proxy.cpp \ + imdisk.rc + diff --git a/sys/sys.vcxproj b/sys/sys.vcxproj index 249826a..75bb1ec 100644 --- a/sys/sys.vcxproj +++ b/sys/sys.vcxproj @@ -191,7 +191,14 @@ + + + + + + + @@ -223,6 +230,8 @@ + + diff --git a/wdk7_secondary.props b/wdk7_secondary.props new file mode 100644 index 0000000..3cb62d5 --- /dev/null +++ b/wdk7_secondary.props @@ -0,0 +1,21 @@ + + + + + + <_PropertySheetDisplayName>wdk7_secondary + C:\WINDDK\7600.16385.1\inc\api\crt\stl60;C:\WINDDK\7600.16385.1\inc\api;C:\WINDDK\7600.16385.1\inc\ddk;C:\WINDDK\7600.16385.1\inc\crt;$(IncludePath) + $(LibraryPath);C:\WINDDK\7600.16385.1\lib\Win7\$(PlatformTarget) + + + + vsnprintf=_vsnprintf;snwprintf=_snwprintf;open=_open;read=_read;close=_close;write=_write;_CRT_NON_CONFORMING_SWPRINTFS;%(PreprocessorDefinitions) + false + + + + 5.02 + + + + \ No newline at end of file