You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2868 lines
82 KiB
2868 lines
82 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ioctl.c
|
|
|
|
Abstract:
|
|
|
|
This file contains RAM disk driver code for processing IOCTLs.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (ChuckL) 2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntverp.h>
|
|
|
|
#if !DBG
|
|
|
|
#define PRINT_CODE( _code )
|
|
|
|
#else
|
|
|
|
#define PRINT_CODE( _code ) \
|
|
if ( print ) { \
|
|
DBGPRINT( DBG_IOCTL, DBG_VERBOSE, ("%s", " " #_code "\n") ); \
|
|
} \
|
|
print = FALSE;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Local functions.
|
|
//
|
|
|
|
NTSTATUS
|
|
RamdiskQueryProperty (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
);
|
|
|
|
//
|
|
// Declare pageable routines.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text( PAGE, RamdiskDeviceControl )
|
|
#pragma alloc_text( PAGE, RamdiskCreateRamDisk )
|
|
#pragma alloc_text( PAGE, RamdiskCreateDiskDevice )
|
|
#pragma alloc_text( PAGE, RamdiskGetDriveLayout )
|
|
#pragma alloc_text( PAGE, RamdiskGetPartitionInfo )
|
|
#pragma alloc_text( PAGE, RamdiskSetPartitionInfo )
|
|
#pragma alloc_text( PAGE, RamdiskQueryProperty )
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
NTSTATUS
|
|
RamdiskDeviceControl (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to perform a device I/O
|
|
control 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
|
|
|
|
--*/
|
|
|
|
{
|
|
//PBIOS_PARAMETER_BLOCK bios;
|
|
PCOMMON_EXTENSION commonExtension;
|
|
PBUS_EXTENSION busExtension;
|
|
PDISK_EXTENSION diskExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PRAMDISK_QUERY_INPUT queryInput;
|
|
PRAMDISK_QUERY_OUTPUT queryOutput;
|
|
PRAMDISK_MARK_FOR_DELETION_INPUT markInput;
|
|
PLIST_ENTRY listEntry;
|
|
NTSTATUS status;
|
|
ULONG_PTR info;
|
|
BOOLEAN lockHeld = FALSE;
|
|
BOOLEAN calleeWillComplete = FALSE;
|
|
|
|
#if DBG
|
|
BOOLEAN print = TRUE;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up device extension and IRP pointers.
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
busExtension = DeviceObject->DeviceExtension;
|
|
diskExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// ISSUE: what about BiosParameters?
|
|
//
|
|
//bios = &diskExtension->BiosParameters;
|
|
|
|
//
|
|
// Acquire the remove lock. If this fails, fail the I/O.
|
|
//
|
|
|
|
status = IoAcquireRemoveLock( &commonExtension->RemoveLock, Irp );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
COMPLETE_REQUEST( status, 0, Irp );
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Indicate that the remove lock is held.
|
|
//
|
|
|
|
lockHeld = TRUE;
|
|
|
|
//
|
|
// Assume failure.
|
|
//
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
info = 0;
|
|
|
|
//
|
|
// Dispatch based on the device type (bus or disk).
|
|
//
|
|
|
|
switch ( commonExtension->DeviceType ) {
|
|
|
|
case RamdiskDeviceTypeBusFdo:
|
|
|
|
//
|
|
// The target is the bus FDO.
|
|
//
|
|
// Dispatch based on the IOCTL code.
|
|
//
|
|
|
|
switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
|
|
|
|
case FSCTL_CREATE_RAM_DISK:
|
|
|
|
PRINT_CODE( FSCTL_DISK_CREATE_RAM_DISK );
|
|
|
|
//
|
|
// Creation of a RAM disk must be handled in thread context. But
|
|
// before sending it off to the thread, we need to verify that
|
|
// the caller has access to the backing file.
|
|
//
|
|
|
|
status = RamdiskCreateRamDisk( DeviceObject, Irp, TRUE );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
status = SendIrpToThread( DeviceObject, Irp );
|
|
}
|
|
|
|
break;
|
|
|
|
case FSCTL_QUERY_RAM_DISK:
|
|
|
|
PRINT_CODE( FSCTL_QUERY_RAM_DISK );
|
|
|
|
//
|
|
// Lock the disk PDO list and look for a disk with the specified
|
|
// disk GUID.
|
|
//
|
|
// Verify that the input parameter buffer is big enough.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(RAMDISK_QUERY_INPUT) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
queryInput = (PRAMDISK_QUERY_INPUT)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if ( queryInput->Version != sizeof(RAMDISK_QUERY_INPUT) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutex( &busExtension->Mutex );
|
|
|
|
diskExtension = NULL;
|
|
|
|
for ( listEntry = busExtension->DiskPdoList.Flink;
|
|
listEntry != &busExtension->DiskPdoList;
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
diskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
|
|
|
|
if ( memcmp(
|
|
&diskExtension->DiskGuid,
|
|
&queryInput->DiskGuid,
|
|
sizeof(diskExtension->DiskGuid)
|
|
) == 0 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
diskExtension = NULL;
|
|
}
|
|
|
|
if ( diskExtension == NULL ) {
|
|
|
|
//
|
|
// Couldn't find a matching device.
|
|
//
|
|
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found a matching device. Return the requested information.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(RAMDISK_QUERY_OUTPUT);
|
|
if ( RAMDISK_IS_FILE_BACKED(diskExtension->DiskType) ) {
|
|
// NB: struct size already includes space for one wchar.
|
|
info += wcslen(diskExtension->FileName) * sizeof(WCHAR);
|
|
}
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength < info ) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
info = 0;
|
|
|
|
} else {
|
|
|
|
queryOutput = (PRAMDISK_QUERY_OUTPUT)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
queryOutput->Version = sizeof(RAMDISK_QUERY_OUTPUT);
|
|
queryOutput->DiskGuid = diskExtension->DiskGuid;
|
|
queryOutput->DiskType = diskExtension->DiskType;
|
|
queryOutput->Options = diskExtension->Options;
|
|
queryOutput->DiskLength = diskExtension->DiskLength;
|
|
queryOutput->DiskOffset = diskExtension->DiskOffset;
|
|
queryOutput->ViewCount = diskExtension->ViewCount;
|
|
queryOutput->ViewLength = diskExtension->ViewLength;
|
|
|
|
if ( diskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
|
|
|
|
queryOutput->BasePage = diskExtension->BasePage;
|
|
queryOutput->DriveLetter = diskExtension->DriveLetter;
|
|
|
|
} else if ( diskExtension->DiskType == RAMDISK_TYPE_VIRTUAL_FLOPPY ) {
|
|
|
|
queryOutput->BaseAddress = diskExtension->BaseAddress;
|
|
|
|
} else {
|
|
|
|
size_t remainingLength;
|
|
HRESULT result;
|
|
|
|
remainingLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
remainingLength -= FIELD_OFFSET( RAMDISK_QUERY_OUTPUT, FileName );
|
|
|
|
result = StringCbCopyW(
|
|
queryOutput->FileName,
|
|
remainingLength,
|
|
diskExtension->FileName
|
|
);
|
|
ASSERT( result == S_OK );
|
|
}
|
|
}
|
|
}
|
|
|
|
ExReleaseFastMutex( &commonExtension->Mutex );
|
|
KeLeaveCriticalRegion();
|
|
|
|
break;
|
|
|
|
case FSCTL_MARK_RAM_DISK_FOR_DELETION:
|
|
|
|
PRINT_CODE( FSCTL_MARK_RAM_DISK_FOR_DELETION );
|
|
|
|
//
|
|
// Lock the disk PDO list and look for a disk with the specified
|
|
// disk GUID.
|
|
//
|
|
// Verify that the input parameter buffer is big enough.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(RAMDISK_MARK_FOR_DELETION_INPUT) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
markInput = (PRAMDISK_MARK_FOR_DELETION_INPUT)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if ( markInput->Version != sizeof(RAMDISK_MARK_FOR_DELETION_INPUT) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutex( &busExtension->Mutex );
|
|
|
|
diskExtension = NULL;
|
|
|
|
for ( listEntry = busExtension->DiskPdoList.Flink;
|
|
listEntry != &busExtension->DiskPdoList;
|
|
listEntry = listEntry->Flink ) {
|
|
|
|
diskExtension = CONTAINING_RECORD( listEntry, DISK_EXTENSION, DiskPdoListEntry );
|
|
|
|
if ( memcmp(
|
|
&diskExtension->DiskGuid,
|
|
&markInput->DiskGuid,
|
|
sizeof(diskExtension->DiskGuid)
|
|
) == 0 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
diskExtension = NULL;
|
|
}
|
|
|
|
if ( diskExtension == NULL ) {
|
|
|
|
//
|
|
// Couldn't find a matching device.
|
|
//
|
|
|
|
status = STATUS_NO_SUCH_DEVICE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found a matching device. Mark it for deletion.
|
|
//
|
|
|
|
diskExtension->MarkedForDeletion = TRUE;
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExReleaseFastMutex( &commonExtension->Mutex );
|
|
KeLeaveCriticalRegion();
|
|
|
|
break;
|
|
|
|
case IOCTL_STORAGE_QUERY_PROPERTY:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_QUERY_PROPERTY );
|
|
|
|
//
|
|
// Call RamdiskQueryProperty() to handle the request. This routine
|
|
// releases the lock. It also takes care of IRP completion.
|
|
//
|
|
|
|
status = RamdiskQueryProperty( DeviceObject, Irp );
|
|
|
|
lockHeld = FALSE;
|
|
calleeWillComplete = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// The specified I/O control code is unrecognized by this driver.
|
|
// The I/O status field in the IRP has already been set, so just
|
|
// terminate the switch.
|
|
//
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR, ("Ramdisk: ERROR: unrecognized IOCTL %x\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
|
|
UNRECOGNIZED_IOCTL_BREAK;
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case RamdiskDeviceTypeDiskPdo:
|
|
|
|
//
|
|
// The target is a disk PDO.
|
|
//
|
|
// Dispatch based on the IOCTL code.
|
|
//
|
|
|
|
switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_QUERY_DEVICE_NAME );
|
|
|
|
{
|
|
PMOUNTDEV_NAME mountName;
|
|
ULONG outputLength;
|
|
|
|
outputLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// The output buffer must be at least big enough to hold the
|
|
// length of the device name.
|
|
//
|
|
|
|
if ( outputLength < sizeof(mountName->NameLength) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the length of the device name into the output buffer.
|
|
// If the buffer is big enough, write the name, too.
|
|
//
|
|
|
|
mountName = Irp->AssociatedIrp.SystemBuffer;
|
|
mountName->NameLength = diskExtension->DeviceName.Length;
|
|
|
|
if ( outputLength < (sizeof(mountName->NameLength) + mountName->NameLength) ) {
|
|
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
info = sizeof(mountName->NameLength);
|
|
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
mountName->Name,
|
|
diskExtension->DeviceName.Buffer,
|
|
mountName->NameLength
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(mountName->NameLength) + mountName->NameLength;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_QUERY_UNIQUE_ID );
|
|
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
ULONG outputLength;
|
|
|
|
outputLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// The output buffer must be at least big enough to hold the
|
|
// length of the unique ID.
|
|
//
|
|
|
|
if ( outputLength < sizeof(uniqueId->UniqueIdLength) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the length of the unique ID into the output buffer.
|
|
// If the buffer is big enough, write the unique ID, too.
|
|
//
|
|
|
|
uniqueId = Irp->AssociatedIrp.SystemBuffer;
|
|
uniqueId->UniqueIdLength = sizeof(diskExtension->DiskGuid);
|
|
|
|
if ( outputLength <
|
|
(sizeof(uniqueId->UniqueIdLength) + uniqueId->UniqueIdLength) ) {
|
|
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
info = sizeof(uniqueId->UniqueIdLength);
|
|
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
uniqueId->UniqueId,
|
|
&diskExtension->DiskGuid,
|
|
uniqueId->UniqueIdLength
|
|
);
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(uniqueId->UniqueIdLength) + uniqueId->UniqueIdLength;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_QUERY_STABLE_GUID );
|
|
|
|
{
|
|
PMOUNTDEV_STABLE_GUID stableGuid;
|
|
ULONG outputLength;
|
|
|
|
outputLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// The output buffer must be at big enough to hold the GUID.
|
|
//
|
|
|
|
if ( outputLength < sizeof(MOUNTDEV_STABLE_GUID) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the GUID to the output buffer;
|
|
//
|
|
|
|
stableGuid = Irp->AssociatedIrp.SystemBuffer;
|
|
stableGuid->StableGuid = diskExtension->DiskGuid;
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(MOUNTDEV_STABLE_GUID);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_MEDIA_TYPES:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_MEDIA_TYPES );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_STORAGE_GET_MEDIA_TYPES:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_GET_MEDIA_TYPES );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_DRIVE_GEOMETRY );
|
|
|
|
//
|
|
// Return the drive geometry for the virtual disk. Note that
|
|
// we return values which were made up to suit the disk size.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PDISK_GEOMETRY outputBuffer;
|
|
|
|
outputBuffer = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
outputBuffer->MediaType = diskExtension->Options.Fixed ?
|
|
FixedMedia : RemovableMedia;
|
|
outputBuffer->Cylinders.QuadPart = diskExtension->NumberOfCylinders;
|
|
outputBuffer->TracksPerCylinder = diskExtension->TracksPerCylinder;
|
|
outputBuffer->SectorsPerTrack = diskExtension->SectorsPerTrack;
|
|
outputBuffer->BytesPerSector = diskExtension->BytesPerSector;
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_PAINFUL,
|
|
(" MediaType = %x\n", outputBuffer->MediaType) );
|
|
DBGPRINT( DBG_IOCTL, DBG_VERBOSE,
|
|
(" Cylinders = %x\n", outputBuffer->Cylinders) );
|
|
DBGPRINT( DBG_IOCTL, DBG_VERBOSE,
|
|
(" Tracks/cyl = %x\n", outputBuffer->TracksPerCylinder) );
|
|
DBGPRINT( DBG_IOCTL, DBG_VERBOSE,
|
|
(" Sector/track = %x\n", outputBuffer->SectorsPerTrack) );
|
|
DBGPRINT( DBG_IOCTL, DBG_VERBOSE,
|
|
(" Bytes/sector = %x\n", outputBuffer->BytesPerSector) );
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof( DISK_GEOMETRY );
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_IS_WRITABLE:
|
|
|
|
PRINT_CODE( IOCTL_DISK_IS_WRITABLE );
|
|
|
|
//
|
|
// Indicate whether the disk is write protected.
|
|
//
|
|
|
|
status = diskExtension->Options.Readonly ?
|
|
STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_VERIFY:
|
|
|
|
PRINT_CODE( IOCTL_DISK_VERIFY );
|
|
|
|
{
|
|
PVERIFY_INFORMATION verifyInformation;
|
|
ULONG inputLength;
|
|
ULONGLONG ioOffset;
|
|
ULONG ioLength;
|
|
|
|
inputLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
if ( inputLength < sizeof(VERIFY_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
verifyInformation = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ioOffset = verifyInformation->StartingOffset.QuadPart;
|
|
ioLength = verifyInformation->Length;
|
|
|
|
//
|
|
// If the requested length is 0, we have nothing to do.
|
|
// Otherwise, verify that the request is sector aligned,
|
|
// doesn't wrap, and doesn't extend beyond the length of
|
|
// the disk. If the request is valid, just return success.
|
|
//
|
|
|
|
if ( ioLength == 0 ) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else if ( ((ioOffset + ioLength) < ioOffset) ||
|
|
((ioOffset | ioLength) & (diskExtension->BytesPerSector - 1)) != 0 ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else if ( (ioOffset + ioLength) > diskExtension->DiskLength ) {
|
|
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_DRIVE_LAYOUT );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DRIVE_LAYOUT_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the RAM disk is file-backed, we must send this off to
|
|
// the thread for processing, because it requires reading
|
|
// from the disk image.
|
|
//
|
|
|
|
if ( !RAMDISK_IS_FILE_BACKED(diskExtension->DiskType) ) {
|
|
|
|
status = RamdiskGetDriveLayout( Irp, diskExtension );
|
|
info = Irp->IoStatus.Information;
|
|
|
|
} else {
|
|
|
|
status = SendIrpToThread( DeviceObject, Irp );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_PARTITION_INFO:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_PARTITION_INFO );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(PARTITION_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the RAM disk is file-backed, we must send this off to
|
|
// the thread for processing, because it requires reading
|
|
// from the disk image.
|
|
//
|
|
|
|
if ( !RAMDISK_IS_FILE_BACKED(diskExtension->DiskType) ) {
|
|
|
|
status = RamdiskGetPartitionInfo( Irp, diskExtension );
|
|
info = Irp->IoStatus.Information;
|
|
|
|
} else {
|
|
|
|
status = SendIrpToThread( DeviceObject, Irp );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_GET_LENGTH_INFO:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_LENGTH_INFO );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(GET_LENGTH_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PGET_LENGTH_INFORMATION outputBuffer;
|
|
|
|
outputBuffer = (PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
outputBuffer->Length.QuadPart =
|
|
diskExtension->NumberOfCylinders *
|
|
diskExtension->TracksPerCylinder *
|
|
diskExtension->SectorsPerTrack *
|
|
diskExtension->BytesPerSector;
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(GET_LENGTH_INFORMATION);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_STORAGE_GET_DEVICE_NUMBER:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_GET_DEVICE_NUMBER );
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(STORAGE_DEVICE_NUMBER) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PSTORAGE_DEVICE_NUMBER outputBuffer;
|
|
|
|
outputBuffer = (PSTORAGE_DEVICE_NUMBER)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//outputBuffer->DeviceType = FILE_DEVICE_VIRTUAL_DISK;
|
|
outputBuffer->DeviceType = FILE_DEVICE_DISK;
|
|
outputBuffer->DeviceNumber = diskExtension->DiskNumber;
|
|
outputBuffer->PartitionNumber = -1;
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(STORAGE_DEVICE_NUMBER);
|
|
}
|
|
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_SET_PARTITION_INFO:
|
|
|
|
PRINT_CODE( IOCTL_DISK_SET_PARTITION_INFO );
|
|
|
|
//
|
|
// Set information about the partition.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SET_PARTITION_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the RAM disk is file-backed, we must send this off to
|
|
// the thread for processing, because it requires writing
|
|
// to the disk image.
|
|
//
|
|
|
|
if ( !RAMDISK_IS_FILE_BACKED(diskExtension->DiskType) ) {
|
|
|
|
status = RamdiskSetPartitionInfo( Irp, diskExtension );
|
|
info = Irp->IoStatus.Information;
|
|
|
|
} else {
|
|
|
|
status = SendIrpToThread( DeviceObject, Irp );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT:
|
|
|
|
PRINT_CODE( IOCTL_DISK_SET_DRIVE_LAYOUT );
|
|
|
|
//
|
|
// Haven't seen this one come down yet. Set a breakpoint so that
|
|
// if it does come down, we can verify that this code works.
|
|
//
|
|
|
|
UNRECOGNIZED_IOCTL_BREAK;
|
|
|
|
//
|
|
// Return the default error.
|
|
//
|
|
|
|
break;
|
|
|
|
case IOCTL_STORAGE_QUERY_PROPERTY:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_QUERY_PROPERTY );
|
|
|
|
//
|
|
// Call RamdiskQueryProperty() to handle the request. This routine
|
|
// releases the lock. It also takes care of IRP completion.
|
|
//
|
|
|
|
status = RamdiskQueryProperty( DeviceObject, Irp );
|
|
|
|
lockHeld = FALSE;
|
|
calleeWillComplete = TRUE;
|
|
|
|
break;
|
|
|
|
case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
|
|
|
|
PRINT_CODE( IOCTL_VOLUME_GET_GPT_ATTRIBUTES );
|
|
|
|
//
|
|
// Return disk attributes.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION outputBuffer;
|
|
|
|
outputBuffer = (PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION)
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
outputBuffer->GptAttributes = 0;
|
|
if ( diskExtension->Options.Readonly ) {
|
|
outputBuffer->GptAttributes |= GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY;
|
|
}
|
|
if ( diskExtension->Options.NoDriveLetter ) {
|
|
outputBuffer->GptAttributes |= GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER;
|
|
}
|
|
if ( diskExtension->Options.Hidden ) {
|
|
outputBuffer->GptAttributes |= GPT_BASIC_DATA_ATTRIBUTE_HIDDEN;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(VOLUME_GET_GPT_ATTRIBUTES_INFORMATION);
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_VOLUME_SET_GPT_ATTRIBUTES:
|
|
|
|
PRINT_CODE( IOCTL_VOLUME_SET_GPT_ATTRIBUTES );
|
|
|
|
//
|
|
// Set disk attributes.
|
|
//
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(VOLUME_SET_GPT_ATTRIBUTES_INFORMATION) ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
PVOLUME_SET_GPT_ATTRIBUTES_INFORMATION inputBuffer;
|
|
|
|
inputBuffer = (PVOLUME_SET_GPT_ATTRIBUTES_INFORMATION)
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if ( diskExtension->Options.Hidden ) {
|
|
|
|
if ( (inputBuffer->GptAttributes & GPT_BASIC_DATA_ATTRIBUTE_HIDDEN) == 0 ) {
|
|
|
|
diskExtension->Options.Hidden = FALSE;
|
|
status = IoSetDeviceInterfaceState(
|
|
&diskExtension->InterfaceString,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( (inputBuffer->GptAttributes & GPT_BASIC_DATA_ATTRIBUTE_HIDDEN) != 0 ) {
|
|
|
|
diskExtension->Options.Hidden = TRUE;
|
|
status = IoSetDeviceInterfaceState(
|
|
&diskExtension->InterfaceString,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
|
|
|
|
PRINT_CODE( IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS );
|
|
|
|
//
|
|
// We only support this for volume-emulating RAM disks. For
|
|
// disk-emulating RAM disks, this IOCTL should be handled by
|
|
// higher layers.
|
|
//
|
|
|
|
if ( diskExtension->DiskType == RAMDISK_TYPE_FILE_BACKED_DISK ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
} else if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(VOLUME_DISK_EXTENTS) ) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else {
|
|
|
|
PVOLUME_DISK_EXTENTS inputBuffer;
|
|
|
|
inputBuffer = (PVOLUME_DISK_EXTENTS)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
inputBuffer->NumberOfDiskExtents = 1;
|
|
inputBuffer->Extents[0].DiskNumber = (ULONG)-1;
|
|
inputBuffer->Extents[0].StartingOffset.QuadPart = 0;
|
|
inputBuffer->Extents[0].ExtentLength.QuadPart = diskExtension->DiskLength;
|
|
|
|
status = STATUS_SUCCESS;
|
|
info = sizeof(VOLUME_DISK_EXTENTS);
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// The following codes return success without doing anything.
|
|
//
|
|
|
|
case IOCTL_DISK_CHECK_VERIFY:
|
|
|
|
PRINT_CODE( IOCTL_DISK_CHECK_VERIFY );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_STORAGE_CHECK_VERIFY:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_CHECK_VERIFY );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_STORAGE_CHECK_VERIFY2:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_CHECK_VERIFY2 );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_VOLUME_ONLINE:
|
|
|
|
PRINT_CODE( IOCTL_VOLUME_ONLINE );
|
|
|
|
//
|
|
// Return STATUS_SUCCESS without actually doing anything.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
//
|
|
// The following codes return the default error.
|
|
//
|
|
|
|
case FT_BALANCED_READ_MODE:
|
|
|
|
PRINT_CODE( FT_BALANCED_READ_MODE );
|
|
|
|
// Fall through.
|
|
|
|
case FT_PRIMARY_READ:
|
|
|
|
PRINT_CODE( FT_PRIMARY_READ );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_DRIVE_LAYOUT_EX );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_DISK_GET_PARTITION_INFO_EX:
|
|
|
|
PRINT_CODE( IOCTL_DISK_GET_PARTITION_INFO_EX );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_DISK_MEDIA_REMOVAL:
|
|
|
|
PRINT_CODE( IOCTL_DISK_MEDIA_REMOVAL );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_MOUNTDEV_LINK_CREATED:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_LINK_CREATED );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY:
|
|
|
|
PRINT_CODE( IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_SCSI_GET_ADDRESS:
|
|
|
|
PRINT_CODE( IOCTL_SCSI_GET_ADDRESS );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS:
|
|
|
|
PRINT_CODE( IOCTL_VOLSNAP_QUERY_NAMES_OF_SNAPSHOTS );
|
|
|
|
// Fall through.
|
|
|
|
case IOCTL_STORAGE_GET_HOTPLUG_INFO:
|
|
|
|
PRINT_CODE( IOCTL_STORAGE_GET_HOTPLUG_INFO );
|
|
|
|
//
|
|
// Return the default error.
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// The specified I/O control code is unrecognized by this driver.
|
|
// The I/O status field in the IRP has already been set, so just
|
|
// terminate the switch.
|
|
//
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR, ("Ramdisk: ERROR: unrecognized IOCTL %x\n",
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
|
|
UNRECOGNIZED_IOCTL_BREAK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Can't get here. Return the default error if the impossible occurs.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Release the remove lock, if it's still held.
|
|
//
|
|
|
|
if ( lockHeld ) {
|
|
IoReleaseRemoveLock( &commonExtension->RemoveLock, Irp );
|
|
}
|
|
|
|
//
|
|
// If we didn't call another routine that owns completing the IRP, and
|
|
// we didn't send the IRP off to the thread for processing, complete the
|
|
// IRP now.
|
|
//
|
|
|
|
if ( !calleeWillComplete && (status != STATUS_PENDING) ) {
|
|
|
|
COMPLETE_REQUEST( status, info, Irp );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // RamdiskDeviceControl
|
|
|
|
NTSTATUS
|
|
RamdiskCreateRamDisk (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN BOOLEAN AccessCheckOnly
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle an FSCTL_CREATE_RAM_DISK IRP. It is called
|
|
in thread context.
|
|
|
|
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
|
|
|
|
AccessCheckOnly - If FALSE, create the RAM disk. Otherwise, just check
|
|
whether the caller has the necessary access rights to create the disk.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG status;
|
|
PBUS_EXTENSION busExtension;
|
|
PDISK_EXTENSION diskExtension;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PRAMDISK_CREATE_INPUT createInput;
|
|
ULONG inputLength;
|
|
PWCHAR p;
|
|
PWCHAR pMax;
|
|
PLOADER_PARAMETER_BLOCK loaderBlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// The target device object for the I/O is our bus FDO.
|
|
//
|
|
|
|
busExtension = DeviceObject->DeviceExtension;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
//
|
|
// Verify that the input parameter buffer is big enough.
|
|
//
|
|
|
|
inputLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
createInput = (PRAMDISK_CREATE_INPUT)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if ( inputLength < sizeof(RAMDISK_CREATE_INPUT) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( createInput->Version != sizeof(RAMDISK_CREATE_INPUT) ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Verify that the disk type is valid. VIRTUAL_FLOPPY disks can only be
|
|
// created via the registry, and then only during textmode setup.
|
|
// A BOOT_DISK can only be created by the kernel early in the boot
|
|
// process -- when the loader block still exists.
|
|
//
|
|
// ISSUE: If the kernel/driver interface for creating the boot disk is
|
|
// changed, change this test to disallow RAMDISK_TYPE_BOOT_DISK.
|
|
//
|
|
|
|
if ( createInput->DiskType == RAMDISK_TYPE_VIRTUAL_FLOPPY ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
} else if ( createInput->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
|
|
|
|
loaderBlock = *(PLOADER_PARAMETER_BLOCK *)KeLoaderBlock;
|
|
|
|
if ( loaderBlock == NULL ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verify that file name string (if present) is properly terminated.
|
|
//
|
|
|
|
if ( RAMDISK_IS_FILE_BACKED(createInput->DiskType) ) {
|
|
|
|
pMax = (PWCHAR)((PUCHAR)createInput + inputLength);
|
|
p = createInput->FileName;
|
|
|
|
while ( p < pMax ) {
|
|
|
|
if ( *p == 0 ) {
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
if ( p == pMax ) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call RamdiskCreateDiskDevice to create the device. If successful, this
|
|
// returns a pointer to the new disk PDO's device extension.
|
|
//
|
|
|
|
status = RamdiskCreateDiskDevice( busExtension, createInput, AccessCheckOnly, &diskExtension );
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Tell PnP that we need to reenumerate our bus.
|
|
//
|
|
|
|
IoInvalidateDeviceRelations( busExtension->Pdo, BusRelations );
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // RamdiskCreateRamDisk
|
|
|
|
NTSTATUS
|
|
RamdiskCreateDiskDevice (
|
|
IN PBUS_EXTENSION BusExtension,
|
|
IN PRAMDISK_CREATE_INPUT CreateInput,
|
|
IN BOOLEAN AccessCheckOnly,
|
|
OUT PDISK_EXTENSION *DiskExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the work to create a new RAM disk device. It is called
|
|
in thread context.
|
|
|
|
Arguments:
|
|
|
|
BusExtension - a pointer to the device extension for the bus FDO
|
|
|
|
CreateInput - a pointer to the desired parameters for the new RAM disk
|
|
|
|
AccessCheckOnly - If FALSE, create the RAM disk. Otherwise, just check
|
|
whether the caller has the necessary access rights to create the disk.
|
|
|
|
DiskExtension - returns a pointer to the new disk PDO's device extension
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG status;
|
|
ULONG_PTR basePage;
|
|
PVOID baseAddress;
|
|
ULONG i;
|
|
ULONG deviceExtensionSize;
|
|
PDEVICE_OBJECT newDeviceObject;
|
|
WCHAR buffer[15];
|
|
UNICODE_STRING guidString;
|
|
UNICODE_STRING realDeviceName;
|
|
PDISK_EXTENSION diskExtension;
|
|
HANDLE fileHandle;
|
|
HANDLE sectionHandle;
|
|
PVOID sectionObject;
|
|
NTSTATUS ntStatus;
|
|
PVOID viewBase;
|
|
SIZE_T viewSize;
|
|
LARGE_INTEGER sectionOffset;
|
|
UNICODE_STRING string;
|
|
OBJECT_ATTRIBUTES obja;
|
|
IO_STATUS_BLOCK iosb;
|
|
UNICODE_STRING dosSymLink;
|
|
FILE_STANDARD_INFORMATION fileInfo;
|
|
PVIEW viewDescriptors = NULL;
|
|
HRESULT result;
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
ULONG diskNumber;
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize local variables to prepare for exit cleanup.
|
|
//
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
diskNumber = 0xffffffff;
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
fileHandle = NULL;
|
|
sectionHandle = NULL;
|
|
sectionObject = NULL;
|
|
viewDescriptors = NULL;
|
|
guidString.Buffer = NULL;
|
|
realDeviceName.Buffer = NULL;
|
|
dosSymLink.Buffer = NULL;
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
|
|
if ( !AccessCheckOnly ) {
|
|
|
|
//
|
|
// Allocate a disk number.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutex( &BusExtension->Mutex );
|
|
|
|
diskNumber = RtlFindClearBitsAndSet( &BusExtension->DiskNumbersBitmap, 1, 0 );
|
|
|
|
ExReleaseFastMutex( &BusExtension->Mutex );
|
|
KeLeaveCriticalRegion();
|
|
|
|
if ( diskNumber == 0xffffffff ) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Convert the zero-based bit number to a one-based disk number.
|
|
//
|
|
|
|
diskNumber++;
|
|
}
|
|
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
//
|
|
// Initialize based on the disk type (file-backed or in-memory).
|
|
//
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_INFO,
|
|
("RamdiskCreateDiskDevice: Creating disk with length 0x%08x\n",
|
|
CreateInput->DiskLength) );
|
|
|
|
sectionObject = NULL;
|
|
basePage = 0;
|
|
baseAddress = NULL;
|
|
|
|
if ( RAMDISK_IS_FILE_BACKED(CreateInput->DiskType) ) {
|
|
|
|
//
|
|
// This is a file-backed RAM disk. Open the backing file. Note that
|
|
// we do NOT create the file here if it doesn't exist. It is up to
|
|
// the caller to handle that.
|
|
//
|
|
|
|
RtlInitUnicodeString( &string, CreateInput->FileName );
|
|
InitializeObjectAttributes( &obja, &string, OBJ_CASE_INSENSITIVE, NULL, NULL );
|
|
|
|
status = IoCreateFile(
|
|
&fileHandle,
|
|
SYNCHRONIZE | FILE_READ_DATA | FILE_READ_ATTRIBUTES |
|
|
(CreateInput->Options.Readonly ? 0 : FILE_WRITE_DATA),
|
|
&obja,
|
|
&iosb,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
0,
|
|
NULL,
|
|
0,
|
|
CreateFileTypeNone,
|
|
NULL,
|
|
(AccessCheckOnly ? IO_FORCE_ACCESS_CHECK | IO_NO_PARAMETER_CHECKING : 0)
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: Can't open target file %ws: %x\n",
|
|
CreateInput->FileName, status) );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if ( AccessCheckOnly ) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get the size of the file.
|
|
//
|
|
|
|
status = ZwQueryInformationFile(
|
|
fileHandle,
|
|
&iosb,
|
|
&fileInfo,
|
|
sizeof(fileInfo),
|
|
FileStandardInformation
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: Can't query info for file %ws: %x\n",
|
|
CreateInput->FileName, status) );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Verify that the file is long enough for the specified DiskOffset
|
|
// and DiskLength.
|
|
//
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_INFO, ("RamdiskCreateDiskDevice: file size = %I64x\n",
|
|
fileInfo.EndOfFile.QuadPart) );
|
|
|
|
if ( (CreateInput->DiskOffset + CreateInput->DiskLength) >
|
|
(ULONGLONG)fileInfo.EndOfFile.QuadPart ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: specified offset and length too big for file:"
|
|
" 0x%x + 0x%I64x > 0x%I64x\n",
|
|
CreateInput->DiskOffset, CreateInput->DiskLength,
|
|
fileInfo.EndOfFile.QuadPart) );
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create a section for the file. Close the file handle.
|
|
//
|
|
|
|
status = ZwCreateSection(
|
|
§ionHandle,
|
|
SECTION_ALL_ACCESS,
|
|
NULL,
|
|
0,
|
|
(CreateInput->Options.Readonly ? PAGE_READONLY : PAGE_READWRITE),
|
|
SEC_COMMIT,
|
|
fileHandle
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: Can't create section for %ws: %x\n",
|
|
CreateInput->FileName, status) );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
NtClose( fileHandle );
|
|
fileHandle = NULL;
|
|
|
|
//
|
|
// Get a referenced pointer to the section object. Close the section.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
sectionHandle,
|
|
SECTION_ALL_ACCESS,
|
|
*(POBJECT_TYPE *)MmSectionObjectType,
|
|
KernelMode,
|
|
§ionObject,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: Can't reference section for %ws: %x\n",
|
|
CreateInput->FileName, status) );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
NtClose( sectionHandle );
|
|
sectionHandle = NULL;
|
|
|
|
//
|
|
// Allocate space for view descriptors. First, get the number of views
|
|
// to use and the size of each view.
|
|
//
|
|
|
|
if ( CreateInput->ViewCount == 0 ) {
|
|
CreateInput->ViewCount = DefaultViewCount;
|
|
} else if ( CreateInput->ViewCount < MinimumViewCount ) {
|
|
CreateInput->ViewCount = MinimumViewCount;
|
|
} else if ( CreateInput->ViewCount > MaximumViewCount ) {
|
|
CreateInput->ViewCount = MaximumViewCount;
|
|
}
|
|
|
|
if ( CreateInput->ViewLength == 0 ) {
|
|
CreateInput->ViewLength = DefaultViewLength;
|
|
} else if ( CreateInput->ViewLength < MinimumViewLength ) {
|
|
CreateInput->ViewLength = MinimumViewLength;
|
|
} else if ( CreateInput->ViewLength > MaximumViewLength ) {
|
|
CreateInput->ViewLength = MaximumViewLength;
|
|
}
|
|
|
|
//
|
|
// Ensure that the total view length is not greater than the maximum
|
|
// per-disk view length. If necessary, decrease the view count until
|
|
// the total view length is low enough. If the view count reaches the
|
|
// configured minimum, reduce the length of each view until the total
|
|
// view length is low enough.
|
|
//
|
|
// It is possible for the administrator to configure the minimum view
|
|
// count, minimum view length, and maximum per-disk view length such
|
|
// that it's impossible for the minimum total view length to be less
|
|
// than the maximum per-disk view length. (That is, the miinimum view
|
|
// count and minimum view length are configured to high relative to
|
|
// the configured maximum per-disk view length.) If this occurs, we
|
|
// create the disk with the compile-time defaults instead.
|
|
//
|
|
|
|
while ( ((ULONGLONG)CreateInput->ViewCount * CreateInput->ViewLength) >
|
|
MaximumPerDiskViewLength ) {
|
|
|
|
//
|
|
// The total view length is too big. If possible, cut the number of
|
|
// views in half.
|
|
//
|
|
|
|
if ( CreateInput->ViewCount > MinimumViewCount ) {
|
|
|
|
//
|
|
// The view count isn't at the minimum. Cut in half, but don't
|
|
// go below the minimum.
|
|
//
|
|
|
|
CreateInput->ViewCount /= 2;
|
|
if ( CreateInput->ViewCount < MinimumViewCount ) {
|
|
CreateInput->ViewCount = MinimumViewCount;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The view count is already at the minimum. If possible,
|
|
// cut the view length in half.
|
|
//
|
|
|
|
if ( CreateInput->ViewLength > MinimumViewLength ) {
|
|
|
|
//
|
|
// The view length isn't at the minimum. Cut in half, but
|
|
// don't go below the minimum.
|
|
//
|
|
|
|
CreateInput->ViewLength /= 2;
|
|
if ( CreateInput->ViewLength < MinimumViewLength ) {
|
|
CreateInput->ViewLength = MinimumViewLength;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// At this point, the view count and the view length are
|
|
// both at the minimum allowed values, but the total view
|
|
// length is beyond the maximum per-disk value. Use the
|
|
// compile-time default values instead. Note that this will
|
|
// result in a total view length that is equal to the
|
|
// minimum allowed maximum per-disk view length, at least
|
|
// given the compile-time values as of this writing.
|
|
//
|
|
|
|
CreateInput->ViewCount = DEFAULT_DEFAULT_VIEW_COUNT;
|
|
CreateInput->ViewLength = DEFAULT_DEFAULT_VIEW_LENGTH;
|
|
ASSERT( ((ULONGLONG)CreateInput->ViewCount * CreateInput->ViewLength) <=
|
|
MaximumPerDiskViewLength );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
viewDescriptors = ALLOCATE_POOL(
|
|
PagedPool,
|
|
CreateInput->ViewCount * sizeof(VIEW),
|
|
TRUE );
|
|
|
|
if ( viewDescriptors == NULL ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("%s", "RamdiskCreateDiskDevice: Can't allocate pool for view descriptors\n") );
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
RtlZeroMemory( viewDescriptors, CreateInput->ViewCount * sizeof(VIEW) );
|
|
|
|
} else if ( CreateInput->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
|
|
|
|
//
|
|
// For a boot disk, the input parameter buffer tells us where the
|
|
// image is in physical memory, and how big the image is.
|
|
//
|
|
|
|
basePage = CreateInput->BasePage;
|
|
|
|
if ( basePage == 0 ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("%s", "RamdiskCreateDiskDevice: Base page for boot disk is 0?!?\n") );
|
|
|
|
ASSERT( FALSE );
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Force options to the appropriate values for a boot disk.
|
|
//
|
|
|
|
CreateInput->Options.Fixed = TRUE;
|
|
CreateInput->Options.Readonly = FALSE;
|
|
CreateInput->Options.NoDriveLetter = FALSE;
|
|
CreateInput->Options.NoDosDevice = FALSE;
|
|
CreateInput->Options.Hidden = FALSE;
|
|
|
|
} else if ( CreateInput->DiskType == RAMDISK_TYPE_VIRTUAL_FLOPPY ) {
|
|
|
|
//
|
|
// For a virtual floppy, the input parameter buffer tells us where the
|
|
// image is in virtual memory, and how big the image is.
|
|
//
|
|
|
|
baseAddress = CreateInput->BaseAddress;
|
|
|
|
ASSERT( baseAddress != NULL );
|
|
|
|
//
|
|
// Force options to the appropriate values for a virtual floppy.
|
|
//
|
|
|
|
CreateInput->Options.Fixed = TRUE;
|
|
CreateInput->Options.Readonly = FALSE;
|
|
CreateInput->Options.NoDriveLetter = TRUE;
|
|
CreateInput->Options.NoDosDevice = FALSE;
|
|
CreateInput->Options.Hidden = FALSE;
|
|
|
|
} else {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: Bad disk type %d\n", CreateInput->DiskType) );
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if ( AccessCheckOnly ) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT( (basePage != 0) || (sectionObject != NULL) || (baseAddress != NULL) );
|
|
|
|
//
|
|
// Create a name for the disk, based on the disk GUID. For all disk types
|
|
// except VIRTUAL_FLOPPY, the name is of the form \Device\Ramdisk{guid}.
|
|
// For VIRTUAL_FLOPPY, the name is of the form \Device\RamdiskN, when N is
|
|
// specified by the Data1 field of the GUID.
|
|
//
|
|
|
|
if ( CreateInput->DiskType != RAMDISK_TYPE_VIRTUAL_FLOPPY ) {
|
|
|
|
status = RtlStringFromGUID( &CreateInput->DiskGuid, &guidString );
|
|
|
|
} else {
|
|
|
|
// This variable is here to keep PREfast quiet (PREfast warning 209).
|
|
size_t size = sizeof(buffer);
|
|
|
|
result = StringCbPrintfW( buffer, size, L"%u", CreateInput->DiskGuid.Data1 );
|
|
ASSERT( result == S_OK );
|
|
|
|
status = RtlCreateUnicodeString( &guidString, buffer );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) || (guidString.Buffer == NULL) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("%s", "RamdiskCreateDiskDevice: can't allocate pool for pretty GUID\n") );
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
i = sizeof(RAMDISK_DEVICENAME) + guidString.Length;
|
|
|
|
realDeviceName.Buffer = ALLOCATE_POOL( NonPagedPool, i, TRUE );
|
|
|
|
if ( (realDeviceName.Buffer == NULL) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("%s", "RamdiskCreateDiskDevice: can't allocate pool for device name\n") );
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
realDeviceName.MaximumLength = (USHORT)i;
|
|
realDeviceName.Length = realDeviceName.MaximumLength - sizeof(WCHAR);
|
|
|
|
result = StringCbCopyW( realDeviceName.Buffer, i, RAMDISK_DEVICENAME );
|
|
ASSERT( result == S_OK );
|
|
result = StringCbCatW( realDeviceName.Buffer, i, guidString.Buffer );
|
|
ASSERT( result == S_OK );
|
|
ASSERT( (wcslen(realDeviceName.Buffer) * sizeof(WCHAR)) == realDeviceName.Length );
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_INFO,
|
|
("RamdiskCreateDiskDevice: Device name is %wZ\n", &realDeviceName) );
|
|
|
|
//
|
|
// Create the RAM disk device.
|
|
//
|
|
// ISSUE: Apply an ACL to the disk device object. (Or does the next issue obviate this?)
|
|
// ISSUE: Should we use an autogenerated name for the PDO? This would mean
|
|
// that we'd have to return the device name as the IOCTL output,
|
|
// not just the disk number.
|
|
//
|
|
|
|
deviceExtensionSize = sizeof(DISK_EXTENSION);
|
|
if ( RAMDISK_IS_FILE_BACKED(CreateInput->DiskType) ) {
|
|
deviceExtensionSize += wcslen( CreateInput->FileName ) * sizeof(WCHAR);
|
|
}
|
|
|
|
status = IoCreateDevice(
|
|
BusExtension->Fdo->DriverObject,
|
|
deviceExtensionSize,
|
|
&realDeviceName,
|
|
FILE_DEVICE_DISK,
|
|
CreateInput->Options.Fixed ? 0 : FILE_REMOVABLE_MEDIA, // | FILE_VIRTUAL_VOLUME,
|
|
FALSE,
|
|
&newDeviceObject
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: IoCreateDevice failed: %x\n", status) );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
diskExtension = newDeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Create a DosDevices link for the device.
|
|
//
|
|
|
|
if ( !CreateInput->Options.NoDosDevice ) {
|
|
|
|
//
|
|
// Create a DosDevices symbolic link. Ignore errors.
|
|
//
|
|
|
|
i = sizeof(RAMDISK_FULL_DOSNAME) + guidString.Length;
|
|
|
|
dosSymLink.MaximumLength = (USHORT)i;
|
|
dosSymLink.Length = dosSymLink.MaximumLength - sizeof(WCHAR);
|
|
|
|
dosSymLink.Buffer = ALLOCATE_POOL( NonPagedPool, i, TRUE );
|
|
|
|
if ( dosSymLink.Buffer == NULL ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("%s", "RamdiskCreateDiskDevice: can't allocate pool for DosDevices name\n") );
|
|
|
|
CreateInput->Options.NoDosDevice = TRUE;
|
|
|
|
} else {
|
|
|
|
result = StringCbCopyW( dosSymLink.Buffer, i, RAMDISK_FULL_DOSNAME );
|
|
ASSERT( result == S_OK );
|
|
result = StringCbCatW( dosSymLink.Buffer, i, guidString.Buffer );
|
|
ASSERT( result == S_OK );
|
|
ASSERT( (wcslen(dosSymLink.Buffer) * sizeof(WCHAR)) == dosSymLink.Length );
|
|
|
|
status = IoCreateSymbolicLink( &dosSymLink, &realDeviceName );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
DBGPRINT( DBG_IOCTL, DBG_ERROR,
|
|
("RamdiskCreateDiskDevice: IoCreateSymbolicLink failed: %x\n", status) );
|
|
|
|
CreateInput->Options.NoDosDevice = TRUE;
|
|
|
|
FREE_POOL( dosSymLink.Buffer, TRUE );
|
|
dosSymLink.Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If creating the boot disk, create a drive letter.
|
|
//
|
|
|
|
if ( CreateInput->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
|
|
|
|
// This variable is here to keep PREfast quiet (PREfast warning 209).
|
|
size_t size = sizeof(buffer);
|
|
|
|
result = StringCbPrintfW(
|
|
buffer,
|
|
size,
|
|
L"\\DosDevices\\%wc:",
|
|
CreateInput->DriveLetter
|
|
);
|
|
ASSERT( result == S_OK );
|
|
RtlInitUnicodeString( &string, buffer );
|
|
IoDeleteSymbolicLink( &string );
|
|
IoCreateSymbolicLink( &string, &realDeviceName );
|
|
|
|
diskExtension->DriveLetter = CreateInput->DriveLetter;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize device object and extension.
|
|
//
|
|
|
|
//
|
|
// Our device does direct I/O, is XIP-capable, and is power pageable.
|
|
// We require word alignment for I/O.
|
|
//
|
|
|
|
newDeviceObject->Flags |= DO_DIRECT_IO | DO_XIP | DO_POWER_PAGABLE;
|
|
newDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
|
|
|
|
//
|
|
// Return a pointer to the device extension to the caller.
|
|
//
|
|
|
|
*DiskExtension = diskExtension;
|
|
|
|
//
|
|
// Set the device type and state in the device extension. Initialize the
|
|
// fast mutex and the remove lock. Save the device name string.
|
|
//
|
|
|
|
diskExtension->DeviceType = RamdiskDeviceTypeDiskPdo;
|
|
diskExtension->DeviceState = RamdiskDeviceStateStopped;
|
|
|
|
ExInitializeFastMutex( &diskExtension->Mutex );
|
|
IoInitializeRemoveLock( &diskExtension->RemoveLock, 'dmaR', 1, 0 );
|
|
|
|
diskExtension->DeviceName = realDeviceName;
|
|
realDeviceName.Buffer = NULL;
|
|
diskExtension->DosSymLink = dosSymLink;
|
|
dosSymLink.Buffer = NULL;
|
|
|
|
diskExtension->DiskGuid = CreateInput->DiskGuid;
|
|
diskExtension->DiskGuidFormatted = guidString;
|
|
guidString.Buffer = NULL;
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
|
|
//
|
|
// Save the disk number.
|
|
//
|
|
|
|
diskExtension->DiskNumber = diskNumber;
|
|
diskNumber = 0xffffffff;
|
|
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
//
|
|
// Save object pointers. The PDO for this extension is the device
|
|
// extension is the device object that we just created. The FDO and
|
|
// the lower device object are the bus FDO.
|
|
//
|
|
|
|
diskExtension->Pdo = newDeviceObject;
|
|
diskExtension->Fdo = RamdiskBusFdo;
|
|
diskExtension->LowerDeviceObject = RamdiskBusFdo;
|
|
|
|
//
|
|
// ISSUE: What about BiosParameters?
|
|
//
|
|
//bios = &diskExtension->BiosParameters;
|
|
//
|
|
//diskExtension->BootParameters = xipbootparameters;
|
|
//status = RamdDispatch(XIPCMD_GETBIOSPARAMETERS, bios, sizeof(*bios));
|
|
|
|
|
|
//
|
|
// Save pointers to the disk image.
|
|
//
|
|
|
|
diskExtension->BasePage = basePage;
|
|
diskExtension->SectionObject = sectionObject;
|
|
sectionObject = NULL;
|
|
diskExtension->BaseAddress = baseAddress;
|
|
|
|
if ( RAMDISK_IS_FILE_BACKED(CreateInput->DiskType) ) {
|
|
|
|
result = StringCbCopyW(
|
|
diskExtension->FileName,
|
|
deviceExtensionSize,
|
|
CreateInput->FileName
|
|
);
|
|
ASSERT( result == S_OK );
|
|
}
|
|
|
|
//
|
|
// Save the disk type (disk or volume) and disk options.
|
|
//
|
|
|
|
diskExtension->DiskType = CreateInput->DiskType;
|
|
diskExtension->Options = CreateInput->Options;
|
|
|
|
//
|
|
// For a file-backed disk image, set up the view descriptors.
|
|
//
|
|
// ISSUE: Need to consider whether to permanently map the first few pages
|
|
// of the image. The first sector on the disk is accessed frequently, so
|
|
// there is some value in keeping it mapped. But it might not be worth it
|
|
// to waste a view descriptor on this. And the LRU nature of the view
|
|
// replacement algorithm will keep the first sector mapped when necessary.
|
|
//
|
|
|
|
if ( viewDescriptors != NULL ) {
|
|
|
|
PVIEW view;
|
|
|
|
//
|
|
// Initialize windowing fields in the disk extension.
|
|
//
|
|
|
|
diskExtension->ViewCount = CreateInput->ViewCount;
|
|
diskExtension->ViewLength = CreateInput->ViewLength;
|
|
diskExtension->ViewDescriptors = viewDescriptors;
|
|
KeInitializeSemaphore( &diskExtension->ViewSemaphore, 0, MAXLONG );
|
|
diskExtension->ViewWaiterCount = 0;
|
|
|
|
//
|
|
// Initialize the view lists, then insert in each view descriptor
|
|
// in order. The result is a list of descriptors, each unmapped
|
|
// (offset and length both 0).
|
|
//
|
|
|
|
InitializeListHead( &diskExtension->ViewsByOffset );
|
|
InitializeListHead( &diskExtension->ViewsByMru );
|
|
|
|
view = viewDescriptors;
|
|
|
|
for ( i = 0; i < diskExtension->ViewCount; i++ ) {
|
|
|
|
InsertTailList( &diskExtension->ViewsByOffset, &view->ByOffsetListEntry );
|
|
InsertTailList( &diskExtension->ViewsByMru, &view->ByMruListEntry );
|
|
|
|
view++;
|
|
}
|
|
|
|
viewDescriptors = NULL;
|
|
}
|
|
|
|
|
|
diskExtension->DiskLength = CreateInput->DiskLength;
|
|
diskExtension->DiskOffset = CreateInput->DiskOffset;
|
|
diskExtension->FileRelativeEndOfDisk = diskExtension->DiskOffset + diskExtension->DiskLength;
|
|
|
|
//
|
|
// Set the disk geometry. The basic geometry is constant for new disks.
|
|
// For RAMDISK_TYPE_BOOT_DISK type volume geometry can be obtained from the partition boot sector.
|
|
//
|
|
|
|
if ( RAMDISK_TYPE_BOOT_DISK != CreateInput->DiskType ) {
|
|
|
|
diskExtension->BytesPerSector = 0x200;
|
|
diskExtension->SectorsPerTrack = 0x80;
|
|
diskExtension->TracksPerCylinder = 0x10;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// 2002/04/19-SaadSyed (Saad Syed)
|
|
// Added generic geometry support to support SDI files
|
|
//
|
|
PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK BootSector = NULL;
|
|
ULONG mappedLength;
|
|
BIOS_PARAMETER_BLOCK Bpb;
|
|
|
|
//
|
|
// Map boot sector of boot ramdisk
|
|
//
|
|
|
|
BootSector = ( PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK ) RamdiskMapPages( diskExtension, 0, PAGE_SIZE, &mappedLength );
|
|
|
|
if ( NULL != BootSector ) {
|
|
|
|
ASSERT( mappedLength == PAGE_SIZE );
|
|
|
|
UnpackBios( &Bpb, &BootSector->Bpb );
|
|
|
|
//
|
|
// Extract geometry from boot sector bios parameter block
|
|
//
|
|
|
|
|
|
diskExtension->BytesPerSector = Bpb.BytesPerSector;
|
|
diskExtension->SectorsPerTrack = Bpb.SectorsPerTrack;
|
|
diskExtension->TracksPerCylinder = Bpb.Heads;
|
|
|
|
RamdiskUnmapPages(diskExtension, (PUCHAR) BootSector, 0, mappedLength);
|
|
|
|
BootSector = NULL;
|
|
}
|
|
else {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
|
|
}
|
|
}
|
|
|
|
diskExtension->BytesPerCylinder = diskExtension->BytesPerSector *
|
|
diskExtension->SectorsPerTrack *
|
|
diskExtension->TracksPerCylinder;
|
|
|
|
diskExtension->NumberOfCylinders =
|
|
(ULONG)(CreateInput->DiskLength / diskExtension->BytesPerCylinder);
|
|
|
|
//
|
|
// A partition/volume does not often map to a geometry completely, the file system driver limits the
|
|
// volume to a capacity determined by the NumberOfSectors (in the Boot Sector) * BytesPerSector (in BPB).
|
|
// The FS driver uses the this geometry to determine if the NumberOfSectors is less than or
|
|
// equal to the total number of sectors reported by the geometry, otherwise it fails to mount the volume.
|
|
// Here we check if the length of disk as obtained from the ramdisk image is less than or equal to the length
|
|
// obtained by the geometry. We increment the number of cylinders if this check fails. This keeps the FS driver
|
|
// happy. A simple ceil operation would yield the same results.
|
|
//
|
|
|
|
if ( ( diskExtension->BytesPerCylinder *
|
|
diskExtension->NumberOfCylinders ) < CreateInput->DiskLength) {
|
|
|
|
diskExtension->NumberOfCylinders++;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Link the new disk into the bus FDO's list of disks.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutex( &BusExtension->Mutex );
|
|
|
|
InsertTailList( &BusExtension->DiskPdoList, &diskExtension->DiskPdoListEntry );
|
|
|
|
ExReleaseFastMutex( &BusExtension->Mutex );
|
|
KeLeaveCriticalRegion();
|
|
|
|
//
|
|
// Indicate that we're done initializing the device.
|
|
//
|
|
|
|
newDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
goto exit_exit;
|
|
|
|
exit:
|
|
|
|
if ( dosSymLink.Buffer != NULL ) {
|
|
FREE_POOL( dosSymLink.Buffer, TRUE );
|
|
dosSymLink.Buffer = NULL;
|
|
}
|
|
|
|
if ( realDeviceName.Buffer != NULL ) {
|
|
FREE_POOL( realDeviceName.Buffer, TRUE );
|
|
realDeviceName.Buffer = NULL;
|
|
}
|
|
|
|
if ( guidString.Buffer != NULL ) {
|
|
FREE_POOL( guidString.Buffer, FALSE );
|
|
guidString.Buffer = NULL;
|
|
}
|
|
|
|
if ( viewDescriptors != NULL ) {
|
|
FREE_POOL( viewDescriptors, TRUE );
|
|
viewDescriptors = NULL;
|
|
}
|
|
|
|
if ( sectionObject != NULL ) {
|
|
ObDereferenceObject( sectionObject );
|
|
sectionObject = NULL;
|
|
}
|
|
|
|
if ( sectionHandle != NULL ) {
|
|
NtClose( sectionHandle );
|
|
sectionHandle = NULL;
|
|
}
|
|
|
|
if ( fileHandle != NULL ) {
|
|
NtClose( fileHandle );
|
|
fileHandle = NULL;
|
|
}
|
|
|
|
#if SUPPORT_DISK_NUMBERS
|
|
|
|
if ( diskNumber != 0xffffffff ) {
|
|
|
|
KeEnterCriticalRegion();
|
|
ExAcquireFastMutex( &BusExtension->Mutex );
|
|
|
|
RtlClearBit( &BusExtension->DiskNumbersBitmap, diskNumber - 1 );
|
|
|
|
ExReleaseFastMutex( &BusExtension->Mutex );
|
|
KeLeaveCriticalRegion();
|
|
|
|
diskNumber = 0xffffffff;
|
|
}
|
|
|
|
#endif // SUPPORT_DISK_NUMBERS
|
|
|
|
exit_exit:
|
|
|
|
ASSERT( fileHandle == NULL );
|
|
ASSERT( sectionHandle == NULL );
|
|
ASSERT( sectionObject == NULL );
|
|
ASSERT( viewDescriptors == NULL );
|
|
ASSERT( guidString.Buffer == NULL );
|
|
ASSERT( realDeviceName.Buffer == NULL );
|
|
ASSERT( dosSymLink.Buffer == NULL );
|
|
|
|
return status;
|
|
|
|
} // RamdiskCreateDiskDevice
|
|
|
|
NTSTATUS
|
|
RamdiskGetDriveLayout (
|
|
PIRP Irp,
|
|
PDISK_EXTENSION DiskExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle an IOCTL_GET_DRIVE_LAYOUT IRP. It is
|
|
called in thread context iff the disk is file-backed.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the I/O Request Packet for this request
|
|
|
|
DiskExtension - a pointer to the device extension for the target disk
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDRIVE_LAYOUT_INFORMATION driveLayout;
|
|
PPARTITION_INFORMATION partInfo;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
PUCHAR va;
|
|
ULONG mappedLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DRIVE_LAYOUT_INFORMATION) ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Map the first page of the image.
|
|
//
|
|
|
|
va = RamdiskMapPages( DiskExtension, 0, PAGE_SIZE, &mappedLength );
|
|
|
|
if ( va == NULL ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT( mappedLength == PAGE_SIZE );
|
|
|
|
//
|
|
// Get a pointer to the output buffer. Fill in the header.
|
|
//
|
|
|
|
driveLayout = (PDRIVE_LAYOUT_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// ISSUE: Is this right for an emulated disk? Or is this routine never
|
|
// called for emulated disks?
|
|
//
|
|
|
|
driveLayout->PartitionCount = 1;
|
|
driveLayout->Signature = 0;
|
|
|
|
partInfo = driveLayout->PartitionEntry;
|
|
|
|
//
|
|
// Point to the partition table in the disk image.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR)&((PUSHORT)va)[PARTITION_TABLE_OFFSET];
|
|
|
|
//
|
|
// Return information about the partition.
|
|
//
|
|
|
|
partInfo->StartingOffset.QuadPart = DiskExtension->BytesPerSector;
|
|
|
|
partInfo->PartitionLength.QuadPart =
|
|
DiskExtension->NumberOfCylinders *
|
|
DiskExtension->TracksPerCylinder *
|
|
DiskExtension->SectorsPerTrack *
|
|
DiskExtension->BytesPerSector;
|
|
|
|
// ISSUE: Currently, HiddenSectors is always 0. Is this right?
|
|
partInfo->HiddenSectors = DiskExtension->HiddenSectors;
|
|
|
|
partInfo->PartitionNumber = 0;
|
|
partInfo->PartitionType = partitionTableEntry->PartitionType;
|
|
partInfo->BootIndicator = (BOOLEAN)(DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK);
|
|
partInfo->RecognizedPartition = IsRecognizedPartition(partInfo->PartitionType);
|
|
partInfo->RewritePartition = FALSE;
|
|
|
|
//
|
|
// Unmap the mapped page.
|
|
//
|
|
|
|
RamdiskUnmapPages( DiskExtension, va, 0, mappedLength );
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(DRIVE_LAYOUT_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // RamdiskGetDriveLayout
|
|
|
|
NTSTATUS
|
|
RamdiskGetPartitionInfo (
|
|
PIRP Irp,
|
|
PDISK_EXTENSION DiskExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle an IOCTL_GET_PARTITION_INFO IRP. It is
|
|
called in thread context iff the disk is file-backed.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the I/O Request Packet for this request
|
|
|
|
DiskExtension - a pointer to the device extension for the target disk
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PPARTITION_INFORMATION partInfo;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
PUCHAR va;
|
|
ULONG mappedLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARTITION_INFORMATION) ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Map the first page of the image.
|
|
//
|
|
|
|
va = RamdiskMapPages( DiskExtension, 0, PAGE_SIZE, &mappedLength );
|
|
|
|
if ( va == NULL ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT( mappedLength == PAGE_SIZE );
|
|
|
|
//
|
|
// Get a pointer to the output buffer.
|
|
//
|
|
|
|
partInfo = (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Point to the partition table in the disk image.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR)&((PUSHORT)va)[PARTITION_TABLE_OFFSET];
|
|
|
|
//
|
|
// Return information about the partition.
|
|
//
|
|
|
|
partInfo->StartingOffset.QuadPart = DiskExtension->BytesPerSector;
|
|
|
|
partInfo->PartitionLength.QuadPart =
|
|
DiskExtension->NumberOfCylinders *
|
|
DiskExtension->TracksPerCylinder *
|
|
DiskExtension->SectorsPerTrack *
|
|
DiskExtension->BytesPerSector;
|
|
|
|
// ISSUE: Currently, HiddenSectors is always 0. Is this right?
|
|
partInfo->HiddenSectors = DiskExtension->HiddenSectors;
|
|
|
|
partInfo->PartitionNumber = 0;
|
|
partInfo->PartitionType = partitionTableEntry->PartitionType;
|
|
partInfo->BootIndicator = (BOOLEAN)(DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK);
|
|
partInfo->RecognizedPartition = IsRecognizedPartition(partInfo->PartitionType);
|
|
partInfo->RewritePartition = FALSE;
|
|
|
|
//
|
|
// Unmap the mapped page.
|
|
//
|
|
|
|
RamdiskUnmapPages( DiskExtension, va, 0, mappedLength );
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // RamdiskGetPartitionInfo
|
|
|
|
|
|
NTSTATUS
|
|
RamdiskSetPartitionInfo (
|
|
PIRP Irp,
|
|
PDISK_EXTENSION DiskExtension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle an IOCTL_SET_PARTITION_INFO IRP. It is
|
|
called in thread context iff the disk is file-backed.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to the I/O Request Packet for this request
|
|
|
|
DiskExtension - a pointer to the device extension for the target disk
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PSET_PARTITION_INFORMATION partInfo;
|
|
PPARTITION_DESCRIPTOR partitionTableEntry;
|
|
PUCHAR va;
|
|
ULONG mappedLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SET_PARTITION_INFORMATION) ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Map the first page of the image.
|
|
//
|
|
|
|
va = RamdiskMapPages( DiskExtension, 0, PAGE_SIZE, &mappedLength );
|
|
|
|
if ( va == NULL ) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ASSERT( mappedLength == PAGE_SIZE );
|
|
|
|
//
|
|
// Get a pointer to the output buffer.
|
|
//
|
|
|
|
partInfo = (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Point to the partition table in the disk image.
|
|
//
|
|
|
|
partitionTableEntry = (PPARTITION_DESCRIPTOR)&((PUSHORT)va)[PARTITION_TABLE_OFFSET];
|
|
|
|
//
|
|
// Write the new partition type.
|
|
//
|
|
|
|
partitionTableEntry->PartitionType = partInfo->PartitionType;
|
|
|
|
//
|
|
// Unmap the mapped page.
|
|
//
|
|
|
|
RamdiskUnmapPages( DiskExtension, va, 0, mappedLength );
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // RamdiskGetPartitionInfo
|
|
|
|
NTSTATUS
|
|
RamdiskQueryProperty (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles a property query request. It builds the descriptor
|
|
on its own if possible, otherwise it forwards the request down to lower
|
|
level drivers.
|
|
|
|
Since this routine may forward the request downwards the caller should
|
|
not complete the IRP.
|
|
|
|
This routine must be called with the remove lock held. The lock is
|
|
released when this routine returns.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - a pointer to the device object being queried
|
|
|
|
Irp - a pointer to the IRP for the query
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the status of the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PSTORAGE_PROPERTY_QUERY query;
|
|
ULONG queryLength;
|
|
PSTORAGE_DEVICE_DESCRIPTOR storageDeviceDescriptor;
|
|
PSTORAGE_ADAPTER_DESCRIPTOR storageAdapterDescriptor;
|
|
ULONG offset;
|
|
ULONG length;
|
|
PUCHAR p;
|
|
|
|
PCOMMON_EXTENSION commonExtension;
|
|
PBUS_EXTENSION busExtension;
|
|
PDISK_EXTENSION diskExtension;
|
|
|
|
STORAGE_DEVICE_DESCRIPTOR sdd;
|
|
STORAGE_ADAPTER_DESCRIPTOR sad;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Assume success.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Get parameters from the IRP.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
|
|
queryLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Get the device extension pointer.
|
|
//
|
|
|
|
commonExtension = DeviceObject->DeviceExtension;
|
|
busExtension = DeviceObject->DeviceExtension;
|
|
diskExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// We don't support mask queries.
|
|
//
|
|
|
|
if ( query->QueryType >= PropertyMaskQuery ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER_1;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Dispatch based on the property ID.
|
|
//
|
|
|
|
switch ( query->PropertyId ) {
|
|
|
|
case StorageDeviceProperty:
|
|
|
|
//
|
|
// Make sure this is a target device.
|
|
//
|
|
|
|
if ( commonExtension->DeviceType != RamdiskDeviceTypeDiskPdo ) {
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If it's a just query for whether the property exists, the
|
|
// answer is yes.
|
|
//
|
|
|
|
if ( query->QueryType == PropertyExistsQuery ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Build a full return buffer. The output buffer might not be
|
|
// big enough to hold the whole thing.
|
|
//
|
|
|
|
RtlZeroMemory( &sdd, sizeof(sdd) );
|
|
|
|
sdd.Version = sizeof(sdd);
|
|
sdd.DeviceType = DIRECT_ACCESS_DEVICE;
|
|
sdd.RemovableMedia = (diskExtension->Options.Fixed ? FALSE : TRUE);
|
|
sdd.CommandQueueing = FALSE;
|
|
sdd.BusType = BusTypeUnknown;
|
|
|
|
length = sizeof(sdd) +
|
|
((strlen(VER_COMPANYNAME_STR) + 1 +
|
|
strlen(RAMDISK_DISK_DEVICE_TEXT_ANSI) + 1) * sizeof(CHAR));
|
|
sdd.Size = length;
|
|
|
|
//
|
|
// Zero the output buffer.
|
|
//
|
|
|
|
storageDeviceDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
|
RtlZeroMemory( storageDeviceDescriptor, queryLength );
|
|
|
|
//
|
|
// Copy the base part of the return descriptor to the output
|
|
// buffer.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
storageDeviceDescriptor,
|
|
&sdd,
|
|
min( queryLength, sizeof(sdd) )
|
|
);
|
|
|
|
//
|
|
// If there's no room for the rest of the data, we're done.
|
|
//
|
|
|
|
if ( queryLength <= sizeof(sdd) ) {
|
|
|
|
Irp->IoStatus.Information = queryLength;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy as much of the rest of the data as will fit.
|
|
//
|
|
|
|
offset = sizeof(sdd);
|
|
p = (PUCHAR)storageDeviceDescriptor + offset;
|
|
|
|
length = (strlen(VER_COMPANYNAME_STR) + 1) * sizeof(CHAR);
|
|
|
|
if ( (offset + length) > queryLength ) {
|
|
|
|
Irp->IoStatus.Information = offset;
|
|
|
|
break;
|
|
}
|
|
|
|
storageDeviceDescriptor->VendorIdOffset = offset;
|
|
memcpy( p, VER_COMPANYNAME_STR, length );
|
|
offset += length;
|
|
p += length;
|
|
|
|
length = (strlen(RAMDISK_DISK_DEVICE_TEXT_ANSI) + 1) * sizeof(CHAR);
|
|
|
|
if ( (offset + length) > queryLength ) {
|
|
|
|
Irp->IoStatus.Information = offset;
|
|
|
|
break;
|
|
}
|
|
|
|
storageDeviceDescriptor->ProductIdOffset = offset;
|
|
memcpy( p, RAMDISK_DISK_DEVICE_TEXT_ANSI, length );
|
|
offset += length;
|
|
p += length;
|
|
|
|
storageDeviceDescriptor->ProductRevisionOffset = 0;
|
|
storageDeviceDescriptor->SerialNumberOffset = 0;
|
|
|
|
storageDeviceDescriptor->Size = offset;
|
|
|
|
Irp->IoStatus.Information = offset;
|
|
|
|
break;
|
|
|
|
case StorageAdapterProperty:
|
|
|
|
//
|
|
// If this is a target device then forward it down to the
|
|
// underlying device object. This lets filters do their magic.
|
|
//
|
|
|
|
if ( commonExtension->DeviceType == RamdiskDeviceTypeDiskPdo ) {
|
|
|
|
//
|
|
// Call the lower device.
|
|
//
|
|
|
|
IoReleaseRemoveLock( &commonExtension->RemoveLock, Irp );
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
return IoCallDriver( commonExtension->LowerDeviceObject, Irp );
|
|
}
|
|
|
|
//
|
|
// If it's a just query for whether the property exists, the
|
|
// answer is yes.
|
|
//
|
|
|
|
if ( query->QueryType == PropertyExistsQuery ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Build a full return buffer. The output buffer might not be
|
|
// big enough to hold the whole thing.
|
|
//
|
|
|
|
RtlZeroMemory( &sad, sizeof(sad) );
|
|
|
|
sad.Version = sizeof(sad);
|
|
sad.Size = sizeof(sad);
|
|
sad.MaximumTransferLength = 0xFFFFFFFF;
|
|
sad.MaximumPhysicalPages = 0xFFFFFFFF;
|
|
sad.AlignmentMask = 1;
|
|
sad.AdapterUsesPio = FALSE;
|
|
sad.AdapterScansDown = FALSE;
|
|
sad.CommandQueueing = FALSE;
|
|
sad.AcceleratedTransfer = TRUE;
|
|
sad.BusType = BusTypeUnknown;
|
|
sad.BusMajorVersion = VER_PRODUCTMAJORVERSION;
|
|
sad.BusMinorVersion = VER_PRODUCTMINORVERSION;
|
|
|
|
//
|
|
// Zero the output buffer.
|
|
//
|
|
|
|
storageAdapterDescriptor = Irp->AssociatedIrp.SystemBuffer;
|
|
RtlZeroMemory( storageAdapterDescriptor, queryLength );
|
|
|
|
//
|
|
// Copy the base part of the return descriptor to the output
|
|
// buffer.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
storageAdapterDescriptor,
|
|
&sad,
|
|
min( queryLength, sizeof(sad) )
|
|
);
|
|
|
|
Irp->IoStatus.Information = min( queryLength, sizeof(sad) );
|
|
|
|
break;
|
|
|
|
case StorageDeviceIdProperty:
|
|
|
|
//
|
|
// Make sure this is a target device.
|
|
//
|
|
|
|
if ( commonExtension->DeviceType != RamdiskDeviceTypeDiskPdo ) {
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We don't support this property.
|
|
//
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// If this is a target device, then some filter beneath us may
|
|
// handle this property.
|
|
//
|
|
|
|
if ( commonExtension->DeviceType == RamdiskDeviceTypeDiskPdo ) {
|
|
|
|
//
|
|
// Call the lower device.
|
|
//
|
|
|
|
IoReleaseRemoveLock( &commonExtension->RemoveLock, Irp );
|
|
|
|
IoSkipCurrentIrpStackLocation( Irp );
|
|
|
|
return IoCallDriver( commonExtension->LowerDeviceObject, Irp );
|
|
}
|
|
|
|
//
|
|
// Nope, this property really doesn't exist.
|
|
//
|
|
|
|
status = STATUS_INVALID_PARAMETER_1;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point, we have not sent the IRP down to a lower device, so
|
|
// we need to complete it now.
|
|
//
|
|
|
|
ASSERT( status != STATUS_PENDING );
|
|
|
|
IoReleaseRemoveLock( &commonExtension->RemoveLock, Irp );
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|
|
|
return status;
|
|
|
|
} // RamdiskQueryProperty
|
|
|