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


/*
	vdkupdate.c

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

#include "vdkbase.h"
#include "vdkutil.h"
#include "vdkaccess.h"

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VdkUpdateDevice)
#endif	// ALLOC_PRAGMA

//
// This routine creates, deletes and changes device objects when
// the IOCTL_SET_DRIVE_LAYOUT is called.  It also updates the partition
// number information in the structure that will be returned to the caller.
// This routine can be used even in the GET_INFO case by insuring that
// the RewritePartition flag is off for all partitions in the drive layout
// structure.
//
VOID
VdkUpdateDevice(
	IN PDEVICE_OBJECT				DiskObject,
	IN PDRIVE_LAYOUT_INFORMATION	DriveLayout)
{
	PDISK_EXTENSION			disk_extension;
	PPART_EXTENSION			part_extension;
	PPARTITION_INFORMATION	partition_info;

	ULONG	part_count;
	ULONG	part_ordinal;
	ULONG	idx;

	VDKTRACE(VDKUPDATE | VDKINFO, ("[VDK] VdkUpdateDevice\n"));

	//
	// The virtual disk is non-partitioned (super-floppy) image
	// in this case, entire disk is treated as a single partition
	// and device 0 represents the partition.
	//
	if (DriveLayout->PartitionCount == 1 &&
		DriveLayout->PartitionEntry[0].StartingOffset.QuadPart == 0) {

		VDKTRACE(VDKUPDATE | VDKWARN,
			("[VDK] Non-Partitioned disk\n"));

		DriveLayout->PartitionEntry[0].PartitionNumber = 0;
		return;
	}

	//
	// First clear all partition numbers in the partition list.
	//
	part_count	= DriveLayout->PartitionCount;

	for (idx = 0; idx < part_count; idx++) {
		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].StartingOffset = %I64u\n", idx,
			DriveLayout->PartitionEntry[idx].StartingOffset.QuadPart));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].PartitionLength = %I64u\n", idx,
			DriveLayout->PartitionEntry[idx].PartitionLength.QuadPart));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].HiddenSectors = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].HiddenSectors));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].PartitionNumber = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].PartitionNumber));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].PartitionType = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].PartitionType));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].BootIndicator = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].BootIndicator));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].RecognizedPartition = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].RecognizedPartition));

		VDKTRACE(VDKUPDATE | VDKINFO,
			("[VDK] PartitionEntry[%lu].RewritePartition = %lu\n", idx,
			DriveLayout->PartitionEntry[idx].RewritePartition));

		DriveLayout->PartitionEntry[idx].PartitionNumber = 0;
	}

	disk_extension = (PDISK_EXTENSION)DiskObject->DeviceExtension;
	part_extension = (PPART_EXTENSION)disk_extension;

	//
	// Walk through chain of partition devices to determine if
	// each existing partition device has a match in the layout list.
	//

	while (part_extension->NextPartition) {

		PPART_EXTENSION prev_part;

		prev_part = part_extension;

		part_extension =
			part_extension->NextPartition->DeviceExtension;

		//
		// Loop through partition information table to look for match.
		//
		part_ordinal = 0;

		for (idx = 0; idx < part_count; idx++) {

			//
			// Get partition info entry.
			//
			partition_info = &DriveLayout->PartitionEntry[idx];

			//
			// Skip empty or extended partitions
			//
			if (partition_info->PartitionType == PARTITION_ENTRY_UNUSED ||
				IsContainerPartition(partition_info->PartitionType)) {
				continue;
			}

			//
			// Advance partition ordinal.
			//
			part_ordinal++;

			//
			// Check if start offset and length is the same
			//
			if (partition_info->StartingOffset.QuadPart ==
				part_extension->PartitionInfo.StartingOffset.QuadPart &&
				partition_info->PartitionLength.QuadPart ==
				part_extension->PartitionInfo.PartitionLength.QuadPart) {

				//
				// This device matches the partition item in the list.
				//
				VDKTRACE(VDKUPDATE | VDKINFO,
					("[VDK] list item #%u matches object %u\n",
					part_extension->PartitionInfo.PartitionNumber,
					idx));

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

				//
				// update PartitionOrdinal to match the order this partition
				// is placed on the disk
				//
				part_extension->PartitionOrdinal = part_ordinal;

				//
				// A match is found.  If this partition is marked for update,
				// check for a partition type change.
				//
				if (partition_info->RewritePartition) {
					part_extension->PartitionInfo.PartitionType
						= partition_info->PartitionType;
				}

				break;
			}

		}	// next partition info entry

		if (idx == part_count) {

			//
			// no match was found, indicate the partition represented by
			// this partition device is gone.	This device is no longer used
			// so remove it from the chain
			//
			if (!prev_part->NextPartition->Vpb ||
				!prev_part->NextPartition->Vpb->ReferenceCount) {

				VDKTRACE(VDKUPDATE | VDKINFO,
					("[VDK] %ws becomes an orphan.\n",
					part_extension->DeviceName.Buffer));

				part_extension->PartitionOrdinal	= 0;
			}
			else {
				VDKTRACE(VDKUPDATE | VDKWARN,
					("[VDK] %ws becomes a zombie.\n",
					part_extension->DeviceName.Buffer));

				part_extension->PartitionOrdinal	= VDK_ZOMBIE;
			}

			prev_part->NextPartition = part_extension->NextPartition;

			RtlZeroMemory(
				&part_extension->PartitionInfo,
				sizeof(part_extension->PartitionInfo));

			part_extension->FirstPartition 		= NULL;
			part_extension->NextPartition 		= NULL;

			if (part_extension->SymbolicLink.Buffer) {
				VDKTRACE(VDKUPDATE | VDKINFO,
					("[VDK] Deleting symbolic link %ws\n",
					part_extension->SymbolicLink.Buffer));

				IoDeleteSymbolicLink(&part_extension->SymbolicLink);
				ExFreePool(part_extension->SymbolicLink.Buffer);
				RtlZeroMemory(
					&part_extension->SymbolicLink,
					sizeof(part_extension->SymbolicLink));
			}

			part_extension = prev_part;
		   	continue;
		}

		//
		// Adjust access type of the device object
		//
		if (disk_extension->DiskInfo.DiskType == VDK_DISKTYPE_READONLY) {
			prev_part->NextPartition->Characteristics |= FILE_READ_ONLY_DEVICE;
		}
		else {
			prev_part->NextPartition->Characteristics &= ~FILE_READ_ONLY_DEVICE;
		}

	}	// next device in the chain

	//
	// Make sure each list item has a partition device to represent it.
	// In some cases new device objects will be created.
	//
	part_ordinal = 0;

	for (idx = 0; idx < part_count; idx++) {

		//
		// Partition info entry to work with
		//
		partition_info = &DriveLayout->PartitionEntry[idx];

		//
		// Skip empty or extended partitons
		//
		if (partition_info->PartitionType == PARTITION_ENTRY_UNUSED ||
			IsContainerPartition(partition_info->PartitionType)) {

			VDKTRACE(VDKUPDATE | VDKINFO,
				("[VDK] Skip: item %lu is unused or container\n", idx));

			continue;
		}

		//
		// Keep track of position of the partition on the disk
		//
		part_ordinal++;

		//
		// Skip partitions which don't need to be updated
		//
		if (!partition_info->RewritePartition ||	// rewrite flag is off or
			partition_info->PartitionNumber) {		// match was found

			// no need to update device object for this partition

			VDKTRACE(VDKUPDATE | VDKINFO,
				("[VDK] Skip: item %lu - matched or not for rewrite\n", idx));

			continue;
		}

		//
		// If partition number is still zero then a new device object
		// must be associated.
		//
		if (partition_info->PartitionNumber == 0) {

			PDEVICE_OBJECT	part_object;
			WCHAR			name_buf[40];
			UNICODE_STRING	uni_name;
			ULONG			part_num;
			NTSTATUS		status;

			//
			//	First search orphaned partition device available
			//

			part_object = DiskObject->DriverObject->DeviceObject;

			while (part_object) {
				if ((!part_object->Vpb || !part_object->Vpb->ReferenceCount) &&
					((PPART_EXTENSION)part_object->DeviceExtension)->FirstPartition == NULL) {
					break;
				}

				part_object = part_object->NextDevice;
			}

			if (part_object) {
				if (disk_extension->DiskInfo.DiskType == VDK_DISKTYPE_READONLY) {
					part_object->Characteristics |= FILE_READ_ONLY_DEVICE;
				}
				else {
					part_object->Characteristics &= ~FILE_READ_ONLY_DEVICE;
				}

				//
				// Link into the partition list.
				//
				part_extension->NextPartition = part_object;

				part_extension =
					(PPART_EXTENSION)part_object->DeviceExtension;
			}
			else {

				//
				//	Create a partition device
				//
				part_num = 0;

				do {
					swprintf(name_buf,
						L"\\Device\\VirtualDiskVolume%u", ++part_num);

					RtlInitUnicodeString(&uni_name, name_buf);

					status = IoCreateDevice(
						DiskObject->DriverObject,
						sizeof(PART_EXTENSION),
						&uni_name,
						FILE_DEVICE_DISK,
						(DiskObject->Characteristics & FILE_READ_ONLY_DEVICE),
						FALSE,
						&part_object);

					if (status != STATUS_OBJECT_NAME_COLLISION) {
						break;
					}
				}
				while (part_num < 100);

				if (!NT_SUCCESS(status)) {
					VDKTRACE(VDKUPDATE,
						("[VDK] IoCreateDevice(%ws) %s\n",
						name_buf,
						VdkStatusStr(status)));

					continue;
				}

				VDKTRACE(VDKUPDATE|VDKINFO,
					("[VDK] Created a partition device %ws\n",
					name_buf));

				//
				// Set up device object fields.
				//
				part_object->Flags		|= DO_DIRECT_IO;
				part_object->StackSize = DiskObject->StackSize;
				part_object->Flags		&= ~DO_DEVICE_INITIALIZING;

				//
				// Link into the partition chain.
				//
				part_extension->NextPartition = part_object;

				part_extension =
					(PPART_EXTENSION)part_object->DeviceExtension;

				RtlZeroMemory(part_extension, sizeof(PART_EXTENSION));

				//
				// Store device name
				//
				part_extension->DeviceName.Buffer =
					ExAllocatePool(NonPagedPool, uni_name.Length + sizeof(WCHAR));

				if (part_extension->DeviceName.Buffer) {
					part_extension->DeviceName.Length = uni_name.Length;
					part_extension->DeviceName.MaximumLength = uni_name.Length;

					RtlZeroMemory(
						part_extension->DeviceName.Buffer,
						uni_name.Length + sizeof(WCHAR));

					RtlCopyMemory(
						part_extension->DeviceName.Buffer,
						uni_name.Buffer,
						uni_name.Length);
				}
				else {
					VDKTRACE(VDKUPDATE,
						("[VDK] Failed to allocate device name buffer.\n"));
				}
			}

			VDKTRACE(VDKUPDATE | VDKINFO,
				("[VDK] Partition List Item %lu uses %ws\n",
				idx, part_extension->DeviceName.Buffer));

			//
			// Set up device extension fields.
			//
			part_extension->FirstPartition	= disk_extension;
			part_extension->NextPartition	= NULL;

			//
			// Create symbolic link
			// Win2K/XP style
			//
			part_num = 0;

			do {
				swprintf(name_buf,
					L"\\Device\\Harddisk%lu\\Partition%u",
					disk_extension->PhysicalNumber, ++part_num);

				RtlInitUnicodeString(&uni_name, name_buf);

				status = IoCreateSymbolicLink(
					&uni_name, &part_extension->DeviceName);

				if (status != STATUS_OBJECT_NAME_COLLISION) {
					break;
				}
			}
			while (part_num <= 128);

			if (NT_SUCCESS(status)) {
				VDKTRACE(VDKUPDATE|VDKINFO,
					("[VDK] Created a symbolic link %ws\n",
					name_buf));

				part_extension->SymbolicLink.Buffer =
					ExAllocatePool(NonPagedPool, uni_name.Length + sizeof(WCHAR));

				if (part_extension->SymbolicLink.Buffer) {
					part_extension->SymbolicLink.Length = uni_name.Length;
					part_extension->SymbolicLink.MaximumLength = uni_name.Length;

					RtlZeroMemory(
						part_extension->SymbolicLink.Buffer,
						uni_name.Length + sizeof(WCHAR));

					RtlCopyMemory(
						part_extension->SymbolicLink.Buffer,
						uni_name.Buffer,
						uni_name.Length);
				}
				else {
					VDKTRACE(VDKUPDATE,
						("[VDK] Failed to allocate symlink name buffer.\n"));
				}
			}
			else {
				VDKTRACE(VDKUPDATE,
					("[VDK] IoCreateSymbolicLink(%ws) %s\n",
					name_buf,
					VdkStatusStr(status)));
			}

			partition_info->PartitionNumber = part_num;

			//
			// informs the Mount Manager of the new device
			//
#if 0
			//
			// I can't make this work for the time being...
			// I guess I have to learn more about MM & PnP stuff :-(
			//
			{
				PDEVICE_OBJECT	mntmgr_dev;

				// Obtain a pointer to the Mount Manager device object
				{
					UNICODE_STRING	mntmgr_name;
					PFILE_OBJECT	mntmgr_file;

					RtlInitUnicodeString(
						&mntmgr_name,
						MOUNTMGR_DEVICE_NAME);

					status = IoGetDeviceObjectPointer(
						&mntmgr_name,
						FILE_READ_ATTRIBUTES,
						&mntmgr_file,
						&mntmgr_dev);
				}

				if (NT_SUCCESS(status)) {
					IO_STATUS_BLOCK			io_status;
					KEVENT					event;
					PIRP					irp;
					PMOUNTMGR_TARGET_NAME	target_name;
					USHORT					target_name_buf[MAXIMUM_FILENAME_LENGTH];

					target_name = (PMOUNTMGR_TARGET_NAME)target_name_buf;

					target_name->DeviceNameLength =
						part_extension->DeviceName.Length;

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

					KeInitializeEvent(&event, NotificationEvent, FALSE);

					irp = IoBuildDeviceIoControlRequest(
						IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
						mntmgr_dev,
						target_name,
						sizeof(target_name->DeviceNameLength) + target_name->DeviceNameLength,
						NULL,
						0,
						FALSE,
						&event,
						&io_status);

					if (irp) {
						status = IoCallDriver(mntmgr_dev, irp);

						if (status == STATUS_PENDING) {
							KeWaitForSingleObject(
								&event, Executive, KernelMode, FALSE, NULL);

							status = io_status.Status;
						}
					}
				}
				else {
					//
					// probably running on Windows NT
					//
					VDKTRACE(VDKUPDATE | VDKWARN,
						("[VDK] Failed to obtain the Mount Manager device object.\n"));
				}
			}
#endif	// 0
		}

		//
		// Update partition extension to match the latest state
		//
		part_extension->PartitionInfo		= *partition_info;
		part_extension->PartitionOrdinal	= part_ordinal;
	}

	VDKTRACE(VDKUPDATE | VDKINFO, ("[VDK] VdkUpdateDevice - EXIT\n"));

	return;
}