/*++ 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( ¶metersHandle, 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