mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 14:30:57 +00:00
25c7e1a8d0
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
591 lines
14 KiB
C
591 lines
14 KiB
C
/*
|
|
vfdioctl.c
|
|
|
|
Virtual Floppy Drive for Windows NT platform
|
|
Kernel mode driver: I/O control request handling
|
|
|
|
Copyright (C) 2003-2005 Ken Kato
|
|
*/
|
|
|
|
#include "imports.h"
|
|
#include "vfddrv.h"
|
|
#include "vfddbg.h"
|
|
|
|
/*
|
|
#include <initguid.h>
|
|
DEFINE_GUID(VFD_GUID, 0x4563b3d8L, 0x936a, 0x4692,
|
|
0xb6, 0x0c, 0x16, 0xd3, 0xb2, 0x57, 0xbb, 0xf2);
|
|
*/
|
|
|
|
#define IO_INPUTLEN(p) (p)->Parameters.DeviceIoControl.InputBufferLength
|
|
#define IO_OUTPUTLEN(p) (p)->Parameters.DeviceIoControl.OutputBufferLength
|
|
#define IO_CTRLCODE(p) (p)->Parameters.DeviceIoControl.IoControlCode
|
|
|
|
//
|
|
// IOCTL commands handler
|
|
//
|
|
NTSTATUS
|
|
NTAPI
|
|
VfdDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp)
|
|
{
|
|
PDEVICE_EXTENSION device_extension;
|
|
PIO_STACK_LOCATION io_stack;
|
|
NTSTATUS status;
|
|
|
|
device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
|
io_stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n",
|
|
GetIoControlName(IO_CTRLCODE(io_stack)),
|
|
device_extension->DeviceName.Buffer));
|
|
|
|
#ifdef VFD_PNP
|
|
status = IoAcquireRemoveLock(&device_extension->RemoveLock, Irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
VFDTRACE(0,
|
|
("Acquire RemoveLock failed %s\n", NtStatusToStr(status)));
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
#endif // VFD_PNP
|
|
|
|
/*
|
|
// Check if volume verification is required
|
|
|
|
if ((DeviceObject->Flags & DO_VERIFY_VOLUME) &&
|
|
!(io_stack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
|
|
|
|
VFDTRACE(VFDWARN,
|
|
("[VFD] %-40s - %s\n",
|
|
GetIoControlName(IO_CTRLCODE(io_stack)),
|
|
GetStatusName(STATUS_VERIFY_REQUIRED)));
|
|
|
|
Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_VERIFY_REQUIRED;
|
|
}
|
|
*/
|
|
|
|
switch (IO_CTRLCODE(io_stack)) {
|
|
case IOCTL_VFD_OPEN_IMAGE:
|
|
// Open an image file or create an empty RAM disk.
|
|
// Only a few checks are done here.
|
|
// Actual operation is done in device thread
|
|
|
|
status = VfdOpenCheck(
|
|
device_extension,
|
|
(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer,
|
|
IO_INPUTLEN(io_stack));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
// Pass the task to the device thread
|
|
status = STATUS_PENDING;
|
|
break;
|
|
|
|
case IOCTL_VFD_CLOSE_IMAGE:
|
|
case IOCTL_DISK_EJECT_MEDIA:
|
|
case IOCTL_STORAGE_EJECT_MEDIA:
|
|
// Close the current image file or delete the RAM disk
|
|
// Only status check is done here.
|
|
// Actual operation is done in device thread.
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
|
|
// Pass the task to the device thread
|
|
status = STATUS_PENDING;
|
|
break;
|
|
|
|
case IOCTL_VFD_QUERY_IMAGE:
|
|
// Returns current image file information
|
|
|
|
status = VfdQueryImage(
|
|
device_extension,
|
|
(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer,
|
|
IO_OUTPUTLEN(io_stack),
|
|
&Irp->IoStatus.Information);
|
|
|
|
break;
|
|
|
|
case IOCTL_VFD_SET_LINK:
|
|
// Create / remove a persistent drive letter
|
|
// and store it in the registry
|
|
|
|
if (IO_INPUTLEN(io_stack) < sizeof(CHAR)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
#ifdef VFD_MOUNT_MANAGER
|
|
if (OsMajorVersion >= 5) {
|
|
// Windows 2000/XP
|
|
// Create a drive letter via the mount manager
|
|
|
|
status = VfdMountMgrMountPoint(device_extension,
|
|
*(PCHAR)Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
// The new drive letter will be stored in the device extension
|
|
// and the registry when IOCTL_MOUNTDEV_LINK_CREATED or
|
|
// IOCTL_MOUNTDEV_LINK_DELETED is issued from the mount manager.
|
|
}
|
|
else
|
|
#else // VFD_MOUNT_MANAGER
|
|
{
|
|
// Windows NT style drive letter assignment
|
|
// Simply create a symbolic link and store the new value
|
|
|
|
status = VfdSetLink(device_extension,
|
|
*(PCHAR)Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
// Store the new drive letter into the registry
|
|
status = VfdStoreLink(device_extension);
|
|
}
|
|
}
|
|
#endif // VFD_MOUNT_MANAGER
|
|
break;
|
|
|
|
case IOCTL_VFD_QUERY_LINK:
|
|
// Return the current persistent drive letter
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(CHAR)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
*(PCHAR)Irp->AssociatedIrp.SystemBuffer =
|
|
device_extension->DriveLetter;
|
|
|
|
Irp->IoStatus.Information = sizeof(CHAR);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_SET_PROTECT:
|
|
// Set media protect flag
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
|
|
device_extension->MediaFlags |= VFD_FLAG_WRITE_PROTECTED;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_CLEAR_PROTECT:
|
|
// Clear media protect flag
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
|
|
device_extension->MediaFlags &= ~VFD_FLAG_WRITE_PROTECTED;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_RESET_MODIFY:
|
|
// Reset the data modify flag
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
|
|
device_extension->MediaFlags &= ~VFD_FLAG_DATA_MODIFIED;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_QUERY_NUMBER:
|
|
// Return VFD device number (\??\VirtualFD<n>)
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
*(PULONG)Irp->AssociatedIrp.SystemBuffer=
|
|
device_extension->DeviceNumber;
|
|
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_QUERY_NAME:
|
|
// Return VFD device name (\Device\Floppy<n>)
|
|
// counted unicode string (not null terminated)
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(USHORT)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
{
|
|
PUSHORT p = (PUSHORT)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
*p = device_extension->DeviceName.Length;
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(USHORT) + *p) {
|
|
|
|
Irp->IoStatus.Information = sizeof(USHORT);
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory(p + 1, device_extension->DeviceName.Buffer, *p);
|
|
|
|
Irp->IoStatus.Information = sizeof(USHORT) + *p;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_VFD_QUERY_VERSION:
|
|
// Return the VFD driver version
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
*(PULONG)Irp->AssociatedIrp.SystemBuffer =
|
|
(VFD_DRIVER_MAJOR << 16) | VFD_DRIVER_MINOR | VFD_DEBUG_FLAG;
|
|
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
//
|
|
// standard disk and storage I/O control requests
|
|
//
|
|
|
|
case IOCTL_DISK_CHECK_VERIFY:
|
|
case IOCTL_STORAGE_CHECK_VERIFY:
|
|
case IOCTL_STORAGE_CHECK_VERIFY2:
|
|
|
|
if (IO_OUTPUTLEN(io_stack) >= sizeof(ULONG)) {
|
|
|
|
*(PULONG)Irp->AssociatedIrp.SystemBuffer =
|
|
device_extension->MediaChangeCount;
|
|
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_DISK_FORMAT_TRACKS:
|
|
case IOCTL_DISK_FORMAT_TRACKS_EX:
|
|
// Only parameter checks are performed here
|
|
// Actual operation is done by the device thread
|
|
|
|
status = VfdFormatCheck(
|
|
device_extension,
|
|
(PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer,
|
|
IO_INPUTLEN(io_stack),
|
|
IO_CTRLCODE(io_stack));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
// Pass the task to the device thread
|
|
status = STATUS_PENDING;
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
|
// Returns the geometry of current media
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case IOCTL_DISK_GET_MEDIA_TYPES:
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES:
|
|
// Return *the last mounted* disk geometry, although xxx_GET_MEDIA_TYPES
|
|
// commands are supposed to return all supported media types.
|
|
// This makes the matter much simpler...;-)
|
|
// If no image has been mounted yet, 1.44MB media is assumed.
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(DISK_GEOMETRY)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Copy appropriate DISK_GEOMETRY into the output buffer
|
|
|
|
if (device_extension->Geometry) {
|
|
RtlCopyMemory(
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
device_extension->Geometry,
|
|
sizeof(DISK_GEOMETRY));
|
|
}
|
|
else {
|
|
// default = 3.5" 1.44 MB media
|
|
RtlCopyMemory(
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
&geom_tbl[VFD_MEDIA_F3_1P4],
|
|
sizeof(DISK_GEOMETRY));
|
|
}
|
|
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_LENGTH_INFO:
|
|
// Return disk length information
|
|
// (Windows XP requires this request to be handled)
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(GET_LENGTH_INFORMATION)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
((PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->Length.QuadPart =
|
|
VFD_SECTOR_TO_BYTE(device_extension->Sectors);
|
|
|
|
Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_DISK_IS_WRITABLE:
|
|
// Checks if current media is writable
|
|
|
|
if (!device_extension->FileHandle &&
|
|
!device_extension->FileBuffer) {
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
}
|
|
else if (device_extension->MediaFlags & VFD_FLAG_WRITE_PROTECTED) {
|
|
status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
}
|
|
else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
case IOCTL_DISK_MEDIA_REMOVAL:
|
|
case IOCTL_STORAGE_MEDIA_REMOVAL:
|
|
// Since removal lock is irrelevant for virtual disks,
|
|
// there's really nothing to do here...
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_STORAGE_GET_HOTPLUG_INFO:
|
|
{
|
|
PSTORAGE_HOTPLUG_INFO hotplug;
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(STORAGE_HOTPLUG_INFO)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
hotplug = (PSTORAGE_HOTPLUG_INFO)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
RtlZeroMemory(hotplug, sizeof(STORAGE_HOTPLUG_INFO));
|
|
|
|
hotplug->Size = sizeof(STORAGE_HOTPLUG_INFO);
|
|
hotplug->MediaRemovable = 1;
|
|
|
|
Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
*/
|
|
|
|
#ifdef VFD_MOUNT_MANAGER
|
|
//
|
|
// IO control requests received from the mount manager
|
|
// (on Windows 2000 / XP)
|
|
//
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
|
|
// Returns a unique ID for the target device
|
|
status = VfdMountDevUniqueId(
|
|
device_extension,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IO_OUTPUTLEN(io_stack),
|
|
&Irp->IoStatus);
|
|
break;
|
|
|
|
// case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY:
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
|
|
// Returns the device name of the target device
|
|
status = VfdMountDevDeviceName(
|
|
device_extension,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IO_OUTPUTLEN(io_stack),
|
|
&Irp->IoStatus);
|
|
break;
|
|
|
|
case 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.
|
|
status = VfdMountDevSuggestedLink(
|
|
device_extension,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IO_OUTPUTLEN(io_stack),
|
|
&Irp->IoStatus);
|
|
break;
|
|
|
|
case IOCTL_MOUNTDEV_LINK_CREATED:
|
|
case IOCTL_MOUNTDEV_LINK_DELETED:
|
|
// Issued after the mount manager created/deleted a symbolic link
|
|
status = VfdMountDevLinkModified(
|
|
device_extension,
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
IO_INPUTLEN(io_stack),
|
|
IO_CTRLCODE(io_stack));
|
|
break;
|
|
|
|
/*
|
|
case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
|
|
{
|
|
PMOUNTDEV_STABLE_GUID guid;
|
|
|
|
if (IO_OUTPUTLEN(io_stack) < sizeof(MOUNTDEV_STABLE_GUID)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
guid = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
RtlCopyMemory(
|
|
&guid->StableGuid, &VFD_GUID, sizeof(GUID));
|
|
|
|
Irp->IoStatus.Information = sizeof(guid);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
*/
|
|
#endif // VFD_MOUNT_MANAGER
|
|
|
|
default:
|
|
// Unknown IOCTL request
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
#if DBG
|
|
if ((NT_SUCCESS(status) && (TraceFlags & VFDINFO) == VFDINFO) ||
|
|
(TraceFlags & VFDWARN) == VFDWARN) {
|
|
VFDTRACE(0,("[VFD] %-40s - %s\n",
|
|
GetIoControlName(IO_CTRLCODE(io_stack)),
|
|
GetStatusName(status)));
|
|
}
|
|
#endif
|
|
|
|
if (status == STATUS_PENDING) {
|
|
// Let the device thread perform the operation
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
ExInterlockedInsertTailList(
|
|
&device_extension->ListHead,
|
|
&Irp->Tail.Overlay.ListEntry,
|
|
&device_extension->ListLock);
|
|
|
|
KeSetEvent(
|
|
&device_extension->RequestEvent,
|
|
(KPRIORITY) 0,
|
|
FALSE);
|
|
}
|
|
else {
|
|
// complete the operation
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
#ifdef VFD_PNP
|
|
IoReleaseRemoveLock(&device_extension->RemoveLock, Irp);
|
|
#endif // VFD_PNP
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Handle IO control requests in the device thread
|
|
//
|
|
VOID
|
|
VfdIoCtlThread(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PIRP Irp,
|
|
IN ULONG ControlCode)
|
|
{
|
|
switch (ControlCode) {
|
|
case IOCTL_VFD_OPEN_IMAGE:
|
|
// open the file from the caller's security context
|
|
// -- this allows this driver to open network files
|
|
if (DeviceExtension->SecurityContext) {
|
|
SeImpersonateClient(DeviceExtension->SecurityContext, NULL);
|
|
}
|
|
|
|
Irp->IoStatus.Status = VfdOpenImage(DeviceExtension,
|
|
(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
PsRevertToSelf();
|
|
break;
|
|
|
|
case IOCTL_VFD_CLOSE_IMAGE:
|
|
case IOCTL_DISK_EJECT_MEDIA:
|
|
case IOCTL_STORAGE_EJECT_MEDIA:
|
|
VfdCloseImage(DeviceExtension);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case IOCTL_DISK_FORMAT_TRACKS:
|
|
case IOCTL_DISK_FORMAT_TRACKS_EX:
|
|
Irp->IoStatus.Status = VfdFormatTrack(DeviceExtension,
|
|
(PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer);
|
|
break;
|
|
|
|
default:
|
|
// This shouldn't happen...
|
|
VFDTRACE(0,
|
|
("[VFD] %s passed to the device thread\n",
|
|
GetIoControlName(ControlCode)));
|
|
|
|
Irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
|
|
}
|
|
|
|
#if DBG
|
|
if ((NT_SUCCESS(Irp->IoStatus.Status) && (TraceFlags & VFDINFO) == VFDINFO) ||
|
|
(TraceFlags & VFDWARN) == VFDWARN) {
|
|
VFDTRACE(0,("[VFD] %-40s - %s\n",
|
|
GetIoControlName(ControlCode),
|
|
GetStatusName(Irp->IoStatus.Status)));
|
|
}
|
|
#endif
|
|
}
|