mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
9e07d0cc74
CORE-18139
When a partition is created, PartMgr notifies the volume manager
(FTDisk on Windows <= 2003, VolMgr on Vista+) of its presence.
(Note that currently in ReactOS, our partmgr does the job of both
PartMgr *AND* VolMgr.)
The VolMgr then sends a `GUID_DEVINTERFACE_VOLUME` PnP notification,
which is handled by the mount manager (MountMgr) as part of a
`GUID_DEVICE_INTERFACE_ARRIVAL` notification:
```
MountMgr!MountMgrMountedDeviceNotification -> MountMgrMountedDeviceArrival
followed by
MountMgr!MountMgrTargetDeviceNotification
```
When a partition is deleted, via e.g. Disk Management or DiskPart,
it can be observed, on Windows, that the PartMgr gets notified by
the PnP manager, as part of QueryDeviceRelations. Before actually
removing the partition, it notifies the VolMgr. The latter invalidates
any volume mounted on that partition (`*PartitionRemoved*` functions
for basic volumes), then requests (`*DeleteMountPoints` function)
the MountMgr to delete all the mount points associated to the volume:
```
VolMgr!*DeleteMountPoints
-> MountMgr!MountMgrDeviceControl -> MountMgrDeletePoints
```
**** THIS is the new functionality that is implemented for ReactOS ****
**** in the present commit. ****
Following this, a subsequent PnP notification is sent, which calls
```
MountMgr!MountMgrTargetDeviceNotification
-> MountMgr!MountMgrMountedDeviceRemoval
```
(Note that this observation somewhat invalidates the modification
made in ReactOS commit 62a4f9d42b
: our MountMgr placed in Windows
*WOULD* receive a `GUID_TARGET_DEVICE_REMOVE_COMPLETE` target-device
notification...)
Finally, a `GUID_DEVICE_INTERFACE_REMOVAL` PnP notification is sent
to the MountMgr:
```
MountMgr!MountMgrMountedDeviceNotification
-> MountMgr!MountMgrMountedDeviceRemoval
```
990 lines
33 KiB
C
990 lines
33 KiB
C
/*
|
|
* PROJECT: Partition manager driver
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Partition device code
|
|
* COPYRIGHT: 2020 Victor Perevertkin (victor.perevertkin@reactos.org)
|
|
*/
|
|
|
|
#include "partmgr.h"
|
|
|
|
static const WCHAR PartitionSymLinkFormat[] = L"\\Device\\Harddisk%lu\\Partition%lu";
|
|
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionCreateDevice(
|
|
_In_ PDEVICE_OBJECT FDObject,
|
|
_In_ PPARTITION_INFORMATION_EX PartitionEntry,
|
|
_In_ UINT32 PdoNumber,
|
|
_In_ PARTITION_STYLE PartitionStyle,
|
|
_Out_ PDEVICE_OBJECT *PDO)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
static UINT32 HarddiskVolumeNextId = 1; // this is 1-based
|
|
|
|
WCHAR nameBuf[64];
|
|
UNICODE_STRING deviceName;
|
|
UINT32 volumeNum;
|
|
|
|
// create the device object
|
|
|
|
volumeNum = HarddiskVolumeNextId++;
|
|
swprintf(nameBuf, L"\\Device\\HarddiskVolume%lu", volumeNum);
|
|
RtlCreateUnicodeString(&deviceName, nameBuf);
|
|
|
|
PDEVICE_OBJECT partitionDevice;
|
|
NTSTATUS status = IoCreateDevice(FDObject->DriverObject,
|
|
sizeof(PARTITION_EXTENSION),
|
|
&deviceName,
|
|
FILE_DEVICE_DISK,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&partitionDevice);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERR("Unable to create device object %wZ\n", &deviceName);
|
|
return status;
|
|
}
|
|
|
|
INFO("Created device object %p %wZ\n", partitionDevice, &deviceName);
|
|
|
|
PPARTITION_EXTENSION partExt = partitionDevice->DeviceExtension;
|
|
RtlZeroMemory(partExt, sizeof(*partExt));
|
|
|
|
partitionDevice->StackSize = FDObject->StackSize;
|
|
partitionDevice->Flags |= DO_DIRECT_IO;
|
|
|
|
if (PartitionStyle == PARTITION_STYLE_MBR)
|
|
{
|
|
partExt->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType;
|
|
partExt->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator;
|
|
partExt->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors;
|
|
}
|
|
else
|
|
{
|
|
partExt->Gpt.PartitionType = PartitionEntry->Gpt.PartitionType;
|
|
partExt->Gpt.PartitionId = PartitionEntry->Gpt.PartitionId;
|
|
partExt->Gpt.Attributes = PartitionEntry->Gpt.Attributes;
|
|
|
|
RtlCopyMemory(partExt->Gpt.Name, PartitionEntry->Gpt.Name, sizeof(partExt->Gpt.Name));
|
|
}
|
|
|
|
partExt->DeviceName = deviceName;
|
|
partExt->StartingOffset = PartitionEntry->StartingOffset.QuadPart;
|
|
partExt->PartitionLength = PartitionEntry->PartitionLength.QuadPart;
|
|
partExt->OnDiskNumber = PartitionEntry->PartitionNumber; // the "physical" partition number
|
|
partExt->DetectedNumber = PdoNumber; // counts only partitions with PDO created
|
|
partExt->VolumeNumber = volumeNum;
|
|
|
|
partExt->DeviceObject = partitionDevice;
|
|
partExt->LowerDevice = FDObject;
|
|
|
|
partitionDevice->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
*PDO = partitionDevice;
|
|
|
|
return status;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandleStartDevice(
|
|
_In_ PPARTITION_EXTENSION PartExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// first, create a symbolic link for our device
|
|
WCHAR nameBuf[64];
|
|
UNICODE_STRING partitionSymlink, interfaceName;
|
|
PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
|
|
|
|
// \\Device\\Harddisk%lu\\Partition%lu
|
|
swprintf(nameBuf, PartitionSymLinkFormat,
|
|
fdoExtension->DiskData.DeviceNumber, PartExt->DetectedNumber);
|
|
|
|
if (!RtlCreateUnicodeString(&partitionSymlink, nameBuf))
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
NTSTATUS status = IoCreateSymbolicLink(&partitionSymlink, &PartExt->DeviceName);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
PartExt->SymlinkCreated = TRUE;
|
|
|
|
INFO("Symlink created %wZ -> %wZ\n", &partitionSymlink, &PartExt->DeviceName);
|
|
|
|
// Our partition device will have two interfaces:
|
|
// GUID_DEVINTERFACE_PARTITION and GUID_DEVINTERFACE_VOLUME
|
|
// (aka. MOUNTDEV_MOUNTED_DEVICE_GUID).
|
|
// The latter one is used to notify MountMgr about the new volume.
|
|
|
|
status = IoRegisterDeviceInterface(PartExt->DeviceObject,
|
|
&GUID_DEVINTERFACE_PARTITION,
|
|
NULL,
|
|
&interfaceName);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
PartExt->PartitionInterfaceName = interfaceName;
|
|
status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
|
|
|
|
INFO("Partition interface %wZ\n", &interfaceName);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
RtlFreeUnicodeString(&interfaceName);
|
|
RtlInitUnicodeString(&PartExt->PartitionInterfaceName, NULL);
|
|
return status;
|
|
}
|
|
|
|
status = IoRegisterDeviceInterface(PartExt->DeviceObject,
|
|
&GUID_DEVINTERFACE_VOLUME,
|
|
NULL,
|
|
&interfaceName);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
PartExt->VolumeInterfaceName = interfaceName;
|
|
status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
|
|
|
|
INFO("Volume interface %wZ\n", &interfaceName);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
RtlFreeUnicodeString(&interfaceName);
|
|
RtlInitUnicodeString(&PartExt->VolumeInterfaceName, NULL);
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Notifies MountMgr to delete all mount points
|
|
* associated with the given volume.
|
|
*
|
|
* @note This should belong to volmgr.sys and act on a PVOLUME_EXTENSION.
|
|
**/
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
VolumeDeleteMountPoints(
|
|
_In_ PPARTITION_EXTENSION PartExt)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING MountMgr;
|
|
ULONG InputSize, OutputSize;
|
|
LOGICAL Retry;
|
|
PUNICODE_STRING DeviceName;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFILE_OBJECT FileObject = NULL;
|
|
PMOUNTMGR_MOUNT_POINT InputBuffer = NULL;
|
|
PMOUNTMGR_MOUNT_POINTS OutputBuffer = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Get the device pointer to the MountMgr */
|
|
RtlInitUnicodeString(&MountMgr, MOUNTMGR_DEVICE_NAME);
|
|
Status = IoGetDeviceObjectPointer(&MountMgr,
|
|
FILE_READ_ATTRIBUTES,
|
|
&FileObject,
|
|
&DeviceObject);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Setup the volume device name for deleting its mount points */
|
|
DeviceName = &PartExt->DeviceName;
|
|
|
|
/* Allocate the input buffer */
|
|
InputSize = sizeof(*InputBuffer) + DeviceName->Length;
|
|
InputBuffer = ExAllocatePoolWithTag(PagedPool, InputSize, TAG_PARTMGR);
|
|
if (!InputBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Fill it in */
|
|
RtlZeroMemory(InputBuffer, sizeof(*InputBuffer));
|
|
InputBuffer->DeviceNameOffset = sizeof(*InputBuffer);
|
|
InputBuffer->DeviceNameLength = DeviceName->Length;
|
|
RtlCopyMemory(&InputBuffer[1], DeviceName->Buffer, DeviceName->Length);
|
|
|
|
/*
|
|
* IOCTL_MOUNTMGR_DELETE_POINTS needs a large-enough scratch output buffer
|
|
* to work with. (It uses it to query the mount points, before deleting
|
|
* them.) Start with a guessed size and call the IOCTL. If the buffer is
|
|
* not big enough, use the value retrieved in MOUNTMGR_MOUNT_POINTS::Size
|
|
* to re-allocate a larger buffer and call the IOCTL once more.
|
|
*/
|
|
OutputSize = max(PAGE_SIZE, sizeof(*OutputBuffer));
|
|
for (Retry = 0; Retry < 2; ++Retry)
|
|
{
|
|
OutputBuffer = ExAllocatePoolWithTag(PagedPool, OutputSize, TAG_PARTMGR);
|
|
if (!OutputBuffer)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
/* Call the MountMgr to delete the drive letter */
|
|
Status = IssueSyncIoControlRequest(IOCTL_MOUNTMGR_DELETE_POINTS,
|
|
DeviceObject,
|
|
InputBuffer,
|
|
InputSize,
|
|
OutputBuffer,
|
|
OutputSize,
|
|
FALSE);
|
|
|
|
/* Adjust the allocation size if it was too small */
|
|
if (Status == STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
OutputSize = OutputBuffer->Size;
|
|
ExFreePoolWithTag(OutputBuffer, TAG_PARTMGR);
|
|
continue;
|
|
}
|
|
/* Success or failure: stop the loop */
|
|
break;
|
|
}
|
|
|
|
Quit:
|
|
if (OutputBuffer)
|
|
ExFreePoolWithTag(OutputBuffer, TAG_PARTMGR);
|
|
if (InputBuffer)
|
|
ExFreePoolWithTag(InputBuffer, TAG_PARTMGR);
|
|
if (FileObject)
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandleRemove(
|
|
_In_ PPARTITION_EXTENSION PartExt,
|
|
_In_ BOOLEAN FinalRemove)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
// remove the symbolic link
|
|
if (PartExt->SymlinkCreated)
|
|
{
|
|
WCHAR nameBuf[64];
|
|
UNICODE_STRING partitionSymlink;
|
|
PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
|
|
|
|
swprintf(nameBuf, PartitionSymLinkFormat,
|
|
fdoExtension->DiskData.DeviceNumber, PartExt->DetectedNumber);
|
|
|
|
RtlInitUnicodeString(&partitionSymlink, nameBuf);
|
|
|
|
status = IoDeleteSymbolicLink(&partitionSymlink);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
PartExt->SymlinkCreated = FALSE;
|
|
|
|
INFO("Symlink removed %wZ -> %wZ\n", &partitionSymlink, &PartExt->DeviceName);
|
|
}
|
|
|
|
// release device interfaces
|
|
if (PartExt->PartitionInterfaceName.Buffer)
|
|
{
|
|
status = IoSetDeviceInterfaceState(&PartExt->PartitionInterfaceName, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
RtlFreeUnicodeString(&PartExt->PartitionInterfaceName);
|
|
RtlInitUnicodeString(&PartExt->PartitionInterfaceName, NULL);
|
|
}
|
|
|
|
if (PartExt->VolumeInterfaceName.Buffer)
|
|
{
|
|
/* Notify MountMgr to delete all associated mount points.
|
|
* MountMgr does not automatically remove these in order to support
|
|
* drive letter persistence for online/offline volume transitions,
|
|
* or volumes arrival/removal on removable devices. */
|
|
status = VolumeDeleteMountPoints(PartExt);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERR("VolumeDeleteMountPoints(%wZ) failed with status 0x%08lx\n",
|
|
&PartExt->DeviceName, status);
|
|
/* Failure isn't major, continue proceeding with volume removal */
|
|
}
|
|
|
|
/* Notify MountMgr of volume removal */
|
|
status = IoSetDeviceInterfaceState(&PartExt->VolumeInterfaceName, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return status;
|
|
}
|
|
RtlFreeUnicodeString(&PartExt->VolumeInterfaceName);
|
|
RtlInitUnicodeString(&PartExt->VolumeInterfaceName, NULL);
|
|
}
|
|
|
|
if (FinalRemove)
|
|
{
|
|
ASSERT(PartExt->DeviceName.Buffer);
|
|
if (PartExt->DeviceName.Buffer)
|
|
{
|
|
INFO("Removed device %wZ\n", &PartExt->DeviceName);
|
|
RtlFreeUnicodeString(&PartExt->DeviceName);
|
|
}
|
|
|
|
IoDeleteDevice(PartExt->DeviceObject);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandleDeviceRelations(
|
|
_In_ PPARTITION_EXTENSION PartExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
|
|
DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type;
|
|
|
|
if (type == TargetDeviceRelation)
|
|
{
|
|
// Device relations have one entry built into their size.
|
|
PDEVICE_RELATIONS deviceRelations =
|
|
ExAllocatePoolZero(PagedPool, sizeof(DEVICE_RELATIONS), TAG_PARTMGR);
|
|
|
|
if (deviceRelations != NULL)
|
|
{
|
|
deviceRelations->Count = 1;
|
|
deviceRelations->Objects[0] = PartExt->DeviceObject;
|
|
ObReferenceObject(deviceRelations->Objects[0]);
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR)deviceRelations;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Irp->IoStatus.Information = 0;
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandleQueryId(
|
|
_In_ PPARTITION_EXTENSION PartExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
|
|
BUS_QUERY_ID_TYPE idType = ioStack->Parameters.QueryId.IdType;
|
|
UNICODE_STRING idString;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (idType)
|
|
{
|
|
case BusQueryDeviceID:
|
|
status = RtlCreateUnicodeString(&idString, L"STORAGE\\Partition")
|
|
? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
case BusQueryHardwareIDs:
|
|
{
|
|
static WCHAR volumeID[] = L"STORAGE\\Volume\0";
|
|
|
|
idString.Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(volumeID), TAG_PARTMGR);
|
|
RtlCopyMemory(idString.Buffer, volumeID, sizeof(volumeID));
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case BusQueryInstanceID:
|
|
{
|
|
WCHAR string[64];
|
|
PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension;
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
|
|
{
|
|
swprintf(string, L"S%08lx_O%I64x_L%I64x",
|
|
fdoExtension->DiskData.Mbr.Signature,
|
|
PartExt->StartingOffset,
|
|
PartExt->PartitionLength);
|
|
}
|
|
else
|
|
{
|
|
swprintf(string,
|
|
L"S%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02xS_O%I64x_L%I64x",
|
|
fdoExtension->DiskData.Gpt.DiskId.Data1,
|
|
fdoExtension->DiskData.Gpt.DiskId.Data2,
|
|
fdoExtension->DiskData.Gpt.DiskId.Data3,
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[0],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[1],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[2],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[3],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[4],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[5],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[6],
|
|
fdoExtension->DiskData.Gpt.DiskId.Data4[7],
|
|
PartExt->StartingOffset,
|
|
PartExt->PartitionLength);
|
|
}
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
status = RtlCreateUnicodeString(&idString, string)
|
|
? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
default:
|
|
status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Information = NT_SUCCESS(status) ? (ULONG_PTR) idString.Buffer : 0;
|
|
return status;
|
|
}
|
|
|
|
static
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandleQueryCapabilities(
|
|
_In_ PPARTITION_EXTENSION PartExt,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_CAPABILITIES devCaps = ioStack->Parameters.DeviceCapabilities.Capabilities;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(devCaps);
|
|
|
|
devCaps->SilentInstall = TRUE;
|
|
devCaps->RawDeviceOK = TRUE;
|
|
devCaps->NoDisplayInUI = TRUE;
|
|
devCaps->Address = PartExt->OnDiskNumber;
|
|
devCaps->UniqueID = FALSE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEG("PAGE")
|
|
NTSTATUS
|
|
PartitionHandlePnp(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (ioStack->MinorFunction)
|
|
{
|
|
case IRP_MN_START_DEVICE:
|
|
{
|
|
status = PartitionHandleStartDevice(partExt, Irp);
|
|
break;
|
|
}
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|
{
|
|
status = PartitionHandleDeviceRelations(partExt, Irp);
|
|
break;
|
|
}
|
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|
case IRP_MN_STOP_DEVICE:
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
{
|
|
status = PartitionHandleRemove(partExt, FALSE);
|
|
break;
|
|
}
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
{
|
|
status = PartitionHandleRemove(partExt, TRUE);
|
|
break;
|
|
}
|
|
case IRP_MN_QUERY_ID:
|
|
{
|
|
status = PartitionHandleQueryId(partExt, Irp);
|
|
break;
|
|
}
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
{
|
|
status = PartitionHandleQueryCapabilities(partExt, Irp);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Irp->IoStatus.Information = 0;
|
|
status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PartitionHandleDeviceControl(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PIRP Irp)
|
|
{
|
|
PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension;
|
|
PFDO_EXTENSION fdoExtension = partExt->LowerDevice->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(!partExt->IsFDO);
|
|
|
|
if (!partExt->IsEnumerated)
|
|
{
|
|
Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
switch (ioStack->Parameters.DeviceIoControl.IoControlCode)
|
|
{
|
|
// disk stuff
|
|
case IOCTL_DISK_GET_PARTITION_INFO:
|
|
{
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(PARTITION_INFORMATION)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
// not supported on anything other than MBR
|
|
if (fdoExtension->DiskData.PartitionStyle != PARTITION_STYLE_MBR)
|
|
{
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
break;
|
|
}
|
|
|
|
PPARTITION_INFORMATION partInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
*partInfo = (PARTITION_INFORMATION){
|
|
.PartitionType = partExt->Mbr.PartitionType,
|
|
.StartingOffset.QuadPart = partExt->StartingOffset,
|
|
.PartitionLength.QuadPart = partExt->PartitionLength,
|
|
.HiddenSectors = partExt->Mbr.HiddenSectors,
|
|
.PartitionNumber = partExt->DetectedNumber,
|
|
.BootIndicator = partExt->Mbr.BootIndicator,
|
|
.RecognizedPartition = partExt->Mbr.RecognizedPartition,
|
|
.RewritePartition = FALSE,
|
|
};
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
Irp->IoStatus.Information = sizeof(*partInfo);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IOCTL_DISK_GET_PARTITION_INFO_EX:
|
|
{
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(PARTITION_INFORMATION_EX)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PPARTITION_INFORMATION_EX partInfoEx = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
*partInfoEx = (PARTITION_INFORMATION_EX){
|
|
.StartingOffset.QuadPart = partExt->StartingOffset,
|
|
.PartitionLength.QuadPart = partExt->PartitionLength,
|
|
.PartitionNumber = partExt->DetectedNumber,
|
|
.PartitionStyle = fdoExtension->DiskData.PartitionStyle,
|
|
.RewritePartition = FALSE,
|
|
};
|
|
|
|
if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
|
|
{
|
|
partInfoEx->Mbr = (PARTITION_INFORMATION_MBR){
|
|
.PartitionType = partExt->Mbr.PartitionType,
|
|
.HiddenSectors = partExt->Mbr.HiddenSectors,
|
|
.BootIndicator = partExt->Mbr.BootIndicator,
|
|
.RecognizedPartition = partExt->Mbr.RecognizedPartition,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
partInfoEx->Gpt = (PARTITION_INFORMATION_GPT){
|
|
.PartitionType = partExt->Gpt.PartitionType,
|
|
.PartitionId = partExt->Gpt.PartitionId,
|
|
.Attributes = partExt->Gpt.Attributes,
|
|
};
|
|
|
|
RtlCopyMemory(partInfoEx->Gpt.Name,
|
|
partExt->Gpt.Name,
|
|
sizeof(partInfoEx->Gpt.Name));
|
|
}
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
Irp->IoStatus.Information = sizeof(*partInfoEx);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IOCTL_DISK_SET_PARTITION_INFO:
|
|
{
|
|
PSET_PARTITION_INFORMATION inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpInBufferSize(Irp, sizeof(*inputBuffer)))
|
|
{
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
// these functions use on disk numbers, not detected ones
|
|
status = IoSetPartitionInformation(fdoExtension->LowerDevice,
|
|
fdoExtension->DiskData.BytesPerSector,
|
|
partExt->OnDiskNumber,
|
|
inputBuffer->PartitionType);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
partExt->Mbr.PartitionType = inputBuffer->PartitionType;
|
|
}
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
case IOCTL_DISK_SET_PARTITION_INFO_EX:
|
|
{
|
|
PSET_PARTITION_INFORMATION_EX inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpInBufferSize(Irp, sizeof(*inputBuffer)))
|
|
{
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
// these functions use on disk numbers, not detected ones
|
|
status = IoSetPartitionInformationEx(fdoExtension->LowerDevice,
|
|
partExt->OnDiskNumber,
|
|
inputBuffer);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR)
|
|
{
|
|
partExt->Mbr.PartitionType = inputBuffer->Mbr.PartitionType;
|
|
}
|
|
else
|
|
{
|
|
partExt->Gpt.PartitionType = inputBuffer->Gpt.PartitionType;
|
|
partExt->Gpt.PartitionId = inputBuffer->Gpt.PartitionId;
|
|
partExt->Gpt.Attributes = inputBuffer->Gpt.Attributes;
|
|
|
|
RtlMoveMemory(partExt->Gpt.Name,
|
|
inputBuffer->Gpt.Name,
|
|
sizeof(partExt->Gpt.Name));
|
|
}
|
|
}
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
case IOCTL_DISK_GET_LENGTH_INFO:
|
|
{
|
|
PGET_LENGTH_INFORMATION lengthInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(*lengthInfo)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
lengthInfo->Length.QuadPart = partExt->PartitionLength;
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(*lengthInfo);
|
|
break;
|
|
}
|
|
case IOCTL_DISK_VERIFY:
|
|
{
|
|
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpInBufferSize(Irp, sizeof(*verifyInfo)))
|
|
{
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
// Partition device should just adjust the starting offset
|
|
verifyInfo->StartingOffset.QuadPart += partExt->StartingOffset;
|
|
return ForwardIrpAndForget(DeviceObject, Irp);
|
|
}
|
|
case IOCTL_DISK_UPDATE_PROPERTIES:
|
|
{
|
|
fdoExtension->LayoutValid = FALSE;
|
|
IoInvalidateDeviceRelations(fdoExtension->PhysicalDiskDO, BusRelations);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IOCTL_STORAGE_MEDIA_REMOVAL:
|
|
{
|
|
return ForwardIrpAndForget(DeviceObject, Irp);
|
|
}
|
|
// volume stuff (most of that should be in volmgr.sys once it is implemented)
|
|
case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
|
|
{
|
|
PVOLUME_DISK_EXTENTS volExts = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
// we fill only one extent entry so sizeof(*volExts) is enough
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(*volExts)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
// the only type of volume we support right now is disk partition
|
|
// so this structure is simple
|
|
|
|
*volExts = (VOLUME_DISK_EXTENTS) {
|
|
.NumberOfDiskExtents = 1,
|
|
.Extents = {{
|
|
.DiskNumber = fdoExtension->DiskData.DeviceNumber,
|
|
.StartingOffset.QuadPart = partExt->StartingOffset,
|
|
.ExtentLength.QuadPart = partExt->PartitionLength
|
|
}}
|
|
};
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(*volExts);
|
|
break;
|
|
}
|
|
case IOCTL_VOLUME_QUERY_VOLUME_NUMBER:
|
|
{
|
|
PVOLUME_NUMBER volNum = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(*volNum)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
volNum->VolumeNumber = partExt->VolumeNumber;
|
|
RtlCopyMemory(volNum->VolumeManagerName,
|
|
L"VOLMGR ", // Must be 8 space-padded characters
|
|
sizeof(volNum->VolumeManagerName));
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(*volNum);
|
|
break;
|
|
}
|
|
case IOCTL_VOLUME_IS_PARTITION:
|
|
{
|
|
// The only type of volume we support right now is disk partition
|
|
// so we just return success. A more robust algorithm would be
|
|
// to check whether the volume has only one single extent, that
|
|
// covers the whole partition on which it lies upon. If this is
|
|
// not the case, return STATUS_UNSUCCESSFUL instead.
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IOCTL_VOLUME_ONLINE:
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
case IOCTL_VOLUME_GET_GPT_ATTRIBUTES:
|
|
{
|
|
PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION gptAttrs = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(*gptAttrs)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
// not supported on anything other than GPT
|
|
if (fdoExtension->DiskData.PartitionStyle != PARTITION_STYLE_GPT)
|
|
{
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
gptAttrs->GptAttributes = partExt->Gpt.Attributes;
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(*gptAttrs);
|
|
break;
|
|
}
|
|
// mountmgr notifications (these should be in volmgr.sys once it is implemented)
|
|
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
|
|
{
|
|
PMOUNTDEV_NAME name = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT)))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
name->NameLength = partExt->DeviceName.Length;
|
|
|
|
// return NameLength back
|
|
if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT) + name->NameLength))
|
|
{
|
|
Irp->IoStatus.Information = sizeof(USHORT);
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory(name->Name, partExt->DeviceName.Buffer, name->NameLength);
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
|
|
break;
|
|
}
|
|
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
|
|
{
|
|
const SIZE_T headerSize = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId);
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Irp->AssociatedIrp.SystemBuffer;
|
|
PBASIC_VOLUME_UNIQUE_ID basicVolId = (PBASIC_VOLUME_UNIQUE_ID)&uniqueId->UniqueId;
|
|
PUNICODE_STRING InterfaceName;
|
|
|
|
// Check whether the minimal header size was provided
|
|
if (!VerifyIrpOutBufferSize(Irp, headerSize))
|
|
{
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PartMgrAcquireLayoutLock(fdoExtension);
|
|
|
|
InterfaceName = &partExt->VolumeInterfaceName;
|
|
if (fdoExtension->IsSuperFloppy)
|
|
InterfaceName = &fdoExtension->DiskInterfaceName;
|
|
|
|
// Calculate and return the necessary data size
|
|
if ((fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR) &&
|
|
!fdoExtension->IsSuperFloppy)
|
|
{
|
|
uniqueId->UniqueIdLength = sizeof(basicVolId->Mbr);
|
|
}
|
|
else if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_GPT)
|
|
{
|
|
uniqueId->UniqueIdLength = sizeof(basicVolId->Gpt);
|
|
}
|
|
else
|
|
{
|
|
if (!InterfaceName->Buffer || !InterfaceName->Length)
|
|
{
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
uniqueId->UniqueIdLength = InterfaceName->Length;
|
|
}
|
|
|
|
// Return UniqueIdLength back
|
|
if (!VerifyIrpOutBufferSize(Irp, headerSize + uniqueId->UniqueIdLength))
|
|
{
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
Irp->IoStatus.Information = headerSize;
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Write the UniqueId
|
|
//
|
|
// Format:
|
|
// - Basic volume on MBR disk: disk Mbr.Signature + partition StartingOffset (length: 0x0C)
|
|
// - Basic volume on GPT disk: "DMIO:ID:" + Gpt.PartitionGuid (length: 0x18)
|
|
// - Volume on Basic disk (NT <= 4): 8-byte FTDisk identifier (length: 0x08)
|
|
// - Volume on Dynamic disk (NT 5+): "DMIO:ID:" + dmio VolumeGuid (length: 0x18)
|
|
// - Super-floppy (single-partition with StartingOffset == 0),
|
|
// or Removable media: DiskInterfaceName.
|
|
// - As fallback, we use the VolumeInterfaceName.
|
|
//
|
|
if ((fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR) &&
|
|
!fdoExtension->IsSuperFloppy)
|
|
{
|
|
basicVolId->Mbr.Signature = fdoExtension->DiskData.Mbr.Signature;
|
|
basicVolId->Mbr.StartingOffset = partExt->StartingOffset;
|
|
}
|
|
else if (fdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_GPT)
|
|
{
|
|
basicVolId->Gpt.Signature = DMIO_ID_SIGNATURE;
|
|
basicVolId->Gpt.PartitionGuid = partExt->Gpt.PartitionId;
|
|
}
|
|
else
|
|
{
|
|
RtlCopyMemory(uniqueId->UniqueId,
|
|
InterfaceName->Buffer,
|
|
uniqueId->UniqueIdLength);
|
|
}
|
|
|
|
PartMgrReleaseLayoutLock(fdoExtension);
|
|
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = headerSize + uniqueId->UniqueIdLength;
|
|
break;
|
|
}
|
|
default:
|
|
return ForwardIrpAndForget(DeviceObject, Irp);
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|