www.pudn.com > vdksrc.zip > vdkioctl.c


/*
	vdkdrv.c

	Virtual Disk kernel-mode driver for Windows NT platform
	Device IO control routine
	Copyright (C) 2003 Ken Kato
*/

#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkver.h"
#include "vdkioctl.h"
#include "vdkfile.h"
#include "vdkaccess.h"

#include "imports.h"
#include "vdkdrv.h"

#if (DBG && VER_PRODUCTBUILD < 2195)
#define FILE_AUTOGENERATED_DEVICE_NAME	0x00000080
#define FILE_DEVICE_SECURE_OPEN 		0x00000100
#define VPB_REMOVE_PENDING				0x00000008
#define VPB_RAW_MOUNT					0x00000010
#endif

#define IO_INPUTLEN(p)	(p)->Parameters.DeviceIoControl.InputBufferLength
#define IO_OUTPUTLEN(p)	(p)->Parameters.DeviceIoControl.OutputBufferLength

//
// Handle various IOCTL commands
//
NTSTATUS
VdkDeviceControl (
	IN PDEVICE_OBJECT	DeviceObject,
	IN PIRP 			Irp)
{
	PPART_EXTENSION		part_extension;
	PDISK_EXTENSION		disk_extension;
	PIO_STACK_LOCATION	io_stack;
	NTSTATUS			status;

	//
	// Set up necessary object and extension pointers.
	//
	part_extension =
		(PPART_EXTENSION)DeviceObject->DeviceExtension;

	disk_extension =
		(PDISK_EXTENSION)part_extension->FirstPartition;

	io_stack = IoGetCurrentIrpStackLocation(Irp);

	VDKTRACE(VDKIOCTL | VDKINFO,
		("[VDK] %ws %s\n",
		part_extension->DeviceName.Buffer,
		IoControlCodeToStr(io_stack->Parameters.DeviceIoControl.IoControlCode)));

	Irp->IoStatus.Information = 0;

	switch (io_stack->Parameters.DeviceIoControl.IoControlCode) {
	//
	// Operation:	Returns VDK driver version
	// Target:		any device
	// Input:		none
	// Output:		ULONG driver version
	// Status:		STATUS_SUCCESS
	//
	case IOCTL_VDK_GET_VERSION:
		{
			if (IO_OUTPUTLEN(io_stack) >= sizeof(ULONG)) {
				*(PULONG)Irp->AssociatedIrp.SystemBuffer =
#if DBG
					VDK_DRIVER_VERSION_VAL | 0x00008000;
#else	// DBG
					VDK_DRIVER_VERSION_VAL;
#endif	// DBG
				Irp->IoStatus.Information = sizeof(ULONG);
			}
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Open image file(s).
	// Target:		disk device only
	// Input:		VDK_OPEN_FILE_INFO open file information
	// Output:		none
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				STATUS_DEVICE_BUSY				image is already opened
	//				STATUS_INVALID_PARAMETER		invalid input parameter
	//				STATUS_INSUFFICIENT_RESOURSES	resource allocation error
	//				...or other status returned by system function call
	//
	case IOCTL_VDK_OPEN_FILE:
		{
#ifdef VDK_SUPPORT_NETWORK
			SECURITY_QUALITY_OF_SERVICE sqos;
#endif	// VDK_SUPPORT_NETWORK

			//
			//	Check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// Check device status
			//
			if (disk_extension->DiskInfo.DiskType) {
				status = STATUS_DEVICE_BUSY;
				break;
			}

			if (disk_extension->PartitionOrdinal == VDK_ZOMBIE) {
				//
				//	The device can be a zombie
				//
				if (DeviceObject->Vpb && DeviceObject->Vpb->ReferenceCount > 1) {
					VDKTRACE(VDKIOCTL|VDKWARN,
						("[VDK] %ws is a zombie\n",
						disk_extension->DeviceName.Buffer));

					status = STATUS_DEVICE_BUSY;
					break;
				}

				//	This device is no longer a zombie
				VDKTRACE(VDKIOCTL|VDKWARN,
					("[VDK] %ws is no longer a zombie\n",
					disk_extension->DeviceName.Buffer));

				disk_extension->PartitionOrdinal = 0;
			}

			//
			// Check input parameter
			//
			status = VdkOpenCheckParam(
				(PVDK_OPEN_FILE_INFO)Irp->AssociatedIrp.SystemBuffer,
				IO_INPUTLEN(io_stack));

			if (!NT_SUCCESS(status)) {
				break;
			}

#ifdef VDK_SUPPORT_NETWORK
			//
			// create security context that matches the calling process' context
			//
			if (disk_extension->SecurityContext) {
				SeDeleteClientSecurity(disk_extension->SecurityContext);
			}
			else {
				disk_extension->SecurityContext = ExAllocatePool(
					NonPagedPool, sizeof(SECURITY_CLIENT_CONTEXT));

				if (!disk_extension->SecurityContext) {

					VDKTRACE(VDKIOCTL,
						("[VDK] IOCTL_VDK_OPEN_FILE: ExAllocatePool\n"));

					status = STATUS_INSUFFICIENT_RESOURCES;
					break;
				}
			}

			RtlZeroMemory(&sqos, sizeof(sqos));

			sqos.Length = sizeof(sqos);
			sqos.ImpersonationLevel = SecurityImpersonation;
			sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
			sqos.EffectiveOnly = FALSE;

			SeCreateClientSecurity(
				PsGetCurrentThread(),
				&sqos,
				FALSE,
				disk_extension->SecurityContext);
#endif	// VDK_SUPPORT_NETWORK

			//
			// The device thread will do the rest of the job
			//
			status = STATUS_PENDING;
			break;
		}

	//
	// Operation:	configure devices to match the virtual disk's
	//				partition layout
	// Target: 		disk device only
	// Input:		none
	// Output:		ULONG size of partition layout information which would be
	//				returned by IOCTL_DISK_GET_DRIVE_LAYOUT
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				...or other status returned by system function call
	//
	case IOCTL_VDK_UPDATE_DEVICE:
		{
			PDRIVE_LAYOUT_INFORMATION drive_layout;
			ULONG idx;

			//
			//	Check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			status = STATUS_SUCCESS;

			if (disk_extension->PartitionOrdinal == VDK_ZOMBIE) {
				if (!DeviceObject->Vpb || DeviceObject->Vpb->ReferenceCount == 1) {
					//	This device is no longer a zombie
					VDKTRACE(VDKIOCTL|VDKWARN,
						("[VDK] %ws is no longer a zombie\n",
						disk_extension->DeviceName.Buffer));

					disk_extension->PartitionOrdinal = 0;
				}
				break;
			}

			if (!disk_extension->DiskInfo.DiskType) {
				break;
			}

			//
			//	Read partition information from virtual disk
			//
			status = IoReadPartitionTable(
				disk_extension->DeviceObject,
				VDK_BYTES_PER_SECTOR,
				FALSE,
				&drive_layout);

			if (!NT_SUCCESS(status)) {

				VDKTRACE(VDKIOCTL,
					("[VDK] IOCTL_VDK_UPDATE_DEVICE: IoReadPartitionTable %s\n",
					VdkStatusStr(status)));

				break;
			}

			if (!drive_layout) {
				status = STATUS_DRIVER_INTERNAL_ERROR;

				VDKTRACE(VDKIOCTL,
					("[VDK] IOCTL_VDK_UPDATE_DEVICE: IoReadPartitionTable %s\n",
					VdkStatusStr(status)));

				break;
			}

			//
			// Create / Delete / Update device objects according to partition table
			//
			for (idx = 0; idx < drive_layout->PartitionCount; idx++) {
				drive_layout->PartitionEntry[idx].RewritePartition = TRUE;
			}

			VdkUpdateDevice(disk_extension->DeviceObject, drive_layout);

			ExFreePool(drive_layout);

			break;
		}

	//
	// Operation:	Close image file(s)
	// Target:		disk device only
	// Input:		!= 0	the volume has been dismounted
	//				0		the volume has not been dismounted
	// Output:		none
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				STATUS_DEVICE_NOT_READY			image is not opened
	//				...or other status returned by system function call
	//
	case IOCTL_VDK_CLOSE_FILE:
		{
			//
			// check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			//	Check device status
			//
			if (!disk_extension->DiskInfo.DiskType) {
				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// Change DiskType so that no one will use the file hereafter
			//
			disk_extension->DiskInfo.DiskType = VDK_DISKTYPE_NONE;

			//
			// The device thread will do the rest of the job
			//
			status = STATUS_PENDING;
			break;
		}

	//
	// Operation:	Returns total size of image file information
	// Target: 		disk device only
	// Input:		none
	// Output:		ULONG required buffer size for IOCTL_VDK_QUERY_FILE
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				STATUS_BUFFER_TOO_SMALL			output buffer too small
	//
	case IOCTL_VDK_QUERY_FILE_SIZE:
		{
			//
			//	Check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// Check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// Store required buffer size
			//
			if (disk_extension->DiskInfo.DiskType) {

				*(PULONG)Irp->AssociatedIrp.SystemBuffer =
					FIELD_OFFSET(VDK_OPEN_FILE_INFO, Files) +
					(sizeof(VDK_OPEN_FILE_ITEM) * disk_extension->DiskInfo.FilesTotal) +
					disk_extension->DiskInfo.BufferLen;
			}
			else {
				*(PULONG)Irp->AssociatedIrp.SystemBuffer = 0;
			}

			Irp->IoStatus.Information = sizeof(ULONG);
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Returns current image file information
	// Target: 		disk device only
	// Input:		none
	// Output:		VDK_OPEN_FILE_INFO	image file information
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				STATUS_BUFFER_TOO_SMALL			output buffer too small
	//
	case IOCTL_VDK_QUERY_FILE:
		{
			PVDK_OPEN_FILE_INFO open_file;
			ULONG	fixed_size;
			ULONG	idx;

			//
			//	Check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// image is opened ?
			//
			if (!disk_extension->DiskInfo.DiskType) {
				// nothing to return
				status = STATUS_SUCCESS;
				break;
			}

			//
			// calculate necessaty buffer length
			//
			fixed_size =
				FIELD_OFFSET(VDK_OPEN_FILE_INFO, Files) +
				(sizeof(VDK_OPEN_FILE_ITEM) * disk_extension->DiskInfo.FilesTotal);

			//
			// Check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) <
				fixed_size + disk_extension->DiskInfo.BufferLen) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// Store disk information
			//
			open_file = (PVDK_OPEN_FILE_INFO)Irp->AssociatedIrp.SystemBuffer;

			open_file->DiskType		= disk_extension->DiskInfo.DiskType;
			open_file->Capacity		= disk_extension->DiskInfo.Capacity;
			open_file->Cylinders	= disk_extension->DiskInfo.Cylinders;
			open_file->Tracks		= disk_extension->DiskInfo.Tracks;
			open_file->Sectors		= disk_extension->DiskInfo.Sectors;
			open_file->FilesTotal	= disk_extension->DiskInfo.FilesTotal;

			//
			// Store each file information
			//
			for (idx = 0; idx < disk_extension->DiskInfo.FilesTotal; idx++) {
				PVDK_FILE_INFO	file_item;
				PVDK_OPEN_FILE_ITEM	open_item;

				file_item = &(disk_extension->DiskInfo.Files[idx]);
				open_item = &(open_file->Files[idx]);

				open_item->FileType		= file_item->FileType;
				open_item->Capacity		= file_item->Capacity;
				open_item->NameLength	= file_item->NameLength;
			}

			//
			// copy filenames following the fixed info
			//
			if (disk_extension->DiskInfo.BufferLen) {
				RtlCopyMemory(
					(PCHAR)open_file + fixed_size,
					disk_extension->DiskInfo.NameBuffer,
					disk_extension->DiskInfo.BufferLen);
			}

			Irp->IoStatus.Information =
				fixed_size + disk_extension->DiskInfo.BufferLen;

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Returns number of partition devices attached to
	//				the current disk device
	// Target: 		disk device only
	// Input:		none
	// Output:		ULONG	number of partition devices
	// Status:		STATUS_INVALID_DEVICE_REQUEST	not a disk device
	//				STATUS_BUFFER_TOO_SMALL			output buffer too small
	//
	case IOCTL_VDK_NUMBER_OF_PARTS:
		{
			PDEVICE_OBJECT device_obj;
			ULONG num_parts;

			//
			//	Check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// Check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			//	Count partition devices
			//
			num_parts = 0;
			device_obj = disk_extension->NextPartition;

			while (device_obj) {
				num_parts++;
				device_obj = ((PPART_EXTENSION)device_obj->DeviceExtension)->NextPartition;
			}

			*(PULONG)Irp->AssociatedIrp.SystemBuffer = num_parts;
			Irp->IoStatus.Information = sizeof(ULONG);

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Create new virtual disk device
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_VDK_CREATE_DISK:
		//
		//	Actual operation is performed in the device thread, for
		//	directory object must be created in the system thread context
		//
		status = STATUS_PENDING;
		break;

	//
	// Operation:	Delete the last created virtual disk device
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_VDK_DELETE_DISK:
		status = STATUS_PENDING;
		break;

	//
	// Operation:	Notifies the device that it has been dismounted
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_VDK_NOTIFY_DISMOUNT:
		if (part_extension->PartitionOrdinal == VDK_ZOMBIE) {

			VDKTRACE(VDKIOCTL | VDKWARN,
				("[VDK] %ws is no longer a zombie.\n",
				part_extension->DeviceName.Buffer));

			part_extension->PartitionOrdinal = 0;
		}
		status = STATUS_SUCCESS;
		break;

	//
	// Operation:	Returnes driver information
	// Target:		any device
	// Input:		none
	// Output:		VDK_DRIVER_INFO driver information
	// Status:		STATUS_BUFFER_TOO_SMALL		output buffer too small
	//
	case IOCTL_VDK_DRIVER_INFO:
		{
			PDEVICE_OBJECT device_obj;
			PVDK_DRIVER_INFO driver_info;

			//
			// Check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(VDK_DRIVER_INFO)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			//	Initialize output buffer
			//
			driver_info =
				(PVDK_DRIVER_INFO)Irp->AssociatedIrp.SystemBuffer;

			RtlZeroMemory(driver_info, sizeof(VDK_DRIVER_INFO));

			//
			//	Count each type of devices
			//
			device_obj = DeviceObject->DriverObject->DeviceObject;

			while (device_obj) {
				PPART_EXTENSION part_ext =
					(PPART_EXTENSION)device_obj->DeviceExtension;

				if (device_obj->Vpb && device_obj->Vpb->ReferenceCount) {
					driver_info->TotalReference
						+= device_obj->Vpb->ReferenceCount;
				}

				if (part_ext) {
					if (part_ext == (PPART_EXTENSION)part_ext->FirstPartition) {
						driver_info->DiskDevices++;
					}
					else if (part_ext->FirstPartition) {
						driver_info->AttachedParts++;
					}
					else {
						driver_info->OrphanedParts++;
					}

#if DBG
					if ((TraceFlags & (VDKIOCTL | VDKINFO)) == (VDKIOCTL | VDKINFO)) {
						if (part_ext->DeviceName.Buffer) {
							KdPrint(("[VDK] DeviceName: %ws\n",
								part_ext->DeviceName.Buffer));
						}
						if (part_ext->SymbolicLink.Buffer) {
							KdPrint(("[VDK] SymbolicLink: %ws\n",
								part_ext->SymbolicLink.Buffer));
						}

						KdPrint(("[VDK] Characteristics = 0x%08x\n",
							device_obj->Characteristics));

						if (device_obj->Characteristics & FILE_AUTOGENERATED_DEVICE_NAME) {
							KdPrint(("    FILE_AUTOGENERATED_DEVICE_NAME\n"));
						}
						if (device_obj->Characteristics & FILE_DEVICE_IS_MOUNTED) {
							KdPrint(("    FILE_DEVICE_IS_MOUNTED\n"));
						}
						if (device_obj->Characteristics & FILE_DEVICE_SECURE_OPEN) {
							KdPrint(("    FILE_DEVICE_SECURE_OPEN\n"));
						}
						if (device_obj->Characteristics & FILE_FLOPPY_DISKETTE) {
							KdPrint(("    FILE_FLOPPY_DISKETTE\n"));
						}
						if (device_obj->Characteristics & FILE_READ_ONLY_DEVICE) {
							KdPrint(("    FILE_READ_ONLY_DEVICE\n"));
						}
						if (device_obj->Characteristics & FILE_REMOTE_DEVICE) {
							KdPrint(("    FILE_REMOTE_DEVICE\n"));
						}
						if (device_obj->Characteristics & FILE_REMOVABLE_MEDIA) {
							KdPrint(("    FILE_REMOVABLE_MEDIA\n"));
						}
						if (device_obj->Characteristics & FILE_VIRTUAL_VOLUME) {
							KdPrint(("    FILE_VIRTUAL_VOLUME\n"));
						}
						if (device_obj->Characteristics & FILE_WRITE_ONCE_MEDIA) {
							KdPrint(("    FILE_WRITE_ONCE_MEDIA\n"));
						}

						if (device_obj->Vpb) {
							KdPrint(("    Vpb->Type = %d\n",
								device_obj->Vpb->Type));

							KdPrint(("    Vpb->SerialNumber = %lu\n",
								device_obj->Vpb->SerialNumber));

							KdPrint(("    Vpb->ReferenceCount = %lu\n",
								device_obj->Vpb->ReferenceCount));

							if (device_obj->Vpb->Flags & VPB_MOUNTED) {
								KdPrint(("    VPB_MOUNTED\n"));
							}
							if (device_obj->Vpb->Flags & VPB_LOCKED) {
								KdPrint(("    VPB_LOCKED\n"));
							}
							if (device_obj->Vpb->Flags & VPB_PERSISTENT) {
								KdPrint(("    VPB_PERSISTENT\n"));
							}
							if (device_obj->Vpb->Flags & VPB_REMOVE_PENDING) {
								KdPrint(("    VPB_REMOVE_PENDING\n"));
							}
							if (device_obj->Vpb->Flags & VPB_RAW_MOUNT) {
								KdPrint(("    VPB_RAW_MOUNT\n"));
							}
						}
					}
#endif	// DBG
				}

				device_obj = device_obj->NextDevice;
			}

			Irp->IoStatus.Information = sizeof(VDK_DRIVER_INFO);
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Returns device information
	// Target:		any device
	// Input:		TRUE	query all devices
	//				FALSE	query this device only
	// Output:		VDK_DEVICE_INFO device information
	// Status:		STATUS_BUFFER_TOO_SMALL	output buffer too small
	//				STATUS_BUFFER_OVERFLOW
	//
	case IOCTL_VDK_DEVICE_INFO:
		{
			PDEVICE_OBJECT device_obj;
			PVDK_DEVICE_INFO device_info;
			ANSI_STRING ansi_str;
			ULONG device_num;

			//
			// Check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(VDK_DEVICE_INFO)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// Check request type
			//
			if (IO_INPUTLEN(io_stack) >= sizeof(ULONG) &&
				*(PULONG)Irp->AssociatedIrp.SystemBuffer) {

				//
				// Information request for all devices
				//
				device_obj = DeviceObject->DriverObject->DeviceObject;
				device_num = 0;

				while (device_obj) {
					device_num++;
					device_obj = device_obj->NextDevice;
				}

				//
				// Recheck output buffer length
				//
				if (IO_OUTPUTLEN(io_stack) < sizeof(VDK_DEVICE_INFO) * device_num) {

					device_num = IO_OUTPUTLEN(io_stack) / sizeof(VDK_DEVICE_INFO);
					status = STATUS_BUFFER_OVERFLOW;
				}
				else {
					status = STATUS_SUCCESS;
				}

				device_obj = DeviceObject->DriverObject->DeviceObject;
			}
			else {

				//
				// Information request for this device only
				//
				device_obj = DeviceObject;
				device_num = 1;
				status = STATUS_SUCCESS;
			}

			//
			// Initialize output buffer
			//
			Irp->IoStatus.Information =
				sizeof(VDK_DEVICE_INFO) * device_num;

			RtlZeroMemory(
				Irp->AssociatedIrp.SystemBuffer,
				sizeof(VDK_DEVICE_INFO) * device_num);

			device_info =
				(PVDK_DEVICE_INFO)Irp->AssociatedIrp.SystemBuffer;

			while (device_obj && device_num) {
				PPART_EXTENSION part_ext =
					(PPART_EXTENSION)device_obj->DeviceExtension;

				//
				// Store devie type
				//
				if (part_ext == (PPART_EXTENSION)part_ext->FirstPartition) {
					device_info->DeviceType = VDK_DEVICE_DISK;
				}
				else {
					device_info->DeviceType = VDK_DEVICE_PART;
				}

				if (part_ext->PartitionOrdinal == VDK_ZOMBIE) {
					device_info->Zombie = VDK_ZOMBIE;
				}

				//
				// Store device name
				//
				if (part_ext->DeviceName.Buffer) {
					ansi_str.Buffer = device_info->DeviceName;
					ansi_str.Length = 0;
					ansi_str.MaximumLength = sizeof(device_info->DeviceName);

					RtlUnicodeStringToAnsiString(
						&ansi_str, &part_ext->DeviceName, FALSE);
				}

				//
				// Store symbolic link name
				//
				if (part_ext->SymbolicLink.Buffer) {
					ansi_str.Buffer = device_info->SymbolicLink;
					ansi_str.Length = 0;
					ansi_str.MaximumLength = sizeof(device_info->SymbolicLink);

					RtlUnicodeStringToAnsiString(
						&ansi_str, &part_ext->SymbolicLink, FALSE);
				}

				//
				// store device reference count
				//
				if (device_obj->Vpb) {
					device_info->ReferenceCount =
						device_obj->Vpb->ReferenceCount;
				}

				//
				// next device;
				//
				device_obj = device_obj->NextDevice;
				device_num--;
				device_info++;
			}

			break;
		}

#if DBG
	//
	// Operation:	Set debug trace flag
	// Target:		any device
	// Input:		ULONG trace flags (optional)
	// Output:		none
	// Status:		STATUS_SUCCESS
	//
	case IOCTL_VDK_DEBUG_TRACE:
		{
			if (IO_INPUTLEN(io_stack) >= sizeof(ULONG)) {
				TraceFlags = *(PULONG)Irp->AssociatedIrp.SystemBuffer;
			}

			KdPrint(("[VDK] Current Trace Flags = 0x%08x\n", TraceFlags));

			if (IO_OUTPUTLEN(io_stack) >= sizeof(ULONG)) {
				*(PULONG)Irp->AssociatedIrp.SystemBuffer = TraceFlags;
				Irp->IoStatus.Information = sizeof(ULONG);
			}

			status = STATUS_SUCCESS;
			break;
		}
#endif	// DBG

	//
	// Operation:	Return the drive geometry for the specified drive.
	// Target:		active device
	// Input:		none
	// Output:		DISK_GEOMETRY
	//
	// we will return the geometry for the physical drive, regardless
	// of which partition was specified for the request.
	//
	case IOCTL_DISK_GET_DRIVE_GEOMETRY:
		{
			PDISK_GEOMETRY geometry;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(DISK_GEOMETRY)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// copy DISK_GEOMETRY information
			//
			geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;

			geometry->MediaType			= FixedMedia;
			geometry->BytesPerSector	= VDK_BYTES_PER_SECTOR;
			geometry->SectorsPerTrack	= disk_extension->DiskInfo.Sectors;
			geometry->TracksPerCylinder	= disk_extension->DiskInfo.Tracks;
			geometry->Cylinders.QuadPart = disk_extension->DiskInfo.Cylinders;

			Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Return the information about the partition specified by the
	// 				device object.
	// Target: 		active device
	// Input:		none
	// Output:		PARTITION_INFORMATION
	//
	case IOCTL_DISK_GET_PARTITION_INFO:
		{
			PPARTITION_INFORMATION partition_info;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(PARTITION_INFORMATION)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// set up the PARTITION_INFORMATION to return
			//
			partition_info =
				(PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

			RtlCopyMemory(partition_info,
				&part_extension->PartitionInfo,
				sizeof(PARTITION_INFORMATION));

			partition_info->RecognizedPartition = TRUE;
			partition_info->RewritePartition	= FALSE;

			Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
			status = STATUS_SUCCESS;
			break;
		}


	//
	// Operation:	Return the information about the partition specified by the
	// 				device object. (WINXP specific)
	// Target: 		active device
	// Input:		none
	// Output:		PARTITION_INFORMATION_EX
	//
	case IOCTL_DISK_GET_PARTITION_INFO_EX:	// WINXP
		{
			PPARTITION_INFORMATION_EX partition_info;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(PARTITION_INFORMATION_EX)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// set up the PARTITION_INFORMATION_EX to return
			//
			partition_info =
				(PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;

			partition_info->PartitionStyle = PARTITION_STYLE_MBR;

			partition_info->StartingOffset.QuadPart =
				part_extension->PartitionInfo.StartingOffset.QuadPart;

			partition_info->PartitionLength.QuadPart =
				part_extension->PartitionInfo.PartitionLength.QuadPart;

			partition_info->PartitionNumber =
				part_extension->PartitionInfo.PartitionNumber;

			partition_info->RewritePartition	= FALSE;

			partition_info->Mbr.PartitionType =
				part_extension->PartitionInfo.PartitionType;

			partition_info->Mbr.BootIndicator =
				part_extension->PartitionInfo.BootIndicator;

			partition_info->Mbr.RecognizedPartition = TRUE;

			partition_info->Mbr.HiddenSectors =
				part_extension->PartitionInfo.HiddenSectors;

			Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	set the partition type.
	// Target: 		active partition device
	// Input:		SET_PARTITION_INFORMATION
	// Output:		none
	//
	case IOCTL_DISK_SET_PARTITION_INFO:
		{
			PSET_PARTITION_INFORMATION set_info;

			//
			// check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			//	check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			//	check device type
			//
			if (part_extension == (PPART_EXTENSION)disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			//	check input buffer length
			//
			if (IO_INPUTLEN(io_stack) < sizeof(SET_PARTITION_INFORMATION)) {

				status = STATUS_INVALID_PARAMETER;
				break;
			}

			//
			//	write partition information to virtual disk
			//
			set_info = (PSET_PARTITION_INFORMATION)
				Irp->AssociatedIrp.SystemBuffer;

			status = IoSetPartitionInformation(
				disk_extension->DeviceObject,
				VDK_BYTES_PER_SECTOR,
				part_extension->PartitionOrdinal,
				set_info->PartitionType);

			if (!NT_SUCCESS(status)) {
				VDKTRACE(VDKIOCTL,
					("[VDK] IOCTL_DISK_SET_PARTITION_INFO: IoSetPartitionInformation %s\n",
					VdkStatusStr(status)));

				break;
			}

			//
			// Store the partition type in the partition extension.
			//

			part_extension->PartitionInfo.PartitionType =
				set_info->PartitionType;

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Return the partition layout for the drive.
	// Target: 		active device
	// Input:		none
	// Output:		DRIVE_LAYOUT_INFORMATION
	// The layout is returned for the disk device, regardless
	// of which partition was specified for the request.
	//
	case IOCTL_DISK_GET_DRIVE_LAYOUT:
		{
			PDRIVE_LAYOUT_INFORMATION layout;
			ULONG data_size;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// check output buffer length
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(DRIVE_LAYOUT_INFORMATION)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// Read drive layout data from virtual disk
			//
			status = IoReadPartitionTable(
				disk_extension->DeviceObject,
				VDK_BYTES_PER_SECTOR,
				FALSE,
				&layout);

			if (!NT_SUCCESS(status)) {

				VDKTRACE(VDKIOCTL,
					("[VDK] IOCTL_DISK_GET_DRIVE_LAYOUT: IoReadPartitionTable %s\n",
					VdkStatusStr(status)));

				break;
			}

			//
			// Make sure the layout information and device state match
			//
			VdkUpdateDevice(
				disk_extension->DeviceObject, layout);

			//
			// Determine its size and, if the data will fit
			// into the intermediary buffer, return it.
			//
			data_size =
				FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
				(sizeof(PARTITION_INFORMATION) * layout->PartitionCount);

			if (IO_OUTPUTLEN(io_stack) < data_size) {
				data_size = IO_OUTPUTLEN(io_stack);
				status = STATUS_BUFFER_OVERFLOW;
			}
			else {
				status = STATUS_SUCCESS;
			}

			RtlMoveMemory(
				Irp->AssociatedIrp.SystemBuffer, layout, data_size);

			Irp->IoStatus.Information = data_size;

			ExFreePool(layout);
			break;
		}

	//
	// Operation:	Update the disk with new partition information.
	// Target:		disk device only
	// Input:		DRIVE_LAYOUT_INFORMATION
	// Output:		DRIVE_LAYOUT_INFORMATION
	//
	case IOCTL_DISK_SET_DRIVE_LAYOUT:
		{
			PDRIVE_LAYOUT_INFORMATION layout;

			//
			//	check device type
			//
			if ((PPART_EXTENSION)disk_extension != part_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is not a disk device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			//	check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// check input buffer length.
			//
			if (IO_INPUTLEN(io_stack) < sizeof(DRIVE_LAYOUT_INFORMATION)) {

				status = STATUS_INFO_LENGTH_MISMATCH;
				break;
			}

			layout = (PDRIVE_LAYOUT_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

			if (IO_INPUTLEN(io_stack) <
				FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry) +
				(sizeof(PARTITION_INFORMATION) * layout->PartitionCount)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// configure devices to match the layout information
			//
			VdkUpdateDevice(
				disk_extension->DeviceObject, layout);

			//
			// Write partition table to virtual disk
			//
			status = IoWritePartitionTable(
				disk_extension->DeviceObject,
				VDK_BYTES_PER_SECTOR,
				disk_extension->DiskInfo.Sectors,
				disk_extension->DiskInfo.Tracks,
				layout);

			if (!NT_SUCCESS(status)) {
				VDKTRACE(VDKIOCTL,
					("[VDK] IOCTL_DISK_SET_DRIVE_LAYOUT: IoWritePartitionTable %s\n",
					VdkStatusStr(status)));

				break;
			}

			Irp->IoStatus.Information = IO_OUTPUTLEN(io_stack);
			break;
		}

	//
	// Operation:	If the caller is kernel mode, set the verify bit.
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_DISK_INTERNAL_SET_VERIFY:
		{
			if (Irp->RequestorMode == KernelMode) {
				DeviceObject->Flags |= DO_VERIFY_VOLUME;
			}

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	If the caller is kernel mode, clear the verify bit.
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
		{
			if (Irp->RequestorMode == KernelMode) {
				DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
			}

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Returns partition length in bytes
	// Target: 		active device
	// Input:		none
	// Output:		GET_LENGTH_INFORMATION
	//
	case IOCTL_DISK_GET_LENGTH_INFO:	// WINXP
		{
			PGET_LENGTH_INFORMATION	len_info;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			//	check device state
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// Check output buffer size
			//
			if (IO_OUTPUTLEN(io_stack) < sizeof(GET_LENGTH_INFORMATION)) {

				status = STATUS_BUFFER_TOO_SMALL;
				break;
			}

			//
			// store output data
			//
			len_info = (PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

			len_info->Length.QuadPart
				= part_extension->PartitionInfo.PartitionLength.QuadPart;

			Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Checks if disk is writable
	// Target:		active device
	// Input:		none
	// Output:		none
	// Status:		STATUS_SUCCESS					disk is writable / write-blocked
	//				STATUS_WRITE_PROTECTED			disk is read-only
	//				STATUS_INVALID_DEVICE_REQUEST	device is an orphaned partition
	//				STATUS_DEVICE_BUSY				device is a zombie
	//
	case IOCTL_DISK_IS_WRITABLE:
		{
			if (part_extension->PartitionOrdinal == VDK_ZOMBIE) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is a zombie device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_DEVICE_BUSY;
				break;
			}

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {
				status = STATUS_DEVICE_NOT_READY;
			}
			else if (disk_extension->DiskInfo.DiskType == VDK_DISKTYPE_READONLY) {
				status = STATUS_MEDIA_WRITE_PROTECTED;
			}
			else {
				status = STATUS_SUCCESS;
			}

			break;
		}

	//
	// Operation:	Checks if removable media has been changed
	// Target:		any device
	// Input:		none
	// Output:		none
	//
	case IOCTL_DISK_CHECK_VERIFY:		// WIN2K requires this
	case IOCTL_STORAGE_CHECK_VERIFY:
	case IOCTL_STORAGE_CHECK_VERIFY2:
		{
			if (IO_OUTPUTLEN(io_stack) >= sizeof(ULONG)) {
				*(PULONG)Irp->AssociatedIrp.SystemBuffer = 0;
				Irp->IoStatus.Information = sizeof(ULONG);
			}

			status = STATUS_SUCCESS;
			break;
		}

	//
	// Operation:	Verify specified extent of a disk
	// Target: 		active device
	// Input:		VERIFY_INFORMATION
	// Output:		none
	// NT uses this command for formatting
	//
	case IOCTL_DISK_VERIFY:
		{
			PVERIFY_INFORMATION param;

			//
			//	check device type
			//
			if (!disk_extension) {
				VDKTRACE(VDKIOCTL,
					("[VDK] %ws is an orphaned partition device\n",
					part_extension->DeviceName.Buffer));

				status = STATUS_INVALID_DEVICE_REQUEST;
				break;
			}

			//
			// Check media status
			//
			if (!disk_extension->DiskInfo.DiskType ||
				!part_extension->PartitionInfo.PartitionLength.QuadPart) {

				status = STATUS_DEVICE_NOT_READY;
				break;
			}

			//
			// Check input parameter size
			//
			if (IO_INPUTLEN(io_stack) < sizeof(VERIFY_INFORMATION)) {
				status = STATUS_INFO_LENGTH_MISMATCH;
				break;
			}

			//
			// Input parameter sanity check
			//
			param = (PVERIFY_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

			if (param->StartingOffset.QuadPart + param->Length >
				part_extension->PartitionInfo.PartitionLength.QuadPart)
			{
				status = STATUS_INVALID_PARAMETER;
				break;
			}

			//
			// no need to actually perform the operation
			// -- virtual media is always clean
			//
			status = STATUS_SUCCESS;
			break;
		}

	//
	// Since removal lock is irrelevant for virtual disks,
	// there's really nothing to do here...
	//
	case IOCTL_DISK_MEDIA_REMOVAL:
	case IOCTL_STORAGE_MEDIA_REMOVAL:
		{
			status = STATUS_SUCCESS;
			break;
		}

#if 0
	//
	// Mount manager stuff... doesn't work for the time being :-(
	// I'd have to work harder, I guess.
	//
	case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:	// WIN2K
	case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:	// WIN2K
		{
			PMOUNTDEV_NAME	dev_name;

			Irp->IoStatus.Information =
				FIELD_OFFSET(MOUNTDEV_NAME, Name) +
				part_extension->DeviceName.Length;

			if (IO_OUTPUTLEN(io_stack) < Irp->IoStatus.Information) {
				status = STATUS_BUFFER_OVERFLOW;
				break;
			}

			dev_name = (PMOUNTDEV_NAME)Irp->AssociatedIrp.SystemBuffer;

			dev_name->NameLength = part_extension->DeviceName.Length;

			RtlCopyMemory(
				dev_name->Name,
				part_extension->DeviceName.Buffer,
				part_extension->DeviceName.Length);

			status = STATUS_SUCCESS;
		}
		break;
#endif	// 0

	default:
		//
		// Unknown IOCTL request
		//
		status = STATUS_INVALID_DEVICE_REQUEST;
		break;
	}

#if DBG
	if ((TraceFlags & VDKIOCTL) && (((TraceFlags & VDKINFO) == VDKINFO) ||
		(!NT_SUCCESS(status) && (status != STATUS_DEVICE_NOT_READY ||
			(io_stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_DISK_GET_PARTITION_INFO &&
			io_stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_DISK_GET_DRIVE_GEOMETRY &&
			io_stack->Parameters.DeviceIoControl.IoControlCode != IOCTL_DISK_IS_WRITABLE))))) {

		PrintIoCtrlStatus(
			part_extension->DeviceName.Buffer,
			io_stack->Parameters.DeviceIoControl.IoControlCode,
			status);
	}
#endif	// DBG

	if (status == STATUS_PENDING) {
		//
		// Let the device thread perform the operation
		//
		IoMarkIrpPending(Irp);

		ExInterlockedInsertTailList(
			&disk_extension->ListHead,
			&Irp->Tail.Overlay.ListEntry,
			&disk_extension->ListLock);

		KeSetEvent(
			&disk_extension->RequestEvent,
			(KPRIORITY) 0,
			FALSE);
	}
	else {
		//
		// complete the operation
		//
		Irp->IoStatus.Status = status;
		IoCompleteRequest(Irp, IO_NO_INCREMENT);
	}

	return status;
}