reactos/modules/rosapps/drivers/vfd/vfdmnt.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

666 lines
16 KiB
C

/*
vfdmnt.c
Virtual Floppy Drive for Windows NT platform
Kernel mode driver mount manager functions
Copyright (C) 2003-2005 Ken Kato
*/
#ifndef VFD_MOUNT_MANAGER
/*
Not in working order for the time being
so DO NOT define VFD_MOUNT_MANAGER macro
unless you know exactly what you are doing...
*/
#ifdef _MSC_VER
// suppress empty compile unit warning
#pragma warning (disable: 4206)
#pragma message ("Mount Manager support feature is disabled.")
#endif
#else // VFD_MOUNT_MANAGER
/*
The flow of the drive letter assignment via the Mount Manager
during the VFD driver start up
1) IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION VFD -> MM
notifies the mount manager of VFD devices.
2) IOCTL_MOUNTDEV_QUERY_DEVICE_NAME VFD <- MM
device name (\Device\Floppy<x>) VFD -> MM
3) IOCTL_MOUNTDEV_QUERY_UNIQUE_ID VFD <- MM
device unique ID (\??\VirtualFD<x>) VFD -> MM
4) IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME VFD <- MM
drive letter link (\DosDevices\<x>:) VFD -> MM
5) The mount manager creates the drive letter link
6) IOCTL_MOUNTDEV_LINK_CREATED VFD <- MM
The driver stores the created drive letter
The flow of the drive letter operation with IOCTL_VFD_SET_LINK
1) IOCTL_MOUNTMGR_CREATE_POINT or
IOCTL_MOUNTMGR_DELETE_POINTS VFD -> MM
2) The mount manager creates/deletes the drive letter link
3) IOCTL_MOUNTDEV_LINK_CREATED or
IOCTL_MOUNTDEV_LINK_DELETED VFD <- MM
The driver stores the created/deleted drive letter
*/
#include "imports.h"
#include "vfddrv.h"
#include "vfddbg.h"
//
// Call the mount manager with an IO control IRP
//
static NTSTATUS
VfdMountMgrSendRequest(
ULONG ControlCode,
PVOID InputBuffer,
ULONG InputLength,
PVOID OutputBuffer,
ULONG OutputLength);
#ifdef ALLOC_PRAGMA
//#pragma alloc_text(PAGE, VfdRegisterMountManager)
#pragma alloc_text(PAGE, VfdMountMgrNotifyVolume)
#pragma alloc_text(PAGE, VfdMountMgrMountPoint)
#pragma alloc_text(PAGE, VfdMountMgrSendRequest)
#pragma alloc_text(PAGE, VfdMountDevUniqueId)
#pragma alloc_text(PAGE, VfdMountDevDeviceName)
#pragma alloc_text(PAGE, VfdMountDevSuggestedLink)
#pragma alloc_text(PAGE, VfdMountDevLinkModified)
#endif // ALLOC_PRAGMA
/*
#include <initguid.h>
#include <mountmgr.h>
//
// register a device to the mount manager interface
// does not work...
//
NTSTATUS
VfdRegisterMountManager(
PDEVICE_EXTENSION DeviceExtension)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING interface;
UNICODE_STRING interface2;
VFDTRACE(VFDINFO,
("[VFD] Registering %ws to the Mount Manager Interface\n",
DeviceExtension->DeviceName.Buffer));
RtlInitUnicodeString(&interface, NULL);
status = IoRegisterDeviceInterface(
DeviceExtension->DeviceObject,
(LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID,
NULL,
&interface);
if (!NT_SUCCESS(status)) {
VFDTRACE(0,
("[VFD] IoRegisterDeviceInterface - %s\n",
GetStatusName(status)));
return status;
}
status = IoSetDeviceInterfaceState(&interface, TRUE);
if (NT_SUCCESS(status)) {
if (VfdCopyUnicode(&interface2, &interface)) {
VFDTRACE(VFDINFO,
("[VFD] Interface: %ws\n", interface2.Buffer));
}
else {
VFDTRACE(0,
("[VFD] Failed to allocate an interface name buffer\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
else {
VFDTRACE(0,
("[VFD] IoSetDeviceInterfaceState - %s\n",
GetStatusName(status)));
}
RtlFreeUnicodeString(&interface);
VfdFreeUnicode(&interface2);
return status;
}
*/
//
// informs the Mount Manager of a new VFD device
//
NTSTATUS
VfdMountMgrNotifyVolume(
PDEVICE_EXTENSION DeviceExtension)
{
PMOUNTMGR_TARGET_NAME target_name;
USHORT target_name_buf[MAXIMUM_FILENAME_LENGTH];
NTSTATUS status;
VFDTRACE(VFDINFO,
("[VFD] VfdMountMgrNotifyVolume - %ws\n",
DeviceExtension->DeviceName.Buffer));
target_name = (PMOUNTMGR_TARGET_NAME)target_name_buf;
target_name->DeviceNameLength =
DeviceExtension->DeviceName.Length;
RtlCopyMemory(
target_name->DeviceName,
DeviceExtension->DeviceName.Buffer,
DeviceExtension->DeviceName.Length);
status = VfdMountMgrSendRequest(
IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
target_name,
sizeof(target_name->DeviceNameLength) + target_name->DeviceNameLength,
NULL,
0);
VFDTRACE(VFDINFO,
("[VFD] VfdMountMgrNotifyVolume - %s\n",
GetStatusName(status)));
return status;
}
//
// Create / remove a drive letter via the Mount Manager
//
NTSTATUS
VfdMountMgrMountPoint(
PDEVICE_EXTENSION DeviceExtension,
CHAR DriveLetter)
{
ULONG alloc_size;
UNICODE_STRING link_name;
WCHAR link_buf[20];
NTSTATUS status;
VFDTRACE(VFDINFO, ("[VFD] VfdMountMgrMountPoint - IN\n"));
// convert lower case into upper case
if (DriveLetter >= 'a' && DriveLetter <= 'z') {
DriveLetter -= ('a' - 'A');
}
if (DriveLetter >= 'A' && DriveLetter <= 'Z') {
// Create a new drive letter
PMOUNTMGR_CREATE_POINT_INPUT create;
swprintf(link_buf, L"\\DosDevices\\%wc:", DriveLetter);
RtlInitUnicodeString(&link_name, link_buf);
VFDTRACE(VFDINFO,
("[VFD] Creating a link: %ws => %ws\n",
link_buf, DeviceExtension->DeviceName.Buffer));
// allocate buffer for MOUNTMGR_CREATE_POINT_INPUT
alloc_size = sizeof(MOUNTMGR_CREATE_POINT_INPUT) +
link_name.Length + DeviceExtension->DeviceName.Length;
create = (PMOUNTMGR_CREATE_POINT_INPUT)ExAllocatePoolWithTag(
NonPagedPool, alloc_size, VFD_POOL_TAG);
if (!create) {
VFDTRACE(0, ("[VFD] Failed to allocate mount point input\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// set the symbolic link name
create->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
create->SymbolicLinkNameLength = link_name.Length;
RtlCopyMemory(
(PCHAR)create + create->SymbolicLinkNameOffset,
link_name.Buffer,
link_name.Length);
// set the target device name
create->DeviceNameOffset = (USHORT)
(create->SymbolicLinkNameOffset + create->SymbolicLinkNameLength);
create->DeviceNameLength = DeviceExtension->DeviceName.Length;
RtlCopyMemory(
(PCHAR)create + create->DeviceNameOffset,
DeviceExtension->DeviceName.Buffer,
DeviceExtension->DeviceName.Length);
// call the mount manager with the IO control request
status = VfdMountMgrSendRequest(
IOCTL_MOUNTMGR_CREATE_POINT,
create, alloc_size, NULL, 0);
ExFreePool(create);
// no need to set the new drive letter into the
// DeviceExtension because the mount manager will issue an
// IOCTL_MOUNTDEV_LINK_CREATED and it will be processed then
}
else if (DriveLetter == 0) {
// Delete the existing drive letter
PMOUNTMGR_MOUNT_POINT mount;
PMOUNTMGR_MOUNT_POINTS points;
UNICODE_STRING unique_id;
WCHAR unique_buf[20];
swprintf(link_buf, L"\\DosDevices\\%wc:",
DeviceExtension->DriveLetter);
VFDTRACE(VFDINFO,
("[VFD] Deleting link: %ws\n", link_buf));
RtlInitUnicodeString(&link_name, link_buf);
swprintf(unique_buf, L"\\??\\" VFD_DEVICE_BASENAME L"%lu",
DeviceExtension->DeviceNumber);
RtlInitUnicodeString(&unique_id, unique_buf);
// allocate buffer for MOUNTMGR_MOUNT_POINT
alloc_size = sizeof(MOUNTMGR_MOUNT_POINT) +
link_name.Length +
unique_id.Length +
DeviceExtension->DeviceName.Length;
mount = (PMOUNTMGR_MOUNT_POINT)ExAllocatePoolWithTag(
NonPagedPool, alloc_size, VFD_POOL_TAG);
if (!mount) {
VFDTRACE(0, ("[VFD] Failed to allocate mount point input\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(mount, alloc_size + sizeof(WCHAR));
// set the symbolic link name
mount->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
mount->SymbolicLinkNameLength = link_name.Length;
RtlCopyMemory(
(PCHAR)mount + mount->SymbolicLinkNameOffset,
link_name.Buffer, link_name.Length);
// set the unique id
mount->UniqueIdOffset =
mount->SymbolicLinkNameOffset +
mount->SymbolicLinkNameLength;
mount->UniqueIdLength = unique_id.Length;
RtlCopyMemory(
(PCHAR)mount + mount->UniqueIdOffset,
unique_id.Buffer, unique_id.Length);
// set the target device name
mount->DeviceNameOffset =
mount->UniqueIdOffset +
mount->UniqueIdLength;
mount->DeviceNameLength =
DeviceExtension->DeviceName.Length;
RtlCopyMemory(
(PCHAR)mount + mount->DeviceNameOffset,
DeviceExtension->DeviceName.Buffer,
DeviceExtension->DeviceName.Length);
// prepare the output buffer
points = (PMOUNTMGR_MOUNT_POINTS)ExAllocatePoolWithTag(
NonPagedPool, alloc_size * 2, VFD_POOL_TAG);
status = VfdMountMgrSendRequest(
IOCTL_MOUNTMGR_DELETE_POINTS,
mount, alloc_size, points, alloc_size * 2);
ExFreePool(mount);
ExFreePool(points);
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
// the drive letter did not exist in the first place
DeviceExtension->DriveLetter = 0;
}
// no need to clear the drive letter in the
// DeviceExtension because the mount manager will issue an
// IOCTL_MOUNTDEV_LINK_DELETED and it will be processed then
}
else {
return STATUS_INVALID_PARAMETER;
}
VFDTRACE(VFDINFO, ("[VFD] VfdMountMgrMountPoint - %s\n",
GetStatusName(status)));
return status;
}
//
// send a request to the Mount Manager
//
NTSTATUS
VfdMountMgrSendRequest(
ULONG ControlCode,
PVOID InputBuffer,
ULONG InputLength,
PVOID OutputBuffer,
ULONG OutputLength)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING mntmgr_name;
PDEVICE_OBJECT mntmgr_dev;
PFILE_OBJECT mntmgr_file;
IO_STATUS_BLOCK io_status;
KEVENT event;
PIRP irp;
// Obtain a pointer to the Mount Manager device object
RtlInitUnicodeString(
&mntmgr_name,
MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(
&mntmgr_name,
FILE_READ_ATTRIBUTES,
&mntmgr_file,
&mntmgr_dev);
if (!NT_SUCCESS(status)) {
VFDTRACE(VFDWARN,
("[VFD] IoGetDeviceObjectPointer - %s\n", GetStatusName(status)));
return status;
}
KeInitializeEvent(&event, NotificationEvent, FALSE);
// Create an IRP request block
irp = IoBuildDeviceIoControlRequest(
ControlCode,
mntmgr_dev,
InputBuffer,
InputLength,
OutputBuffer,
OutputLength,
FALSE,
&event,
&io_status);
if (!irp) {
VFDTRACE(VFDWARN,
("[VFD] IoBuildDeviceIoControlRequest\n"));
ObDereferenceObject(mntmgr_file);
return STATUS_DRIVER_INTERNAL_ERROR;
}
// Call the mount manager
status = IoCallDriver(mntmgr_dev, irp);
if (!NT_SUCCESS(status)) {
VFDTRACE(VFDWARN,
("[VFD] IoCallDriver - %s\n", GetStatusName(status)));
}
if (status == STATUS_PENDING) {
// Wait for the operation to complete
KeWaitForSingleObject(
&event, Executive, KernelMode, FALSE, NULL);
status = io_status.Status;
if (!NT_SUCCESS(status)) {
VFDTRACE(VFDWARN,
("[VFD] IoCallDriver - %s\n", GetStatusName(status)));
}
}
ObDereferenceObject(mntmgr_file);
return status;
}
//
// IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
// -- use the device interface link (\??\VirtualFD<x>) as the unique ID
//
NTSTATUS
VfdMountDevUniqueId(
PDEVICE_EXTENSION DeviceExtension,
PMOUNTDEV_UNIQUE_ID UniqueId,
ULONG OutputLength,
PIO_STATUS_BLOCK IoStatus)
{
WCHAR buf[20];
UNICODE_STRING unicode;
if (OutputLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
return STATUS_INVALID_PARAMETER;
}
swprintf(buf,
L"\\??\\" VFD_DEVICE_BASENAME L"%lu",
DeviceExtension->DeviceNumber);
RtlInitUnicodeString(&unicode, buf);
UniqueId->UniqueIdLength = unicode.Length;
if (OutputLength <
sizeof(UniqueId->UniqueIdLength) + UniqueId->UniqueIdLength) {
IoStatus->Information = sizeof(MOUNTDEV_UNIQUE_ID);
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(
UniqueId->UniqueId, buf, unicode.Length);
IoStatus->Information =
sizeof(UniqueId->UniqueIdLength) + UniqueId->UniqueIdLength;
return STATUS_SUCCESS;
}
//
// IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
// Returns the device name of the target device (\Device\Floppy<n>)
//
NTSTATUS
VfdMountDevDeviceName(
PDEVICE_EXTENSION DeviceExtension,
PMOUNTDEV_NAME DeviceName,
ULONG OutputLength,
PIO_STATUS_BLOCK IoStatus)
{
if (OutputLength < sizeof(MOUNTDEV_NAME)) {
return STATUS_INVALID_PARAMETER;
}
DeviceName->NameLength = DeviceExtension->DeviceName.Length;
if (OutputLength <
sizeof(DeviceName->NameLength) + DeviceName->NameLength) {
IoStatus->Information = sizeof(MOUNTDEV_NAME);
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(
DeviceName->Name,
DeviceExtension->DeviceName.Buffer,
DeviceName->NameLength);
IoStatus->Information =
sizeof(DeviceName->NameLength) + DeviceName->NameLength;
return STATUS_SUCCESS;
}
//
// IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
// Returns the drive letter link which we want the mount manager
// to create. This request is issued in response to the volume
// arrival notification, and the mount manager will create the
// symbolic link.
//
NTSTATUS
VfdMountDevSuggestedLink(
PDEVICE_EXTENSION DeviceExtension,
PMOUNTDEV_SUGGESTED_LINK_NAME LinkName,
ULONG OutputLength,
PIO_STATUS_BLOCK IoStatus)
{
WCHAR buf[20];
UNICODE_STRING unicode;
if (OutputLength < sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
return STATUS_INVALID_PARAMETER;
}
LinkName->UseOnlyIfThereAreNoOtherLinks = TRUE;
if (!DeviceExtension->DriveLetter) {
// No persistent drive letter stored in the registry
VFDTRACE(VFDINFO, ("[VFD] suggested link : none\n"));
LinkName->NameLength = 0;
IoStatus->Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
return STATUS_SUCCESS;
}
// A persistent drive letter exists
swprintf(buf, L"\\DosDevices\\%wc:",
DeviceExtension->DriveLetter);
VFDTRACE(VFDINFO, ("[VFD] suggested link : %ws\n", buf));
RtlInitUnicodeString(&unicode, buf);
LinkName->NameLength = unicode.Length;
if (OutputLength <
sizeof(MOUNTDEV_SUGGESTED_LINK_NAME) +
LinkName->NameLength - sizeof(WCHAR)) {
IoStatus->Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(LinkName->Name, buf, unicode.Length);
IoStatus->Information =
sizeof(MOUNTDEV_SUGGESTED_LINK_NAME) +
LinkName->NameLength - sizeof(WCHAR);
return STATUS_SUCCESS;
}
//
// IOCTL_MOUNTDEV_LINK_CREATED / IOCTL_MOUNTDEV_LINK_DELETED
// Issued after the mount manager created/deleted a symbolic link
// If the link is a drive letter, store the new value into the
// registry as the new drive letter
//
NTSTATUS
VfdMountDevLinkModified(
PDEVICE_EXTENSION DeviceExtension,
PMOUNTDEV_NAME LinkName,
ULONG InputLength,
ULONG ControlCode)
{
if (InputLength < sizeof(MOUNTDEV_NAME)) {
return STATUS_INVALID_PARAMETER;
}
if (InputLength < sizeof(MOUNTDEV_NAME) +
LinkName->NameLength - sizeof(WCHAR)) {
return STATUS_INVALID_PARAMETER;
}
#if DBG
{ // Print the reported link name
PWSTR buf = ExAllocatePoolWithTag(
PagedPool, LinkName->NameLength + sizeof(WCHAR), VFD_POOL_TAG);
if (buf) {
RtlZeroMemory(buf, LinkName->NameLength + sizeof(WCHAR));
RtlCopyMemory(buf, LinkName->Name, LinkName->NameLength);
VFDTRACE(VFDINFO, ("[VFD] %ws\n", buf));
ExFreePool(buf);
}
}
#endif // DBG
if (LinkName->NameLength == 28 &&
LinkName->Name[0] == L'\\' &&
LinkName->Name[1] == L'D' &&
LinkName->Name[2] == L'o' &&
LinkName->Name[3] == L's' &&
LinkName->Name[4] == L'D' &&
LinkName->Name[5] == L'e' &&
LinkName->Name[6] == L'v' &&
LinkName->Name[7] == L'i' &&
LinkName->Name[8] == L'c' &&
LinkName->Name[9] == L'e' &&
LinkName->Name[10] == L's' &&
LinkName->Name[11] == L'\\' &&
LinkName->Name[12] >= L'A' &&
LinkName->Name[12] <= L'Z' &&
LinkName->Name[13] == L':') {
// The link is a drive letter
if (ControlCode == IOCTL_MOUNTDEV_LINK_CREATED) {
// link is created - store the new drive letter
DeviceExtension->DriveLetter = (CHAR)LinkName->Name[12];
}
else {
// link is deleted - clear the drive letter
DeviceExtension->DriveLetter = 0;
}
// Store the value into the registry
VfdStoreLink(DeviceExtension);
}
return STATUS_SUCCESS;
}
#endif // VFD_MOUNT_MANAGER