reactos/modules/rosapps/drivers/vfd/vfddrv.c
Pierre Schweitzer 25c7e1a8d0
[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
floppy drives in ReactOS and mount images on them.
Only the cmd got imported. The GUI interface may come later on.
Note that, as for vcdrom, the driver is left disabled and you need to explicitely
start it through vfd command line interface.

CORE-14090
2017-12-16 21:48:34 +01:00

523 lines
12 KiB
C

/*
vfddrv.c
Virtual Floppy Drive for Windows NT platform
Kernel mode driver: miscellaneous driver functions
Copyright (C) 2003-2005 Ken Kato
*/
#include "imports.h"
#include "vfddrv.h"
#include "vfddbg.h"
//
// driver reinitialize routine
// -- create a drive letter for each device
//
#ifdef __cplusplus
extern "C"
#endif // __cplusplus
static VOID
NTAPI
VfdReinitialize(
IN PDRIVER_OBJECT DriverObject,
IN PVOID Context,
IN ULONG Count);
//
// specify code segment
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, VfdReinitialize)
#pragma alloc_text(PAGE, VfdUnloadDriver)
#pragma alloc_text(PAGE, VfdCreateClose)
#pragma alloc_text(PAGE, VfdCopyUnicode)
#pragma alloc_text(PAGE, VfdFreeUnicode)
#endif // ALLOC_PRAGMA
//
// operating system version
//
#ifndef __REACTOS__
extern ULONG OsMajorVersion = 0;
extern ULONG OsMinorVersion = 0;
extern ULONG OsBuildNumber = 0;
#else
ULONG OsMajorVersion = 0;
ULONG OsMinorVersion = 0;
ULONG OsBuildNumber = 0;
#endif
//
// Trace level flag
//
#if DBG
#ifndef __REACTOS__
extern ULONG TraceFlags = (ULONG)-1;
#else
ULONG TraceFlags = (ULONG)-1;
#endif
#endif // DBG
//
// Driver Entry routine
//
NTSTATUS
NTAPI
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
PVFD_DRIVER_EXTENSION driver_extension;
ULONG number_of_devices = VFD_DEFAULT_DEVICES;
ASSERT(DriverObject);
// Get operating system version
PsGetVersion(&OsMajorVersion, &OsMinorVersion, &OsBuildNumber, NULL);
#ifdef VFD_PNP
#define VFD_PNP_TAG "(Plug & Play version)"
#else
#define VFD_PNP_TAG
#endif
VFDTRACE(0, ("[VFD] %s %s" VFD_PNP_TAG "\n",
VFD_PRODUCT_NAME, VFD_DRIVER_VERSION_STR));
VFDTRACE(0,
("[VFD] Running on Windows NT %lu.%lu build %lu\n",
OsMajorVersion, OsMinorVersion, OsBuildNumber));
VFDTRACE(0,
("[VFD] Build Target Environment: %d\n", VER_PRODUCTBUILD));
#ifdef VFD_PNP
// Create device_extension for the driver object to store driver specific
// information. Device specific information are stored in device extension
// for each device object.
status = IoAllocateDriverObjectExtension(
DriverObject,
VFD_DRIVER_EXTENSION_ID,
sizeof(VFD_DRIVER_EXTENSION),
&driver_extension);
if(!NT_SUCCESS(status)) {
VFDTRACE(0, ("[VFD] IoAllocateDriverObjectExtension - %s\n",
GetStatusName(status)));
return status;
}
#else // VFD_PNP
// Windows NT doesn't have the IoAllocateDriverObjectExtension
// function and I think there's little point in making a non-PnP
// driver incompatible with Windows NT.
driver_extension = (PVFD_DRIVER_EXTENSION)ExAllocatePoolWithTag(
PagedPool, sizeof(VFD_DRIVER_EXTENSION), VFD_POOL_TAG);
if (!driver_extension) {
VFDTRACE(0, ("[VFD] failed to allocate the driver extension.\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
#endif // VFD_PNP
RtlZeroMemory(driver_extension, sizeof(VFD_DRIVER_EXTENSION));
//
// Copy the registry path into the driver extension so we can use it later
//
if (VfdCopyUnicode(&(driver_extension->RegistryPath), RegistryPath)) {
//
// Read config values from the registry
//
RTL_QUERY_REGISTRY_TABLE params[3];
ULONG default_devs = VFD_DEFAULT_DEVICES;
#if DBG
ULONG default_trace = (ULONG)-1;
#endif
RtlZeroMemory(params, sizeof(params));
VFDTRACE(0, ("[VFD] Registry Path: %ws\n",
driver_extension->RegistryPath.Buffer));
params[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
params[0].Name = VFD_REG_DEVICE_NUMBER;
params[0].EntryContext = &number_of_devices;
params[0].DefaultType = REG_DWORD;
params[0].DefaultData = &default_devs;
params[0].DefaultLength = sizeof(ULONG);
#if DBG
params[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
params[1].Name = VFD_REG_TRACE_FLAGS;
params[1].EntryContext = &TraceFlags;
params[1].DefaultType = REG_DWORD;
params[1].DefaultData = &default_trace;
params[1].DefaultLength = sizeof(ULONG);
#endif // DBG
status = RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
driver_extension->RegistryPath.Buffer,
params, NULL, NULL);
if (!NT_SUCCESS(status) ||
number_of_devices == 0 ||
number_of_devices > VFD_MAXIMUM_DEVICES) {
number_of_devices = VFD_DEFAULT_DEVICES;
}
VFDTRACE(0,("[VFD] NumberOfDevices = %lu\n", number_of_devices));
VFDTRACE(0,("[VFD] TraceFlags = 0x%08x\n", TraceFlags));
}
else {
VFDTRACE(0, ("[VFD] failed to allocate the registry path buffer.\n"));
// this error is not fatal
}
//
// Create VFD device objects
//
do {
#ifdef VFD_PNP
status = VfdCreateDevice(DriverObject, NULL);
#else // VFD_PNP
status = VfdCreateDevice(DriverObject, driver_extension);
#endif // VFD_PNP
if (!NT_SUCCESS(status)) {
break;
}
}
while (driver_extension->NumberOfDevices < number_of_devices);
if (!driver_extension->NumberOfDevices) {
// Failed to create even one device
VfdFreeUnicode(&(driver_extension->RegistryPath));
return status;
}
// Setup dispatch table
DriverObject->MajorFunction[IRP_MJ_CREATE] = VfdCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = VfdCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = VfdReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = VfdReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VfdDeviceControl;
#ifdef VFD_PNP
DriverObject->MajorFunction[IRP_MJ_PNP] = VfdPlugAndPlay;
DriverObject->MajorFunction[IRP_MJ_POWER] = VfdPowerControl;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = VfdSystemControl;
DriverObject->DriverExtension->AddDevice = VfdAddDevice;
#endif // VFDPNP
DriverObject->DriverUnload = VfdUnloadDriver;
// Register the driver reinitialize routine to be called
// *after* the DriverEntry routine returns
IoRegisterDriverReinitialization(
DriverObject, VfdReinitialize, NULL);
VFDTRACE(VFDINFO,
("[VFD] driver initialized with %lu devices.\n",
driver_extension->NumberOfDevices));
return STATUS_SUCCESS;
}
//
// Driver unload routine
// Cleans up the device objects and other resources
//
VOID
NTAPI
VfdUnloadDriver (
IN PDRIVER_OBJECT DriverObject)
{
PDEVICE_OBJECT device_object;
PVFD_DRIVER_EXTENSION driver_extension;
VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - IN\n"));
device_object = DriverObject->DeviceObject;
#ifdef VFD_PNP
driver_extension = IoGetDriverObjectExtension(
DriverObject, VFD_DRIVER_EXTENSION_ID);
#else
if (device_object && device_object->DeviceExtension) {
driver_extension =
((PDEVICE_EXTENSION)(device_object->DeviceExtension))->DriverExtension;
}
else {
driver_extension = NULL;
}
#endif // VFD_PNP
//
// Delete all remaining device objects
//
while (device_object) {
PDEVICE_OBJECT next_device = device_object->NextDevice;
VfdDeleteDevice(device_object);
device_object = next_device;
}
//
// Release the driver extension and the registry path buffer
//
if (driver_extension) {
if (driver_extension->RegistryPath.Buffer) {
VFDTRACE(0, ("[VFD] Releasing the registry path buffer\n"));
ExFreePool(driver_extension->RegistryPath.Buffer);
}
#ifndef VFD_PNP
// The system takes care of freeing the driver extension
// allocated with IoAllocateDriverObjectExtension in a PnP driver.
VFDTRACE(0, ("[VFD] Releasing the driver extension\n"));
ExFreePool(driver_extension);
#endif // VFD_PNP
}
VFDTRACE(VFDINFO, ("[VFD] VfdUnloadDriver - OUT\n"));
}
//
// IRP_MJ_CREATE and IRP_MJ_CLOSE handler
// Really nothing to do here...
//
NTSTATUS
NTAPI
VfdCreateClose (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
#if DBG
if (DeviceObject && DeviceObject->DeviceExtension &&
((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer) {
VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n",
GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->DeviceName.Buffer));
}
else {
VFDTRACE(VFDINFO, ("[VFD] %-40s %p\n",
GetMajorFuncName(IoGetCurrentIrpStackLocation(Irp)->MajorFunction),
DeviceObject));
}
#endif // DBG
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
//
// Called after the DriverEntry routine has returned
// (Re)Create a persistent drive letter for each device
//
VOID
NTAPI
VfdReinitialize(
IN PDRIVER_OBJECT DriverObject,
IN PVOID Context,
IN ULONG Count)
{
PDEVICE_OBJECT device_object;
PDEVICE_EXTENSION device_extension;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(Count);
VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - IN\n"));
device_object = DriverObject->DeviceObject;
while (device_object) {
device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
#ifdef VFD_MOUNT_MANAGER
if (OsMajorVersion >= 5) {
// Windows 2000 / XP
// Notify the mount manager of a VFD volume arrival
VfdMountMgrNotifyVolume(device_extension);
if (device_extension->DriveLetter) {
// Create a drive letter via the mount manager.
// The mount manager may have created a drive letter
// in response to the volume arrival notification above.
// In that case, the following call just fails.
VfdMountMgrMountPoint(
device_extension, device_extension->DriveLetter);
// ignoring the error for it is not fatal here
}
}
else
#endif // VFD_MOUNT_MANAGER
{
// Windows NT style drive letter assignment
// Simply create a symbolic link here
if (device_extension->DriveLetter) {
VfdSetLink(
device_extension, device_extension->DriveLetter);
// ignoring the error for it is not fatal here
}
}
device_object = device_object->NextDevice;
}
VFDTRACE(VFDINFO, ("[VFD] VfdReinitialize - OUT\n"));
}
//
// Device dedicated thread routine
// Dispatch read, write and device I/O request
// redirected from the driver dispatch routines
//
VOID
NTAPI
VfdDeviceThread (
IN PVOID ThreadContext)
{
PDEVICE_OBJECT device_object;
PDEVICE_EXTENSION device_extension;
PLIST_ENTRY request;
PIRP irp;
PIO_STACK_LOCATION io_stack;
ASSERT(ThreadContext != NULL);
device_object = (PDEVICE_OBJECT)ThreadContext;
device_extension = (PDEVICE_EXTENSION)device_object->DeviceExtension;
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
for (;;) {
// wait for the request event to be signalled
KeWaitForSingleObject(
&device_extension->RequestEvent,
Executive,
KernelMode,
FALSE,
NULL);
// terminate request ?
if (device_extension->TerminateThread) {
VFDTRACE(0, ("[VFD] Exitting the I/O thread\n"));
PsTerminateSystemThread(STATUS_SUCCESS);
}
// perform requested tasks
while ((request = ExInterlockedRemoveHeadList(
&device_extension->ListHead,
&device_extension->ListLock)) != NULL)
{
irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);
io_stack = IoGetCurrentIrpStackLocation(irp);
irp->IoStatus.Information = 0;
switch (io_stack->MajorFunction) {
case IRP_MJ_READ:
VfdReadData(device_extension, irp,
io_stack->Parameters.Read.Length,
&io_stack->Parameters.Read.ByteOffset);
break;
case IRP_MJ_WRITE:
VfdWriteData(device_extension, irp,
io_stack->Parameters.Write.Length,
&io_stack->Parameters.Write.ByteOffset);
break;
case IRP_MJ_DEVICE_CONTROL:
VfdIoCtlThread(device_extension, irp,
io_stack->Parameters.DeviceIoControl.IoControlCode);
break;
default:
// This shouldn't happen...
VFDTRACE(0,
("[VFD] %s passed to the I/O thread\n",
GetMajorFuncName(io_stack->MajorFunction)));
irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
}
IoCompleteRequest(irp,
(CCHAR)(NT_SUCCESS(irp->IoStatus.Status) ?
IO_DISK_INCREMENT : IO_NO_INCREMENT));
#ifdef VFD_PNP
IoReleaseRemoveLock(&device_extension->RemoveLock, irp);
#endif // VFD_PNP
} // while
} // for (;;)
}
//
// Copy a UNICODE_STRING adding a trailing NULL characer
//
PWSTR VfdCopyUnicode(
PUNICODE_STRING dst,
PUNICODE_STRING src)
{
RtlZeroMemory(dst, sizeof(UNICODE_STRING));
dst->MaximumLength =
(USHORT)(src->MaximumLength + sizeof(UNICODE_NULL));
dst->Buffer = (PWSTR)ExAllocatePoolWithTag(
PagedPool, dst->MaximumLength, VFD_POOL_TAG);
if(dst->Buffer) {
dst->Length = src->Length;
RtlZeroMemory(dst->Buffer, dst->MaximumLength);
if (src->Length) {
RtlCopyMemory(dst->Buffer, src->Buffer, src->Length);
}
}
return dst->Buffer;
}
//
// Free a UNICODE_STRING buffer
//
VOID VfdFreeUnicode(
PUNICODE_STRING str)
{
if (str->Buffer) {
ExFreePool(str->Buffer);
}
RtlZeroMemory(str, sizeof(UNICODE_STRING));
}