windows-nt/Source/XPSP1/NT/drivers/storage/ramdisk/pnp.c
2020-09-26 16:20:57 +08:00

2888 lines
71 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
pnp.c
Abstract:
This file contains RAM disk driver code for processing PnP IRPs.
Author:
Chuck Lenzmeier (ChuckL) 2001
Environment:
Kernel mode only.
Notes:
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Registry value format for virtual floppy disks created by textmode setup.
// Should be in a header file, but that's not the way it was done for the
// original virtual floppy driver.
//
typedef struct _VIRTUAL_FLOPPY_DESCRIPTOR {
//
// The structure starts with a system virtual address. On 32-bit systems,
// this is padded to 64 bits.
//
union {
PVOID VirtualAddress;
ULONGLONG Reserved; // align to 64 bits
} ;
//
// The length of the virtual floppy comes next.
//
ULONG Length;
//
// Textmode writes the registry value with 12 bytes of data. In order
// to get the right size for our check, we use of the field offset of
// the following field. We can't use sizeof a struct that just has the
// above fields, because that comes out as 16 bytes due to alignment.
//
ULONG StructSizer;
} VIRTUAL_FLOPPY_DESCRIPTOR, *PVIRTUAL_FLOPPY_DESCRIPTOR;
#if !DBG
#define PRINT_CODE( _code )
#else
#define PRINT_CODE( _code ) \
if ( print ) { \
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", " " #_code "\n") ); \
} \
print = FALSE;
#endif
#if DBG
PSTR StateTable[] = {
"STOPPED",
"WORKING",
"PENDINGSTOP",
"PENDINGREMOVE",
"SURPRISEREMOVED",
"REMOVED",
"UNKNOWN"
};
#endif // DBG
//
// Local functions.
//
NTSTATUS
RamdiskDeleteDiskDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp OPTIONAL
);
NTSTATUS
RamdiskIoCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
);
NTSTATUS
RamdiskQueryBusInformation (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
RamdiskQueryCapabilities (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
RamdiskQueryId (
IN PDISK_EXTENSION DiskExtension,
IN PIRP Irp
);
NTSTATUS
RamdiskQueryDeviceRelations (
IN DEVICE_RELATION_TYPE RelationsType,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
RamdiskQueryDeviceText (
IN PDISK_EXTENSION DiskExtension,
IN PIRP Irp
);
NTSTATUS
RamdiskRemoveBusDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
#if DBG
PSTR
GetPnpIrpName (
IN UCHAR PnpMinorFunction
);
PCHAR
GetDeviceRelationString (
IN DEVICE_RELATION_TYPE Type
);
#endif // DBG
//
// Declare pageable routines.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, RamdiskPnp )
#pragma alloc_text( PAGE, RamdiskPower )
#pragma alloc_text( PAGE, RamdiskAddDevice )
#pragma alloc_text( PAGE, CreateRegistryDisks )
#pragma alloc_text( PAGE, RamdiskDeleteDiskDevice )
#pragma alloc_text( PAGE, RamdiskQueryBusInformation )
#pragma alloc_text( PAGE, RamdiskQueryCapabilities )
#pragma alloc_text( PAGE, RamdiskQueryId )
#pragma alloc_text( PAGE, RamdiskQueryDeviceRelations )
#pragma alloc_text( PAGE, RamdiskQueryDeviceText )
#pragma alloc_text( PAGE, RamdiskRemoveBusDevice )
#if DBG
#pragma alloc_text( PAGE, GetPnpIrpName )
#pragma alloc_text( PAGE, GetDeviceRelationString )
#endif // DBG
#endif // ALLOC_PRAGMA
NTSTATUS
RamdiskPnp (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to perform a PnP function.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
I/O is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PCOMMON_EXTENSION commonExtension;
PBUS_EXTENSION busExtension;
PDISK_EXTENSION diskExtension;
KEVENT event;
BOOLEAN lockHeld = FALSE;
#if DBG
BOOLEAN print = TRUE;
#endif
PAGED_CODE();
//
// Get pointers the IRP stack location and the device extension.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
commonExtension = DeviceObject->DeviceExtension;
busExtension = DeviceObject->DeviceExtension;
diskExtension = DeviceObject->DeviceExtension;
ASSERT( commonExtension->DeviceState < RamdiskDeviceStateMaximum );
DBGPRINT( DBG_PNP, DBG_INFO, ("RamdiskPnp: DO=(%p [Type=%d]) Irp=(%p %s) Device State=%s\n",
DeviceObject, commonExtension->DeviceType, Irp,
GetPnpIrpName(irpSp->MinorFunction), StateTable[commonExtension->DeviceState]) );
//
// If the device has been removed, only pass IRP_REMOVE down for cleanup.
//
if ( (commonExtension->DeviceState >= RamdiskDeviceStateRemoved) &&
(irpSp->MinorFunction != IRP_MN_REMOVE_DEVICE) ) {
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("RamdiskPnp: rejecting IRP %d for Removed Device\n",
irpSp->MinorFunction) );
status = STATUS_NO_SUCH_DEVICE;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
return status;
}
//
// Acquire the remove lock. If this fails, fail the I/O.
//
status = IoAcquireRemoveLock( &commonExtension->RemoveLock, Irp );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR, ("RamdiskPnp: IoAcquireRemoveLock failed: %x\n", status) );
COMPLETE_REQUEST( status, 0, Irp );
return status;
}
//
// Indicate that the remove lock is held.
//
lockHeld = TRUE;
//
// Dispatch based on the minor function.
//
switch ( irpSp->MinorFunction ) {
case IRP_MN_START_DEVICE:
PRINT_CODE( IRP_MN_START_DEVICE );
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
//
// Starting the bus device.
//
// Send the IRP down and wait for it to come back.
//
IoCopyCurrentIrpStackLocationToNext( Irp );
KeInitializeEvent( &event, NotificationEvent, FALSE );
IoSetCompletionRoutine(
Irp,
RamdiskIoCompletionRoutine,
&event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver( commonExtension->LowerDeviceObject, Irp );
if ( status == STATUS_PENDING ) {
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
status = Irp->IoStatus.Status;
}
if ( NT_SUCCESS(status) ) {
//
// Lower drivers didn't fail the IRP. Start the interface.
//
status = IoSetDeviceInterfaceState( &commonExtension->InterfaceString, TRUE );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("IoSetDeviceInterfaceState FAILED Status = 0x%x\n", status) );
}
//
// Device started successfully.
//
commonExtension->DeviceState = RamdiskDeviceStateWorking;
}
} else {
//
// Starting a RAM disk.
//
// Register the device interface. If it's an emulated disk, use the
// private RAM disk interface GUID. If it's an emulated volume, use
// the systemwide volume GUID.
//
if ( commonExtension->InterfaceString.Buffer != NULL ) {
FREE_POOL( commonExtension->InterfaceString.Buffer, FALSE );
}
status = IoRegisterDeviceInterface(
DeviceObject,
diskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK ?
&RamdiskDiskInterface :
&VolumeClassGuid,
NULL,
&commonExtension->InterfaceString
);
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("IoRegisterDeviceInterface FAILED Status = 0x%x\n", status) );
}
//
// Start the interface.
//
if ( !diskExtension->Options.Hidden &&
(commonExtension->InterfaceString.Buffer != NULL) ) {
ULONG installState;
ULONG resultLength;
status = IoGetDeviceProperty(
DeviceObject,
DevicePropertyInstallState,
sizeof(installState),
&installState,
&resultLength
);
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("IoGetDeviceProperty FAILED Status = 0x%x\n", status) );
//
// If we can't get the install state, we set the interface
// state to TRUE anyway, just to be safe.
//
installState = InstallStateInstalled;
}
if ( installState == InstallStateInstalled ) {
DBGPRINT( DBG_PNP, DBG_INFO,
("%s", "Calling IoSetDeviceInterfaceState(TRUE)\n") );
status = IoSetDeviceInterfaceState( &commonExtension->InterfaceString, TRUE );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("IoSetDeviceInterfaceState FAILED Status = 0x%x\n", status) );
}
} else {
DBGPRINT( DBG_PNP, DBG_INFO,
("Skipping IoSetDeviceInterfaceState; state = 0x%x\n", installState) );
}
}
//
// Device started successfully.
//
commonExtension->DeviceState = RamdiskDeviceStateWorking;
}
//
// Complete the I/O request.
//
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
break;
case IRP_MN_QUERY_STOP_DEVICE:
PRINT_CODE( IRP_MN_QUERY_STOP_DEVICE );
//
// Mark that a stop is pending.
//
commonExtension->DeviceState = RamdiskDeviceStatePendingStop;
//
// Indicate success. Send the IRP on down the stack.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
case IRP_MN_CANCEL_STOP_DEVICE:
PRINT_CODE( IRP_MN_CANCEL_STOP_DEVICE );
//
// Before sending the IRP down make sure we have received
// a IRP_MN_QUERY_STOP_DEVICE. We may get Cancel Stop
// without receiving a Query Stop earlier, if the
// driver on top fails a Query Stop and passes down the
// Cancel Stop.
//
if ( commonExtension->DeviceState == RamdiskDeviceStatePendingStop ) {
//
// Mark that the device is back in the working state, and
// pass the IRP down.
//
commonExtension->DeviceState = RamdiskDeviceStateWorking;
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
} else {
//
// A spurious Cancel Stop request. Just complete it.
//
status = STATUS_SUCCESS;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
break;
case IRP_MN_STOP_DEVICE:
PRINT_CODE( IRP_MN_STOP_DEVICE );
//
// Mark that the device is now stopped. Send the IRP on down the stack.
//
commonExtension->DeviceState = RamdiskDeviceStateStopped;
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
case IRP_MN_QUERY_REMOVE_DEVICE:
PRINT_CODE( IRP_MN_QUERY_REMOVE_DEVICE );
//
// Mark that the device is pending removal. Send the IRP on down the
// stack.
//
commonExtension->DeviceState = RamdiskDeviceStatePendingRemove;
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
case IRP_MN_CANCEL_REMOVE_DEVICE:
PRINT_CODE( IRP_MN_CANCEL_REMOVE_DEVICE );
//
// Before sending the IRP down make sure we have received
// a IRP_MN_QUERY_REMOVE_DEVICE. We may get Cancel Remove
// without receiving a Query Remove earlier, if the
// driver on top fails a Query Remove and passes down the
// Cancel Remove.
//
if ( commonExtension->DeviceState == RamdiskDeviceStatePendingRemove ) {
//
// Mark that the device is back in the working state. Send the
// IRP on down the stack.
//
commonExtension->DeviceState = RamdiskDeviceStateWorking;
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
} else {
//
// A spurious Cancel Remove request. Just complete it.
//
status = STATUS_SUCCESS;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
break;
case IRP_MN_SURPRISE_REMOVAL:
PRINT_CODE( IRP_MN_SURPRISE_REMOVAL );
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
//
// Mark that the device has been removed, and
// pass the IRP down.
//
commonExtension->DeviceState = RamdiskDeviceStateSurpriseRemoved;
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
} else {
//
// Ignore surprise removal for disk PDOs.
//
ASSERT( FALSE );
status = STATUS_SUCCESS;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
break;
case IRP_MN_REMOVE_DEVICE:
PRINT_CODE( IRP_MN_REMOVE_DEVICE );
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
//
// Remove the bus FDO.
//
// Note that RamdiskRemoveBusDevice() sends the IRP down the
// device stack, so we don't complete the IRP here.
//
status = RamdiskRemoveBusDevice( DeviceObject, Irp );
} else {
//
// Remove a disk PDO.
//
status = RamdiskDeleteDiskDevice( DeviceObject, Irp );
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
//
// The remove lock was released by RamdiskRemoveBusDevice or
// RamdiskDeleteDiskDevice.
//
lockHeld = FALSE;
break;
case IRP_MN_EJECT:
PRINT_CODE( IRP_MN_EJECT );
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
//
// Ignore eject for the bus FDO. Just send the IRP down.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
goto send_irp_down;
} else {
//
// Ignore eject for a disk PDO, too. Don't send the IRP down.
//
status = STATUS_SUCCESS;
COMPLETE_REQUEST( status, 0, Irp );
}
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
//
// Let RamdiskQueryDeviceRelations() do the work. Note that it
// completes the IRP.
//
status = RamdiskQueryDeviceRelations(
irpSp->Parameters.QueryDeviceRelations.Type,
DeviceObject,
Irp
);
break;
case IRP_MN_QUERY_DEVICE_TEXT:
//
// For the bus FDO, just pass the IRP down. For a disk PDO, let
// RamdiskQueryDeviceText() do the work and complete the IRP.
//
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
goto send_irp_down;
} else {
status = RamdiskQueryDeviceText( diskExtension, Irp );
}
break;
case IRP_MN_QUERY_BUS_INFORMATION:
//
// Let RamdiskQueryBusInformation() do the work. Note that it
// completes the IRP.
//
status = RamdiskQueryBusInformation( DeviceObject, Irp );
break;
case IRP_MN_QUERY_CAPABILITIES:
//
// For the bus FDO, just pass the IRP down. For a disk PDO, let
// RamdiskQueryCapabilities() do the work and complete the IRP.
//
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
goto send_irp_down;
} else {
status = RamdiskQueryCapabilities( DeviceObject, Irp );
}
break;
case IRP_MN_QUERY_RESOURCES:
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
//
// We don't have any resources to add to whatever might already be
// there, so just complete the IRP.
//
status = Irp->IoStatus.Status;
COMPLETE_REQUEST( Irp->IoStatus.Status, Irp->IoStatus.Information, Irp );
break;
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
//
// For the bus FDO, just pass the IRP down. For a disk PDO, just
// complete the IRP.
//
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
goto send_irp_down;
} else {
status = Irp->IoStatus.Status;
COMPLETE_REQUEST( Irp->IoStatus.Status, Irp->IoStatus.Information, Irp );
}
break;
case IRP_MN_QUERY_ID:
//
// For the bus FDO, just pass the IRP down. For a disk PDO, let
// RamdiskQueryId() do the work and complete the IRP.
//
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
goto send_irp_down;
} else {
status = RamdiskQueryId( diskExtension, Irp );
}
break;
case IRP_MN_QUERY_PNP_DEVICE_STATE:
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
default:
send_irp_down:
//
// If this is the bus FDO, and there is a lower device object,
// send the IRP down to the next device. If this is a disk PDO,
// just complete the IRP.
//
if ( (commonExtension->DeviceType == RamdiskDeviceTypeBusFdo) &&
(commonExtension->LowerDeviceObject != NULL) ) {
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( commonExtension->LowerDeviceObject, Irp );
} else {
status = Irp->IoStatus.Status;
COMPLETE_REQUEST( Irp->IoStatus.Status, Irp->IoStatus.Information, Irp );
}
break;
} // switch
//
// If the lock is still held, release it now.
//
if ( lockHeld ) {
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("RamdiskPnp: done; Device State=%s\n",
StateTable[commonExtension->DeviceState]) );
IoReleaseRemoveLock( &commonExtension->RemoveLock, Irp );
}
return status;
} // RamdiskPnp
NTSTATUS
RamdiskPower (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to perform a power function.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
I/O is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PCOMMON_EXTENSION commonExtension;
PAGED_CODE();
DBGPRINT( DBG_POWER, DBG_VERBOSE,
("RamdiskPower: DO=(%p) Irp=(%p)\n", DeviceObject, Irp) );
commonExtension = DeviceObject->DeviceExtension;
if ( commonExtension->DeviceType == RamdiskDeviceTypeBusFdo ) {
//
// This is the bus FDO. There's not much for us to do here.
//
// Start the next power IRP.
//
PoStartNextPowerIrp( Irp );
//
// If the device has been removed, the driver should not pass
// the IRP down to the next lower driver.
//
if ( commonExtension->DeviceState >= RamdiskDeviceStateRemoved ) {
status = STATUS_DELETE_PENDING;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
return status;
}
//
// Send the IRP on down the stack.
//
IoSkipCurrentIrpStackLocation( Irp );
status = PoCallDriver( commonExtension->LowerDeviceObject, Irp );
} else {
PIO_STACK_LOCATION irpSp;
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
//
// This is a request for a disk PDO.
//
// Get parameters from the IRP.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
powerType = irpSp->Parameters.Power.Type;
powerState = irpSp->Parameters.Power.State;
//
// Dispatch based on the minor function.
//
switch ( irpSp->MinorFunction ) {
case IRP_MN_SET_POWER:
//
// For SET_POWER, we don't have to do anything but return success.
//
switch ( powerType ) {
case DevicePowerState:
case SystemPowerState:
status = STATUS_SUCCESS;
break;
default:
status = STATUS_NOT_SUPPORTED;
break;
}
break;
case IRP_MN_QUERY_POWER:
//
// For QUERY_POWER, we don't have to do anything but return
// success.
//
status = STATUS_SUCCESS;
break;
case IRP_MN_WAIT_WAKE:
case IRP_MN_POWER_SEQUENCE:
default:
status = STATUS_NOT_SUPPORTED;
break;
}
if ( status != STATUS_NOT_SUPPORTED ) {
Irp->IoStatus.Status = status;
}
PoStartNextPowerIrp( Irp );
status = Irp->IoStatus.Status;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
DBGPRINT( DBG_POWER, DBG_VERBOSE, ("RamdiskPower: status = 0x%x\n", status) );
return status;
} // RamdiskPower
NTSTATUS
RamdiskAddDevice (
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT Pdo
)
/*++
Routine Description:
This routine is called by the PnP system to add a device.
We expect to get this call exactly once, to add our bus PDO.
Arguments:
DriverObject - a pointer to our driver object
Pdo - a pointer to the PDO for the FDO that we create
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
UNICODE_STRING deviceName;
PDEVICE_OBJECT fdo;
PBUS_EXTENSION busExtension;
PULONG bitmap;
PLOADER_PARAMETER_BLOCK loaderBlock;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskAddDevice: entered\n") );
//
// If we've already done this once, fail this call.
//
if ( RamdiskBusFdo != NULL ) {
return STATUS_DEVICE_ALREADY_ATTACHED;
}
#if SUPPORT_DISK_NUMBERS
//
// Allocate space for the disk numbers bitmap.
//
bitmap = ALLOCATE_POOL( PagedPool, DiskNumbersBitmapSize, TRUE );
if ( bitmap == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
#endif // SUPPORT_DISK_NUMBERS
//
// Create the bus device object.
//
// ISSUE: Apply an ACL to the bus device object. (Or does the next issue obviate this?)
// ISSUE: We're supposed to use autogenerated names for FDOs. What is the
// harm in using our own name? (Benefit is that it's easier to
// find the device when creating/deleting disks.)
//
RtlInitUnicodeString( &deviceName, L"\\Device\\Ramdisk" );
status = IoCreateDevice(
DriverObject, // DriverObject
sizeof(BUS_EXTENSION), // DeviceExtension
&deviceName, // DeviceName
FILE_DEVICE_BUS_EXTENDER, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
FALSE, // Exclusive
&fdo // DeviceObject
);
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR, ("RamdiskAddDevice: error %x creating bus FDO\n", status) );
#if SUPPORT_DISK_NUMBERS
FREE_POOL( bitmap, TRUE );
#endif // SUPPORT_DISK_NUMBERS
return status;
}
busExtension = fdo->DeviceExtension;
RtlZeroMemory( busExtension, sizeof(BUS_EXTENSION) );
//
// Initialize device object and extension.
//
//
// Our device does direct I/O and is power pageable.
//
fdo->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE;
//
// Set the device type and state in the device extension. Initialize the
// fast mutex and the remove lock. Initialize the disk PDO list.
//
busExtension->DeviceType = RamdiskDeviceTypeBusFdo;
busExtension->DeviceState = RamdiskDeviceStateStopped;
ExInitializeFastMutex( &busExtension->Mutex );
IoInitializeRemoveLock( &busExtension->RemoveLock, 'dmaR', 1, 0 );
InitializeListHead( &busExtension->DiskPdoList );
//
// Save object pointers. The PDO for this extension is the PDO that
// was passed in. The FDO is the device object that we just created. The
// lower device object will be set later.
//
busExtension->Pdo = Pdo;
busExtension->Fdo = fdo;
//
// Register the device interface.
//
status = IoRegisterDeviceInterface(
Pdo,
&RamdiskBusInterface,
NULL,
&busExtension->InterfaceString
);
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("RamdiskAddDevice: error %x registering device interface for bus FDO\n",
status) );
IoDeleteDevice( fdo );
#if SUPPORT_DISK_NUMBERS
FREE_POOL( bitmap, TRUE );
#endif // SUPPORT_DISK_NUMBERS
return status;
}
//
// Attach the FDO to the PDO's device stack. Remember the lower device
// object to which we are to forward PnP IRPs.
//
busExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack( fdo, Pdo );
if ( busExtension->LowerDeviceObject == NULL ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("%s", "RamdiskAddDevice: error attaching bus FDO to PDO stack\n") );
//
// Tell PnP that we're not going to be activating the interface that
// we just registered. Free the symbolic link string associated with
// the interface. Delete the device object.
//
IoSetDeviceInterfaceState( &busExtension->InterfaceString, FALSE );
RtlFreeUnicodeString( &busExtension->InterfaceString );
IoDeleteDevice( fdo );
#if SUPPORT_DISK_NUMBERS
FREE_POOL( bitmap, TRUE );
#endif // SUPPORT_DISK_NUMBERS
return STATUS_NO_SUCH_DEVICE;
}
#if SUPPORT_DISK_NUMBERS
//
// Initialize the disk numbers bitmap.
//
busExtension->DiskNumbersBitmapBuffer = bitmap;
RtlInitializeBitMap( &busExtension->DiskNumbersBitmap, bitmap, DiskNumbersBitmapSize );
RtlClearAllBits( &busExtension->DiskNumbersBitmap );
#endif // SUPPORT_DISK_NUMBERS
RamdiskBusFdo = fdo;
//
// If textmode setup is running, create any RAM disks specified in the
// registry.
//
loaderBlock = *(PLOADER_PARAMETER_BLOCK *)KeLoaderBlock;
if ( (loaderBlock != NULL) && (loaderBlock->SetupLoaderBlock != NULL) ) {
CreateRegistryDisks( FALSE );
}
//
// Indicate that we're done initializing the device.
//
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
} // RamdiskAddDevice
BOOLEAN
CreateRegistryDisks (
IN BOOLEAN CheckPresenceOnly
)
/*++
Routine Description:
This routine creates virtual floppy disks specified in the registry.
It is called only during textmode setup.
Arguments:
CheckPresenceOnly - indicates whether this routine should just check for
the presence of at least one disk in the registry
Return Value:
BOOLEAN - indicates whether any disks were specified in the registry
--*/
{
NTSTATUS status;
OBJECT_ATTRIBUTES obja;
UNICODE_STRING string;
HANDLE serviceHandle;
HANDLE parametersHandle;
ULONG diskNumber;
WCHAR valueNameBuffer[15];
UNICODE_STRING valueName;
UCHAR valueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(VIRTUAL_FLOPPY_DESCRIPTOR)];
PKEY_VALUE_PARTIAL_INFORMATION value;
PVIRTUAL_FLOPPY_DESCRIPTOR descriptor;
ULONG valueLength;
RAMDISK_CREATE_INPUT createInput;
PDISK_EXTENSION diskExtension;
BOOLEAN disksPresent = FALSE;
HRESULT result;
value = (PKEY_VALUE_PARTIAL_INFORMATION)valueBuffer;
descriptor = (PVIRTUAL_FLOPPY_DESCRIPTOR)value->Data;
//
// Open the driver's key under Services.
//
InitializeObjectAttributes( &obja, &DriverRegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL );
status = ZwOpenKey( &serviceHandle, KEY_READ, &obja );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_INIT, DBG_ERROR, ("CreateRegistryDisks: ZwOpenKey(1) failed: %x\n", status) );
return FALSE;
}
//
// Open the Parameters subkey.
//
RtlInitUnicodeString( &string, L"Parameters" );
InitializeObjectAttributes( &obja, &string, OBJ_CASE_INSENSITIVE, serviceHandle, NULL );
status = ZwOpenKey( &parametersHandle, KEY_READ, &obja );
NtClose( serviceHandle );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_INIT, DBG_ERROR, ("CreateRegistryDisks: ZwOpenKey(2) failed: %x\n", status) );
return FALSE;
}
//
// Initialize static fields in the CREATE_INPUT structure that we'll pass
// to RamdiskCreateDiskDevice.
//
RtlZeroMemory( &createInput, sizeof(createInput) );
createInput.DiskType = RAMDISK_TYPE_VIRTUAL_FLOPPY;
createInput.Options.Fixed = TRUE;
createInput.Options.NoDriveLetter = TRUE;
//
// Look for values named DISKn, where n starts at 0 and increases by 1
// each loop. Break out as soon as the expected DISKn is not found.
// (If values named DISK0 and DISK2 are present, only DISK0 will be
// created -- DISK2 will not be found.)
//
diskNumber = 0;
while ( TRUE ) {
// This variable is here to keep PREfast quiet (PREfast warning 209).
size_t size = sizeof(valueNameBuffer);
result = StringCbPrintfW(
valueNameBuffer,
size,
L"DISK%u",
diskNumber
);
ASSERT( result == S_OK );
RtlInitUnicodeString( &valueName, valueNameBuffer );
status = ZwQueryValueKey(
parametersHandle,
&valueName,
KeyValuePartialInformation,
value,
sizeof(valueBuffer),
&valueLength
);
if ( !NT_SUCCESS(status) ) {
if ( status != STATUS_OBJECT_NAME_NOT_FOUND ) {
DBGPRINT( DBG_INIT, DBG_ERROR,
("CreateRegistryDisks: ZwQueryValueKey failed: %x\n", status) );
}
break;
}
//
// We've found a DISKn value in the registry. For the purposes of
// the CheckPresenceOnly flag, this is enough to know that at least
// one virtual floppy disk is present. We don't care whether the
// data is valid -- we just need to know that it's there.
//
disksPresent = TRUE;
//
// If we're just checking for the presence of at least one disk, we
// can leave now.
//
if ( CheckPresenceOnly ) {
break;
}
//
// We expect the value to be a REG_BINARY with the correct length.
// We don't explicitly check the value type; we assume that the
// length check is sufficient. We also expect the base address
// (which is a system virtual address -- either in KSEG0 or in
// nonpaged pool) and the length to be nonzero.
//
if ( value->DataLength != FIELD_OFFSET(VIRTUAL_FLOPPY_DESCRIPTOR, StructSizer) ) {
DBGPRINT( DBG_INIT, DBG_ERROR,
("CreateRegistryDisks: key length wrong, wanted 0x%x, got 0x%x\n",
sizeof(VIRTUAL_FLOPPY_DESCRIPTOR), valueLength) );
} else if ( (descriptor->VirtualAddress == NULL) || (descriptor->Length == 0) ) {
DBGPRINT( DBG_INIT, DBG_ERROR,
("CreateRegistryDisks: address (%x) or length (0x%x) invalid\n",
descriptor->VirtualAddress, descriptor->Length) );
} else {
//
// Create a virtual floppy RAM disk at the specified address and
// with the specified length. Pass the disk number in the GUID.
//
createInput.DiskGuid.Data1 = diskNumber;
createInput.DiskLength = descriptor->Length;
createInput.BaseAddress = descriptor->VirtualAddress;
DBGPRINT( DBG_INIT, DBG_INFO,
("CreateRegistryDisks: creating virtual floppy #%d at %p for %x\n",
diskNumber, descriptor->VirtualAddress, descriptor->Length) );
ASSERT( RamdiskBusFdo != NULL );
ASSERT( RamdiskBusFdo->DeviceExtension != NULL );
status = RamdiskCreateDiskDevice(
RamdiskBusFdo->DeviceExtension,
&createInput,
FALSE,
&diskExtension
);
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_INIT, DBG_ERROR,
("CreateRegistryDisks: RamdiskCreateDiskDevice failed: %x\n", status) );
}
}
diskNumber++;
}
//
// Close the Parameters key and return.
//
NtClose( parametersHandle );
return disksPresent;
} // CreateRegistryDisks
NTSTATUS
RamdiskDeleteDiskDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp OPTIONAL
)
/*++
Routine Description:
This routine is called to delete a RAM disk device.
NOTE: The remove lock is held on entry to this routine. It is released on
exit. If Irp == NULL, the bus mutex is held on entry and released on exit.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request. If NULL, this
is a call from RamdiskRemoveBusDevice().
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PDISK_EXTENSION diskExtension;
PDISK_EXTENSION tempDiskExtension;
PBUS_EXTENSION busExtension;
PLIST_ENTRY listEntry;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskDeleteDiskDevice\n") );
diskExtension = DeviceObject->DeviceExtension;
busExtension = diskExtension->Fdo->DeviceExtension;
DBGPRINT( DBG_PNP, DBG_INFO,
("RamdiskDeleteDiskDevice: Deleting device %wZ\n", &diskExtension->DeviceName) );
//
// If no IRP was specified, then we delete the disk device unconditionally.
// (It's a call from RamdiskRemoveBusDevice().) Otherwise, we need to check
// whether we really want to delete the device now.
//
if ( Irp != NULL ) {
Irp->IoStatus.Information = 0;
//
// Check to see if the device has been marked for removal. If not,
// ignore this IRP. We do this because user-mode PnP likes to remove
// and immmediately recreate the devices that we materialize, but we
// don't want to remove the device and lose the information about the
// disk image.
//
if ( !diskExtension->MarkedForDeletion ) {
//
// This device has not really been removed, so ignore this IRP.
// But do mark that the device is no longer claimed.
//
diskExtension->Status &= ~RAMDISK_STATUS_CLAIMED;
IoReleaseRemoveLock( &diskExtension->RemoveLock, Irp );
return STATUS_SUCCESS;
}
//
// The device has been marked for deletion, so it's OK for PnP to be
// trying to remove it. If this is PnP's first attempt at removing the
// device, just mark it as removed and tell PnP to reenumerate the
// bus. During reenumeration, we will skip this device, and PnP will
// come back with another remove IRP.
//
if ( diskExtension->DeviceState < RamdiskDeviceStateRemoved ) {
diskExtension->DeviceState = RamdiskDeviceStateRemoved;
busExtension = diskExtension->Fdo->DeviceExtension;
IoInvalidateDeviceRelations( busExtension->Pdo, BusRelations );
IoReleaseRemoveLock( &diskExtension->RemoveLock, Irp );
return STATUS_SUCCESS;
}
//
// If the device is marked as removed, but it hasn't yet been skipped
// in a bus enumeration, don't do anything now.
//
if ( diskExtension->DeviceState == RamdiskDeviceStateRemoved ) {
IoReleaseRemoveLock( &diskExtension->RemoveLock, Irp );
return STATUS_SUCCESS;
}
//
// If we get here, we have already skipped this device in a bus
// enumeration, so it's time to delete it. Acquire the bus mutex
// so that we can do this.
//
KeEnterCriticalRegion();
ExAcquireFastMutex( &busExtension->Mutex );
}
//
// If we get here, we really do want to delete this device. If we've
// already deleted it, don't do it again.
//
if ( diskExtension->DeviceState >= RamdiskDeviceStateDeleted ) {
DBGPRINT( DBG_PNP, DBG_INFO,
("RamdiskDeleteDiskDevice: device %wZ has already been deleted\n",
&diskExtension->DeviceName) );
//
// Release the bus mutex and the remove lock.
//
ExReleaseFastMutex( &busExtension->Mutex );
KeLeaveCriticalRegion();
IoReleaseRemoveLock( &diskExtension->RemoveLock, Irp );
return STATUS_SUCCESS;
}
//
// Indicate that the device has been deleted.
//
diskExtension->DeviceState = RamdiskDeviceStateDeleted;
//
// Remove the disk PDO from the bus FDO's list.
//
for ( listEntry = busExtension->DiskPdoList.Flink;
listEntry != &busExtension->DiskPdoList;
listEntry = listEntry->Flink ) {
tempDiskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
if ( tempDiskExtension == diskExtension ) {
RemoveEntryList( listEntry );
#if SUPPORT_DISK_NUMBERS
RtlClearBit( &busExtension->DiskNumbersBitmap, diskExtension->DiskNumber - 1 );
#endif // SUPPORT_DISK_NUMBERS
break;
}
}
//
// We no longer need to hold the bus mutex and the remove lock.
//
ExReleaseFastMutex( &busExtension->Mutex );
KeLeaveCriticalRegion();
IoReleaseRemoveLockAndWait( &diskExtension->RemoveLock, Irp );
//
// If the interface has been started, stop it now.
//
if ( diskExtension->InterfaceString.Buffer != NULL ) {
if ( !diskExtension->Options.Hidden ) {
status = IoSetDeviceInterfaceState( &diskExtension->InterfaceString, FALSE );
}
RtlFreeUnicodeString( &diskExtension->InterfaceString );
}
//
// Close the file backing the RAM disk, if any.
//
if ( diskExtension->SectionObject != NULL ) {
if ( diskExtension->ViewDescriptors != NULL ) {
//
// Clean up the mapped views.
//
PVIEW view;
ASSERT( diskExtension->ViewWaiterCount == 0 );
while ( !IsListEmpty( &diskExtension->ViewsByOffset ) ) {
listEntry = RemoveHeadList( &diskExtension->ViewsByOffset );
view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry );
RemoveEntryList( &view->ByMruListEntry );
ASSERT( view->ReferenceCount == 0 );
if ( view->Address != NULL ) {
DBGPRINT( DBG_WINDOW, DBG_VERBOSE,
("RamdiskDeleteDiskDevice: unmapping view %p; addr %p\n",
view, view->Address) );
MmUnmapViewOfSection( PsGetCurrentProcess(), view->Address );
}
}
ASSERT( IsListEmpty( &diskExtension->ViewsByMru ) );
FREE_POOL( diskExtension->ViewDescriptors, TRUE );
}
ObDereferenceObject( diskExtension->SectionObject );
}
if ( !diskExtension->Options.NoDosDevice ) {
//
// Delete the DosDevices symbolic link.
//
ASSERT( diskExtension->DosSymLink.Buffer != NULL );
status = IoDeleteSymbolicLink( &diskExtension->DosSymLink );
if ( !NT_SUCCESS(status) ) {
DBGPRINT( DBG_PNP, DBG_ERROR,
("RamdiskDeleteDiskDevice: IoDeleteSymbolicLink failed: %x\n", status) );
}
FREE_POOL( diskExtension->DosSymLink.Buffer, TRUE );
}
//
// Delete the device name string and the GUID string.
//
if ( diskExtension->DeviceName.Buffer != NULL ) {
FREE_POOL( diskExtension->DeviceName.Buffer, TRUE );
}
if ( diskExtension->DiskGuidFormatted.Buffer != NULL ) {
FREE_POOL( diskExtension->DiskGuidFormatted.Buffer, FALSE );
}
//
// Delete the device object.
//
IoDeleteDevice( DeviceObject );
return STATUS_SUCCESS;
} // RamdiskDeleteDiskDevice
NTSTATUS
RamdiskIoCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This internal routine is used as the I/O completion routine when we send
an IRP down the device stack and want to short-circuit IRP completion so
that we can do more work.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Event - a pointer to an event that is to be set to signal the calling code
that the lower layers have completed the IRP
Return Value:
NTSTATUS - the status of the operation
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
//
// Set the event to signal the IRP issuer that it's time to continue.
//
KeSetEvent( Event, 0, FALSE );
//
// Tell the I/O system to stop completing the IRP.
//
return STATUS_MORE_PROCESSING_REQUIRED;
} // RamdiskIoCompletionRoutine
NTSTATUS
RamdiskQueryBusInformation (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine processes the IRP_MN_QUERY_BUS_INFORMATION IRP.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PPNP_BUS_INFORMATION busInformation;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("RamdiskQueryBusInformation: DO (0x%p) Type 0x%x\n",
DeviceObject, ((PCOMMON_EXTENSION)DeviceObject->DeviceExtension)->DeviceType) );
//
// Allocate a buffer to use for returning the requested information.
//
busInformation = ALLOCATE_POOL( PagedPool, sizeof(PNP_BUS_INFORMATION), FALSE );
if ( busInformation == NULL ) {
//
// Fail the IRP.
//
status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST( status, 0, Irp );
return status;
}
//
// Fill in the requested information.
//
busInformation->BusTypeGuid = GUID_BUS_TYPE_RAMDISK;
busInformation->LegacyBusType = PNPBus;
busInformation->BusNumber = 0x00;
//
// Complete the IRP.
//
status = STATUS_SUCCESS;
COMPLETE_REQUEST( status, (ULONG_PTR)busInformation, Irp );
return status;
} // RamdiskQueryBusInformation
NTSTATUS
RamdiskQueryCapabilities (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine processes the IRP_MN_QUERY_CAPABILITIES IRP.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PDEVICE_CAPABILITIES deviceCapabilities;
PDISK_EXTENSION diskExtension;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskQueryCapabilities\n") );
//
// Get a pointer to the device extension and get parameters from the IRP.
//
diskExtension = DeviceObject->DeviceExtension;
irpSp = IoGetCurrentIrpStackLocation( Irp );
deviceCapabilities = irpSp->Parameters.DeviceCapabilities.Capabilities;
if ( (deviceCapabilities->Version != 1) ||
(deviceCapabilities->Size < sizeof(DEVICE_CAPABILITIES)) ) {
//
// We don't support this version. Fail the request.
//
status = STATUS_UNSUCCESSFUL;
} else {
status = STATUS_SUCCESS;
//
// If this is an emulated volume, we want to allow access to the raw
// device. (Otherwise PnP won't start the device.)
//
// Note that a RAM disk boot disk is an emulated volume.
//
deviceCapabilities->RawDeviceOK =
(BOOLEAN)(diskExtension->DiskType != RAMDISK_TYPE_FILE_BACKED_DISK);
//
// Indicate that ejection is not supported.
//
deviceCapabilities->EjectSupported = FALSE;
//
// This flag specifies whether the device's hardware is disabled.
// The PnP Manager only checks this bit right after the device is
// enumerated. Once the device is started, this bit is ignored.
//
deviceCapabilities->HardwareDisabled = FALSE;
//
// Indicate that the emulated device cannot be physically removed.
// (Unless the right registry key was specified...)
//
deviceCapabilities->Removable = MarkRamdisksAsRemovable;
//
// Setting SurpriseRemovalOK to TRUE prevents the warning dialog from
// appearing whenever the device is surprise removed. Setting it FALSE
// allows the Hot unplug applet to stop the device.
//
// We don't want our disks to show up in the systray, so we set
// SurpriseRemovalOK to TRUE. There is never really any surprise
// removal anyway -- removal comes from the user mode control app
// calling CM_Query_And_Remove_SubTree_Ex().
//
deviceCapabilities->SurpriseRemovalOK = TRUE;
//
// We support system-wide unique IDs.
//
deviceCapabilities->UniqueID = TRUE;
//
// Indicate that the Device Manager should suppress all
// installation pop-ups except required pop-ups such as
// "no compatible drivers found."
//
deviceCapabilities->SilentInstall = TRUE;
//
// Indicate that we do not want this device displayed in
// Device Manager.
//
deviceCapabilities->NoDisplayInUI = TRUE;
}
//
// Complete the request.
//
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
return status;
} // RamdiskQueryCapabilities
NTSTATUS
RamdiskQueryId (
IN PDISK_EXTENSION DiskExtension,
IN PIRP Irp
)
/*++
Routine Description:
This routine processes the IRP_MN_QUERY_ID IRP for disk devices.
Arguments:
DiskExtension - a pointer to the device extension for the device object on
which the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
#define MAX_LOCAL_STRING 50
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
PWCHAR buffer;
PWCHAR p;
ULONG length;
PWCHAR deviceType;
HRESULT result;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskQueryId\n") );
//
// Assume success.
//
status = STATUS_SUCCESS;
irpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Dispatch based on the query type.
//
switch ( irpSp->Parameters.QueryId.IdType ) {
case BusQueryDeviceID:
//
// DeviceID is a string to identify a device. We return the string
// "Ramdisk\RamVolume" or "Ramdisk\RamDisk".
//
// Allocate pool to hold the string.
//
length = sizeof(RAMDISK_ENUMERATOR_TEXT) - sizeof(WCHAR) +
((DiskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK) ?
sizeof(RAMDISK_DISK_DEVICE_TEXT) : sizeof(RAMDISK_VOLUME_DEVICE_TEXT));
buffer = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW( buffer, length, RAMDISK_ENUMERATOR_TEXT );
ASSERT( result == S_OK );
result = StringCbCatW(
buffer,
length,
(DiskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK) ?
RAMDISK_DISK_DEVICE_TEXT : RAMDISK_VOLUME_DEVICE_TEXT
);
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 1) * sizeof(WCHAR)) == length );
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("BusQueryDeviceID=%S\n", buffer) );
break;
case BusQueryInstanceID:
//
// InstanceID is a string to identify the device instance. We return
// the disk GUID in string form.
//
// Allocate pool to hold the string.
//
buffer = ALLOCATE_POOL( PagedPool, DiskExtension->DiskGuidFormatted.MaximumLength, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW(
buffer,
DiskExtension->DiskGuidFormatted.MaximumLength,
DiskExtension->DiskGuidFormatted.Buffer
);
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 1) * sizeof(WCHAR)) == DiskExtension->DiskGuidFormatted.MaximumLength );
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("BusQueryInstanceID=%S\n", buffer) );
break;
case BusQueryHardwareIDs:
//
// HardwareIDs is a multi-sz string to identify a device's hardware
// type. We return the string "Ramdisk\RamVolume\0" or
// "Ramdisk\RamDisk\0".
//
// Allocate pool to hold the string. Note that we allocate space
// for two null terminators.
//
length = sizeof(RAMDISK_ENUMERATOR_TEXT) - sizeof(WCHAR) +
((DiskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK) ?
sizeof(RAMDISK_DISK_DEVICE_TEXT) : sizeof(RAMDISK_VOLUME_DEVICE_TEXT)) +
sizeof(WCHAR);
buffer = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW( buffer, length, RAMDISK_ENUMERATOR_TEXT );
ASSERT( result == S_OK );
result = StringCbCatW(
buffer,
length,
(DiskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK) ?
RAMDISK_DISK_DEVICE_TEXT : RAMDISK_VOLUME_DEVICE_TEXT
);
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 2) * sizeof(WCHAR)) == length );
buffer[length/sizeof(WCHAR) - 1] = 0;
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("BusQueryHardwareIDs=%S\n", buffer) );
break;
case BusQueryCompatibleIDs:
//
// HardwareIDs is a multi-sz string to identify device classes that
// are compatible with a device. For volume-emulating RAM disks, we
// return no compatible IDs, so that the device stands on its own at
// the volume level. For disk-emulating RAM disks, we return the
// string "Gendisk\0", so that the device gets hooked in below disk.sys.
//
if ( DiskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK ) {
//
// Disk emulation. Allocate pool to hold the string.
//
length = sizeof(L"GenDisk") + sizeof(WCHAR);
buffer = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW( buffer, length, L"GenDisk" );
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 2) * sizeof(WCHAR)) == length );
buffer[length/sizeof(WCHAR) - 1] = 0;
} else {
//
// Volume emulation. Do not return any compatible IDs.
//
buffer = NULL;
status = STATUS_INVALID_DEVICE_REQUEST;
}
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("BusQueryCompatibleIDs=%S\n", buffer) );
break;
default:
//
// Unknown query type. Just leave whatever's already in the IRP there.
//
status = Irp->IoStatus.Status;
buffer = (PWCHAR)Irp->IoStatus.Information;
}
//
// Complete the request.
//
COMPLETE_REQUEST( status, (ULONG_PTR)buffer, Irp );
return status;
} // RamdiskQueryId
NTSTATUS
RamdiskQueryDeviceRelations (
IN DEVICE_RELATION_TYPE RelationsType,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine processes the IRP_MN_QUERY_DEVICE_RELATIONS IRP.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PCOMMON_EXTENSION commonExtension;
PBUS_EXTENSION busExtension;
PDISK_EXTENSION diskExtension;
RAMDISK_DEVICE_TYPE deviceType;
PLIST_ENTRY listEntry;
PDEVICE_RELATIONS deviceRelations;
PDEVICE_RELATIONS oldRelations;
ULONG prevCount = 0;
ULONG length = 0;
ULONG numPdosPresent = 0;
PAGED_CODE();
//
// Assume success.
//
status = STATUS_SUCCESS;
//
// Get the device extension pointer and save the device type.
//
commonExtension = (PCOMMON_EXTENSION)DeviceObject->DeviceExtension;
deviceType = commonExtension->DeviceType;
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("RamdiskQueryDeviceRelations: QueryDeviceRelation Type: %s, DeviceType 0x%x\n",
GetDeviceRelationString(RelationsType), deviceType) );
//
// Dispatch based on the device type.
//
if ( deviceType == RamdiskDeviceTypeDiskPdo ) {
//
// It's a disk PDO. We only handle TargetDeviceRelation for PDOs.
//
diskExtension = (PDISK_EXTENSION)commonExtension;
if ( RelationsType == TargetDeviceRelation ) {
//
// Allocate pool to hold the return information. (DEVICE_RELATIONS
// has space for one entry built-in).
//
deviceRelations = ALLOCATE_POOL( PagedPool, sizeof(DEVICE_RELATIONS), FALSE );
if ( deviceRelations != NULL ) {
//
// Return a referenced pointer to the device object for this
// device.
//
ObReferenceObject( DeviceObject );
deviceRelations->Count = 1;
deviceRelations->Objects[0] = DeviceObject;
status = STATUS_SUCCESS;
} else {
//
// Couldn't allocate pool.
//
status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Complete the request.
//
COMPLETE_REQUEST( status, (ULONG_PTR)deviceRelations, Irp );
} else {
//
// PDOs just complete enumeration requests without altering
// the status.
//
status = Irp->IoStatus.Status;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
}
return status;
} else {
//
// It's the bus FDO. We only handle BusRelations for the FDO.
//
busExtension = (PBUS_EXTENSION)commonExtension;
if ( RelationsType == BusRelations ) {
//
// Re-enumerate the device.
//
// Lock the disk PDO list.
//
KeEnterCriticalRegion();
ExAcquireFastMutex( &busExtension->Mutex );
//
// There might also be device relations below and above this FDO,
// so propagate the relations from the upper drivers.
//
oldRelations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
if (oldRelations != NULL) {
prevCount = oldRelations->Count;
} else {
prevCount = 0;
}
//
// Calculate the number of PDOs actually present on the bus.
//
numPdosPresent = 0;
for ( listEntry = busExtension->DiskPdoList.Flink;
listEntry != &busExtension->DiskPdoList;
listEntry = listEntry->Flink ) {
diskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
if ( diskExtension->DeviceState < RamdiskDeviceStateRemoved ) {
numPdosPresent++;
}
}
//
// Allocate a new relations structure and add our PDOs to it.
//
length = sizeof(DEVICE_RELATIONS) +
((numPdosPresent + prevCount - 1) * sizeof(PDEVICE_OBJECT));
deviceRelations = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( deviceRelations == NULL ) {
//
// Fail the IRP.
//
ExReleaseFastMutex( &busExtension->Mutex );
KeLeaveCriticalRegion();
status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST( status, Irp->IoStatus.Information, Irp );
return status;
}
//
// Copy in the device objects so far.
//
if ( prevCount != 0 ) {
RtlCopyMemory(
deviceRelations->Objects,
oldRelations->Objects,
prevCount * sizeof(PDEVICE_OBJECT)
);
}
deviceRelations->Count = prevCount + numPdosPresent;
//
// For each PDO present on this bus, add a pointer to the device
// relations buffer, being sure to take out a reference to that
// object. PnP will dereference the object when it is done with it
// and free the device relations buffer.
//
for ( listEntry = busExtension->DiskPdoList.Flink;
listEntry != &busExtension->DiskPdoList;
listEntry = listEntry->Flink ) {
diskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
if ( diskExtension->DeviceState < RamdiskDeviceStateRemoved ) {
ObReferenceObject( diskExtension->Pdo );
deviceRelations->Objects[prevCount] = diskExtension->Pdo;
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("QueryDeviceRelations(BusRelations) PDO = 0x%p\n",
deviceRelations->Objects[prevCount]) );
prevCount++;
} else {
if ( diskExtension->DeviceState == RamdiskDeviceStateRemoved ) {
diskExtension->DeviceState = RamdiskDeviceStateRemovedAndNotReported;
}
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("QueryDeviceRelations(BusRelations) PDO = 0x%p -- SKIPPED\n",
diskExtension->Pdo) );
}
}
//
// Release the lock.
//
ExReleaseFastMutex( &busExtension->Mutex );
KeLeaveCriticalRegion();
DBGPRINT( DBG_PNP, DBG_VERBOSE,
("QueryDeviceRelations(BusRelations) Total #PDOs reported = %d, "
"%d were new\n",
deviceRelations->Count, numPdosPresent) );
//
// Replace the relations structure in the IRP with the new
// one.
//
if ( oldRelations != NULL ) {
FREE_POOL( oldRelations, FALSE );
}
Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
Irp->IoStatus.Status = STATUS_SUCCESS;
}
//
// Send the IRP down the device stack.
//
IoCopyCurrentIrpStackLocationToNext( Irp );
status = IoCallDriver( busExtension->LowerDeviceObject, Irp );
}
return status;
} // RamdiskQueryDeviceRelations
NTSTATUS
RamdiskQueryDeviceText (
IN PDISK_EXTENSION DiskExtension,
IN PIRP Irp
)
/*++
Routine Description:
This routine processes the IRP_MN_QUERY_DEVICE_TEXT IRP.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
ULONG length;
PWCHAR buffer;
UNICODE_STRING tempString;
HRESULT result;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskQueryDeviceText\n") );
//
// Assume success.
//
status = STATUS_SUCCESS;
irpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Dispatch based on the query type.
//
switch ( irpSp->Parameters.QueryDeviceText.DeviceTextType ) {
case DeviceTextDescription:
//
// Description is just "RamDisk".
//
// Allocate pool to hold the string.
//
length = sizeof( RAMDISK_DISK_DEVICE_TEXT );
buffer = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW( buffer, length, RAMDISK_DISK_DEVICE_TEXT );
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 1) * sizeof(WCHAR)) == length );
break;
case DeviceTextLocationInformation:
//
// LocationInformation is just "Ramdisk\\0".
//
// Allocate pool to hold the string.
//
length = sizeof( RAMDISK_ENUMERATOR_BUS_TEXT );
buffer = ALLOCATE_POOL( PagedPool, length, FALSE );
if ( buffer == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy the string into the destination buffer.
//
result = StringCbCopyW( buffer, length, RAMDISK_ENUMERATOR_BUS_TEXT );
ASSERT( result == S_OK );
ASSERT( ((wcslen(buffer) + 1) * sizeof(WCHAR)) == length );
break;
default:
//
// Unknown query type. Just leave whatever's already in the IRP there.
//
status = Irp->IoStatus.Status;
buffer = (PWCHAR)Irp->IoStatus.Information;
}
//
// Complete the request.
//
COMPLETE_REQUEST( status, (ULONG_PTR)buffer, Irp );
return status;
} // RamdiskQueryDeviceText
NTSTATUS
RamdiskRemoveBusDevice (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine removes the bus device.
The remove lock must be held on entry.
Arguments:
DeviceObject - a pointer to the object that represents the device on which
the operation is to be performed
Irp - a pointer to the I/O Request Packet for this request
Return Value:
NTSTATUS - the status of the operation
--*/
{
NTSTATUS status;
PBUS_EXTENSION busExtension;
PLIST_ENTRY listEntry;
PDISK_EXTENSION diskExtension;
PAGED_CODE();
DBGPRINT( DBG_PNP, DBG_VERBOSE, ("%s", "RamdiskRemoveBusDevice\n" ) );
//
// Get a pointer to the device extension.
//
busExtension = DeviceObject->DeviceExtension;
//
// Lock the disk PDO list. Walk the list, deleting all remaining devices.
//
KeEnterCriticalRegion();
ExAcquireFastMutex( &busExtension->Mutex );
while ( !IsListEmpty( &busExtension->DiskPdoList ) ) {
listEntry = busExtension->DiskPdoList.Flink;
//
// Delete the device and clean it up. Acquire the remove lock first.
// RamdiskDeleteDiskDevice releases it.
//
diskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
status = IoAcquireRemoveLock( &diskExtension->RemoveLock, NULL );
ASSERT( NT_SUCCESS(status) );
RamdiskDeleteDiskDevice( diskExtension->Pdo, NULL );
KeEnterCriticalRegion();
ExAcquireFastMutex( &busExtension->Mutex );
}
//
// Release the lock.
//
ExReleaseFastMutex( &busExtension->Mutex );
KeLeaveCriticalRegion();
//
// Pass the IRP on down to lower levels.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( busExtension->LowerDeviceObject, Irp );
//
// Set the device status to Removed and wait for other drivers
// to release the lock, then delete the device object.
//
busExtension->DeviceState = RamdiskDeviceStateRemoved;
IoReleaseRemoveLockAndWait( &busExtension->RemoveLock, Irp );
//
// Stop the interface and free the interface string.
//
if ( busExtension->InterfaceString.Buffer != NULL ) {
IoSetDeviceInterfaceState( &busExtension->InterfaceString, FALSE );
RtlFreeUnicodeString( &busExtension->InterfaceString );
}
//
// If attached to a lower device, detach now.
//
if ( busExtension->LowerDeviceObject != NULL ) {
IoDetachDevice( busExtension->LowerDeviceObject );
}
#if SUPPORT_DISK_NUMBERS
//
// Free the disk numbers bitmap.
//
ASSERT( !RtlAreBitsSet( &busExtension->DiskNumbersBitmap, 0, DiskNumbersBitmapSize ) );
FREE_POOL( busExtension->DiskNumbersBitmapBuffer, TRUE );
#endif // SUPPORT_DISK_NUMBERS
//
// Indicate that we no longer have a bus FDO, and delete the device object.
//
RamdiskBusFdo = NULL;
IoDeleteDevice( DeviceObject );
DBGPRINT( DBG_PNP, DBG_NOTIFY, ("%s", "Device removed succesfully\n") );
return status;
} // RamdiskRemoveBusDevice
#if DBG
PSTR
GetPnpIrpName (
IN UCHAR PnpMinorFunction
)
{
static char functionName[25];
HRESULT result;
PAGED_CODE();
switch ( PnpMinorFunction ) {
case IRP_MN_START_DEVICE:
return "IRP_MN_START_DEVICE";
case IRP_MN_QUERY_REMOVE_DEVICE:
return "IRP_MN_QUERY_REMOVE_DEVICE";
case IRP_MN_REMOVE_DEVICE:
return "IRP_MN_REMOVE_DEVICE";
case IRP_MN_CANCEL_REMOVE_DEVICE:
return "IRP_MN_CANCEL_REMOVE_DEVICE";
case IRP_MN_STOP_DEVICE:
return "IRP_MN_STOP_DEVICE";
case IRP_MN_QUERY_STOP_DEVICE:
return "IRP_MN_QUERY_STOP_DEVICE";
case IRP_MN_CANCEL_STOP_DEVICE:
return "IRP_MN_CANCEL_STOP_DEVICE";
case IRP_MN_QUERY_DEVICE_RELATIONS:
return "IRP_MN_QUERY_DEVICE_RELATIONS";
case IRP_MN_QUERY_INTERFACE:
return "IRP_MN_QUERY_INTERFACE";
case IRP_MN_QUERY_CAPABILITIES:
return "IRP_MN_QUERY_CAPABILITIES";
case IRP_MN_QUERY_RESOURCES:
return "IRP_MN_QUERY_RESOURCES";
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
case IRP_MN_QUERY_DEVICE_TEXT:
return "IRP_MN_QUERY_DEVICE_TEXT";
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
case IRP_MN_READ_CONFIG:
return "IRP_MN_READ_CONFIG";
case IRP_MN_WRITE_CONFIG:
return "IRP_MN_WRITE_CONFIG";
case IRP_MN_EJECT:
return "IRP_MN_EJECT";
case IRP_MN_SET_LOCK:
return "IRP_MN_SET_LOCK";
case IRP_MN_QUERY_ID:
return "IRP_MN_QUERY_ID";
case IRP_MN_QUERY_PNP_DEVICE_STATE:
return "IRP_MN_QUERY_PNP_DEVICE_STATE";
case IRP_MN_QUERY_BUS_INFORMATION:
return "IRP_MN_QUERY_BUS_INFORMATION";
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
case IRP_MN_SURPRISE_REMOVAL:
return "IRP_MN_SURPRISE_REMOVAL";
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
return "IRP_MN_QUERY_LEGACY_BUS_INFORMATION";
default:
result = StringCbPrintfA(
functionName,
sizeof( functionName ),
"Unknown PnP IRP 0x%02x",
PnpMinorFunction
);
ASSERT( result == S_OK );
return functionName;
}
} // GetPnpIrpName
PCHAR
GetDeviceRelationString (
IN DEVICE_RELATION_TYPE Type
)
{
static char relationName[30];
HRESULT result;
PAGED_CODE();
switch ( Type ) {
case BusRelations:
return "BusRelations";
case EjectionRelations:
return "EjectionRelations";
case RemovalRelations:
return "RemovalRelations";
case TargetDeviceRelation:
return "TargetDeviceRelation";
default:
result = StringCbPrintfA(
relationName,
sizeof( relationName ),
"Unknown relation 0x%02x",
Type
);
ASSERT( result == S_OK );
return relationName;
}
} // GetDeviceRelationString
#endif // DBG