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


/*
	vdkdrv.c

	Virtual Disk kernel-mode driver for Windows NT platform
	Standard driver routines other than ioctl dispatch routine
	Copyright (C) 2003 Ken Kato
*/

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

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, VdkShutdown)
#pragma alloc_text(PAGE, VdkCreateClose)
#pragma alloc_text(PAGE, VdkUnloadDriver)
#endif	// ALLOC_PRAGMA

//
//	runtime operating system version
//
extern ULONG OsMajorVersion = 0;
extern ULONG OsMinorVersion = 0;
extern ULONG OsBuildNumber 	= 0;

//
// Driver Entry routine
//
NTSTATUS
DriverEntry (
	IN PDRIVER_OBJECT	DriverObject,
	IN PUNICODE_STRING	RegistryPath)
{
	ULONG		idx;
	ULONG		disk_num = 0;
	PWCHAR 		reg_path = NULL;
	NTSTATUS	status;

	VDKTRACE(0, ("[VDK] " VDK_PRODUCT_NAME " version "
		VDK_DRIVER_VERSION_STR " by " VDK_COMPANY_NAME "\n"));

	// Store running OS version into global variables

	StoreCurrentOsVersion();

	VDKTRACE(0,
		("[VDK] Running on Windows NT %lu.%lu build %lu\n",
		OsMajorVersion, OsMinorVersion, OsBuildNumber));

	// Get driver config data from registry

	reg_path = ExAllocatePool(
		PagedPool, RegistryPath->Length + sizeof(WCHAR));

	if (reg_path) {
#if DBG
		RTL_QUERY_REGISTRY_TABLE	reg_param[3];
		ULONG default_trace = (ULONG)~(VDKINFO | VDKWARN);
#else
		RTL_QUERY_REGISTRY_TABLE	reg_param[2];
#endif
		idx = 0;

		RtlZeroMemory(reg_path, RegistryPath->Length + sizeof(WCHAR));
		RtlMoveMemory(reg_path, RegistryPath->Buffer, RegistryPath->Length);

		RtlZeroMemory(reg_param, sizeof(reg_param));

		reg_param[0].Flags			= RTL_QUERY_REGISTRY_DIRECT;
		reg_param[0].Name			= VDK_REG_DISKNUM_VALUE;
		reg_param[0].EntryContext 	= &disk_num;
		reg_param[0].DefaultType	= REG_DWORD;
		reg_param[0].DefaultData	= &idx;
		reg_param[0].DefaultLength	= sizeof(ULONG);

#if DBG
		reg_param[1].Flags			= RTL_QUERY_REGISTRY_DIRECT;
		reg_param[1].Name			= L"TraceFlags";
		reg_param[1].EntryContext	= &TraceFlags;
		reg_param[1].DefaultType	= REG_DWORD;
		reg_param[1].DefaultData	= &default_trace;
		reg_param[1].DefaultLength	= sizeof(ULONG);
#endif

		status = RtlQueryRegistryValues(
			RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
			reg_path, reg_param, NULL, NULL);

		ExFreePool(reg_path);

		if (!NT_SUCCESS(status)) {

			VDKTRACE(0,
				("[VDK] Failed to get config dat from registry - %s\n",
				VdkStatusStr(status)));

			disk_num = VDK_DEFAULT_DISK_NUM;
		}

		if (disk_num == 0 || disk_num > VDK_MAXIMUM_DISK_NUM) {
			VDKTRACE(0,
				("[VDK] Invalid disk device number - %lu\n", disk_num));

			disk_num = VDK_DEFAULT_DISK_NUM;
		}
	}
	else {
		//
		// Failed to allocate regstry path buffer
		//
		VDKTRACE(0,
			("[VDK] Failed to allocate registry path buffer.\n"));

		disk_num = VDK_DEFAULT_DISK_NUM;
	}

	VDKTRACE(0,
		("[VDK] Number of Disks = %lu, TraceFlags = 0x%08x\n",
		disk_num, TraceFlags));

	// Create disk device objects

	idx = 0;

	do {
		status = VdkCreateDisk(DriverObject, idx);

		if (!NT_SUCCESS(status)) {

			if (DriverObject->DeviceObject) {
				// at least one disk device was created
				status = STATUS_SUCCESS;
			}
			break;
		}
	}
	while (++idx < disk_num);

	if (NT_SUCCESS(status)) {

		// Setup dispatch table

		DriverObject->MajorFunction[IRP_MJ_CREATE]			= VdkCreateClose;
		DriverObject->MajorFunction[IRP_MJ_CLOSE]			= VdkCreateClose;
		DriverObject->MajorFunction[IRP_MJ_READ]			= VdkReadWrite;
		DriverObject->MajorFunction[IRP_MJ_WRITE]			= VdkReadWrite;
		DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]	= VdkDeviceControl;
		DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]		= VdkShutdown;
		DriverObject->DriverUnload = VdkUnloadDriver;

		VDKTRACE(0, ("[VDK] %lu virtual disks successfully initialized.\n", idx));
	}

	return status;
}

//
//	Device shutdown routine
//
NTSTATUS
VdkShutdown (
	IN PDEVICE_OBJECT	DeviceObject,
	IN PIRP				Irp)
{
	PDISK_EXTENSION	disk_extension;

	UNREFERENCED_PARAMETER(Irp);

	VDKTRACE(VDKDISPATCH | VDKINFO,
		("[VDK] VdkShutdown\n"));

	disk_extension =
		(PDISK_EXTENSION)DeviceObject->DeviceExtension;

	if (disk_extension &&
		disk_extension == disk_extension->FirstPartition &&
		disk_extension->DiskInfo.DiskType) {

		// close if virtual disk is opened

		disk_extension->DiskInfo.DiskType = VDK_DISKTYPE_NONE;
		VdkCloseDisk(&disk_extension->DiskInfo);
	}

	Irp->IoStatus.Information = 0;
	Irp->IoStatus.Status = STATUS_SUCCESS;

	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return STATUS_SUCCESS;
}

//
// IRP_MJ_CREATE and IRP_MJ_CLOSE handler
// Really nothing to do here...
//
NTSTATUS
VdkCreateClose (
	IN PDEVICE_OBJECT	DeviceObject,
	IN PIRP 			Irp)
{
	UNREFERENCED_PARAMETER(DeviceObject);

	VDKTRACE(VDKDISPATCH | VDKINFO,
		("[VDK] %ws %s\n",
		((PPART_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer,
		IoGetCurrentIrpStackLocation(Irp)->MajorFunction ==
			IRP_MJ_CLOSE ? "IRP_MJ_CLOSE" : "IRP_MJ_CREATE"));

	//
	// Information value for MJ_CLOSE is not defined so it's OK to
	// return FILE_OPEND as defined for MJ_OPEN
	//
	Irp->IoStatus.Information = FILE_OPENED;
	Irp->IoStatus.Status = STATUS_SUCCESS;

	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return STATUS_SUCCESS;
}

//
// Driver unload routine
//
VOID
VdkUnloadDriver (
	IN PDRIVER_OBJECT DriverObject)
{
	PDEVICE_OBJECT	device_object;
	PDEVICE_OBJECT	next_device;

	VDKTRACE(VDKDISPATCH | VDKINFO,
		("[VDK] VdkUnloadDriver\n"));

	device_object = DriverObject->DeviceObject;

	// Delete all device objects

	while (device_object) {
		next_device = device_object->NextDevice;

		VdkDeleteDevice(device_object);

		device_object = next_device;
	}

	VDKTRACE(0, ("[VDK] driver unloaded\n"));
}

//
// IRP_MJ_READ and IRP_MJ_WRITE handler
// Insert the IRP into queue list.
// Actual read/write operation is performed by device thread
//
#define IO_READ_OFF(p)	(p)->Parameters.Read.ByteOffset.QuadPart
#define IO_READ_LEN(p)	(p)->Parameters.Read.Length

NTSTATUS
VdkReadWrite (
	IN PDEVICE_OBJECT	DeviceObject,
	IN PIRP 			Irp)
{
	PPART_EXTENSION		part_extension;
	PDISK_EXTENSION		disk_extension;
	PIO_STACK_LOCATION	io_stack;

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

	disk_extension =
		(PDISK_EXTENSION)part_extension->FirstPartition;

	io_stack = IoGetCurrentIrpStackLocation(Irp);

	//
	//	Check if the device is a zombie -- closed but not released
	//	READ / WRITE request must succeed or NTFS driver cannot release
	//	the device.
	//
	if (part_extension->PartitionOrdinal == VDK_ZOMBIE) {
		VDKTRACE(0,
			("[VDK] %ws %s: ZOMBIE DEVICE\n",
			part_extension->DeviceName.Buffer,
			io_stack->MajorFunction == IRP_MJ_WRITE ? "IRP_MJ_WRITE" : "IRP_MJ_READ"));

		Irp->IoStatus.Status = STATUS_SUCCESS;
		Irp->IoStatus.Information = IO_READ_LEN(io_stack);

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_SUCCESS;
	}

	//
	// Check if image is opened
	//
	if (!disk_extension || !disk_extension->DiskInfo.DiskType ||
		!part_extension->PartitionInfo.PartitionLength.QuadPart)	{

		VDKTRACE(VDKWARN,
			("[VDK] %ws %s: STATUS_DEVICE_NOT_READY\n",
			part_extension->DeviceName.Buffer,
			io_stack->MajorFunction == IRP_MJ_WRITE ? "IRP_MJ_WRITE" : "IRP_MJ_READ"));

		Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
		Irp->IoStatus.Information = 0;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_DEVICE_NOT_READY;
	}

	//
	// Check for invalid parameters.  It is an error for the starting offset
	// + length to go past the end of the partition, or for the length or
	// offset to not be a proper multiple of the sector size.
	//
	// Others are possible, but we don't check them since we trust the
	// file system and they aren't deadly.
	//
	if (IO_READ_OFF(io_stack) + IO_READ_LEN(io_stack) >
		part_extension->PartitionInfo.PartitionLength.QuadPart) {

		VDKTRACE(0,
			("[VDK] %ws Offset:%I64u + Length:%u goes past partition size:%I64u\n",
			part_extension->DeviceName.Buffer,
			IO_READ_OFF(io_stack),
			IO_READ_LEN(io_stack),
			part_extension->PartitionInfo.PartitionLength.QuadPart));

		Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
		Irp->IoStatus.Information = 0;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_INVALID_PARAMETER;
	}

	if ((IO_READ_LEN(io_stack) & VDK_SECTOR_ALIGNMENT_MASK) ||
		(IO_READ_OFF(io_stack) & VDK_SECTOR_ALIGNMENT_MASK)) {

		VDKTRACE(0,
			("[VDK] %ws Invalid Alignment Offset:%I64u Length:%u\n",
			part_extension->DeviceName.Buffer,
			IO_READ_OFF(io_stack), IO_READ_LEN(io_stack)));

		Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
		Irp->IoStatus.Information = 0;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_INVALID_PARAMETER;
	}

	//
	// Check if write operation is allowed
	//
	if (io_stack->MajorFunction == IRP_MJ_WRITE) {
		if (disk_extension->DiskInfo.DiskType == VDK_DISKTYPE_READONLY) {

			VDKTRACE(VDKWRITE | VDKWARN,
				("[VDK] %ws IRP_MJ_WRITE: STATUS_MEDIA_WRITE_PROTECTED\n",
				disk_extension->DeviceName.Buffer));

			Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED;
			Irp->IoStatus.Information = 0;

			IoCompleteRequest(Irp, IO_NO_INCREMENT);

			return STATUS_MEDIA_WRITE_PROTECTED;
		}
		else if (disk_extension->DiskInfo.DiskType == VDK_DISKTYPE_WRITEBLOCK) {

			VDKTRACE(VDKWRITE | VDKINFO,
				("[VDK] %ws IRP_MJ_WRITE: Blocked write operation\n",
				disk_extension->DeviceName.Buffer));

			Irp->IoStatus.Status = STATUS_SUCCESS;
			Irp->IoStatus.Information = IO_READ_LEN(io_stack);

			IoCompleteRequest(Irp, IO_NO_INCREMENT);

			return STATUS_SUCCESS;
		}
	}

	//
	// If read/write data length is 0, we are done
	//
	if (IO_READ_LEN(io_stack) == 0) {
		Irp->IoStatus.Status = STATUS_SUCCESS;
		Irp->IoStatus.Information = 0;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_SUCCESS;
	}

	//
	// The offset passed in is relative to the start of the partition.
	// We always work from partition 0 (the whole disk) so adjust the
	// offset.
	//
	IO_READ_OFF(io_stack) +=
		part_extension->PartitionInfo.StartingOffset.QuadPart;

	//
	// Mark the IRP as pending, insert the IRP into queue list
	// then signal the device thread to perform the operation
	//
	IoMarkIrpPending(Irp);

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

	KeSetEvent(
		&disk_extension->RequestEvent,
		(KPRIORITY) 0,
		FALSE);

	return STATUS_PENDING;
}