https://git.reactos.org/?p=reactos.git;a=commitdiff;h=bd8226afe7f34c9135db15...
commit bd8226afe7f34c9135db1543638144177aab2081 Author: Victor Perevertkin victor.perevertkin@reactos.org AuthorDate: Fri Nov 13 03:04:15 2020 +0300 Commit: Victor Perevertkin victor.perevertkin@reactos.org CommitDate: Fri Nov 13 03:04:15 2020 +0300
[PARTMGR] Add the Partition Manager driver
This driver works as complement to disk.sys/classpnp.sys from Windows 10 Manages partition PDOs and exposes them as volumes to mountmgr.sys. The driver is almost complete, just some minor IOCTLs missing (will be added on demand) --- drivers/storage/CMakeLists.txt | 1 + drivers/storage/partmgr/CMakeLists.txt | 19 + drivers/storage/partmgr/debug.h | 14 + drivers/storage/partmgr/guid.c | 11 + drivers/storage/partmgr/partition.c | 816 ++++++++++++++++++ drivers/storage/partmgr/partmgr.c | 1373 +++++++++++++++++++++++++++++++ drivers/storage/partmgr/partmgr.h | 192 +++++ drivers/storage/partmgr/partmgr.rc | 5 + drivers/storage/partmgr/partmgr_reg.inf | 7 + drivers/storage/partmgr/utils.c | 73 ++ 10 files changed, 2511 insertions(+)
diff --git a/drivers/storage/CMakeLists.txt b/drivers/storage/CMakeLists.txt index b3f0f02a4c4..44c5131d8f6 100644 --- a/drivers/storage/CMakeLists.txt +++ b/drivers/storage/CMakeLists.txt @@ -2,4 +2,5 @@ add_subdirectory(class) add_subdirectory(floppy) add_subdirectory(ide) add_subdirectory(mountmgr) +add_subdirectory(partmgr) add_subdirectory(port) diff --git a/drivers/storage/partmgr/CMakeLists.txt b/drivers/storage/partmgr/CMakeLists.txt new file mode 100644 index 00000000000..275fc305fb4 --- /dev/null +++ b/drivers/storage/partmgr/CMakeLists.txt @@ -0,0 +1,19 @@ + +list(APPEND SOURCE + partition.c + partmgr.c + utils.c) + +list(APPEND PCH_SKIP_SOURCE + guid.c) + +add_library(partmgr MODULE + ${SOURCE} + ${PCH_SKIP_SOURCE} + partmgr.rc) + +add_pch(partmgr partmgr.h "${PCH_SKIP_SOURCE}") +set_module_type(partmgr kernelmodedriver) +add_importlibs(partmgr ntoskrnl hal) +add_registry_inf(partmgr_reg.inf) +add_cd_file(TARGET partmgr DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/storage/partmgr/debug.h b/drivers/storage/partmgr/debug.h new file mode 100644 index 00000000000..4ff75db66e3 --- /dev/null +++ b/drivers/storage/partmgr/debug.h @@ -0,0 +1,14 @@ +/* + * PROJECT: Partition manager driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Debug helpers + * COPYRIGHT: 2020 Victor Perevertkin (victor.perevertkin@reactos.org) + */ + +#include <reactos/debug.h> + +#define ERR(fmt, ...) ERR__(DPFLTR_DISK_ID, fmt, ##__VA_ARGS__) +#define WARN(fmt, ...) WARN__(DPFLTR_DISK_ID, fmt, ##__VA_ARGS__) +#define TRACE(fmt, ...) TRACE__(DPFLTR_DISK_ID, fmt, ##__VA_ARGS__) +#define INFO(fmt, ...) INFO__(DPFLTR_DISK_ID, fmt, ##__VA_ARGS__) +// #define FDPRINT(lvl, fmt, ...) FDPRINT__(DPFLTR_DISK_ID, lvl, fmt, __VA_ARGS__) diff --git a/drivers/storage/partmgr/guid.c b/drivers/storage/partmgr/guid.c new file mode 100644 index 00000000000..9aa431750c7 --- /dev/null +++ b/drivers/storage/partmgr/guid.c @@ -0,0 +1,11 @@ +/* DO NOT USE THE PRECOMPILED HEADER FOR THIS FILE! */ + +#include <ntdef.h> +#include <initguid.h> +#include <wdmguid.h> + +#define DEVICE_TYPE ULONG +#include <ntdddisk.h> +#include <ioevent.h> + +/* NO CODE HERE, THIS IS JUST REQUIRED FOR THE GUID DEFINITIONS */ diff --git a/drivers/storage/partmgr/partition.c b/drivers/storage/partmgr/partition.c new file mode 100644 index 00000000000..694836d3ae0 --- /dev/null +++ b/drivers/storage/partmgr/partition.c @@ -0,0 +1,816 @@ +/* + * 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%u\Partition%u"; + + +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; + + // create the device object + + swprintf(nameBuf, L"\Device\HarddiskVolume%u", HarddiskVolumeNextId++); + 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.PartitionType; + 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->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(); + + // fix the damn kernel! + if (PartExt->DeviceRemoved) + { + DPRINT1("IRP_MN_START_DEVICE after IRP_MN_REMOVE_DEVICE!\n"); + return STATUS_SUCCESS; + } + + // first, create a symbolic link for our device + WCHAR nameBuf[64]; + UNICODE_STRING partitionSymlink, interfaceName; + PFDO_EXTENSION fdoExtension = PartExt->LowerDevice->DeviceExtension; + + // \Device\Harddisk%u\Partition%u + 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; + + TRACE("Symlink created %wZ -> %wZ\n", &PartExt->DeviceName, &partitionSymlink); + + // our partition device will have two interfaces: + // GUID_DEVINTERFACE_PARTITION and GUID_DEVINTERFACE_VOLUME + // the former one is used to notify mountmgr about new device + + 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; +} + +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", &PartExt->DeviceName, &partitionSymlink); + } + + // 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) + { + status = IoSetDeviceInterfaceState(&PartExt->VolumeInterfaceName, FALSE); + if (!NT_SUCCESS(status)) + { + return status; + } + RtlFreeUnicodeString(&PartExt->VolumeInterfaceName); + RtlInitUnicodeString(&PartExt->VolumeInterfaceName, NULL); + } + + if (FinalRemove) + { + // fix the damn kernel! + if (PartExt->DeviceRemoved) + { + DPRINT1("Double IRP_MN_REMOVE_DEVICE!\n"); + return STATUS_SUCCESS; + } + + PartExt->DeviceRemoved = TRUE; + + 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(); + + // fix the damn kernel! + if (PartExt->DeviceRemoved) + { + DPRINT1("QDR after device removal!\n"); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type; + + if (type == TargetDeviceRelation) + { + // Device relations has one entry built in to it's 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: + case BusQueryCompatibleIDs: + { + 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 = 1; + + 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 one 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_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 stuff + 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: + { + PMOUNTDEV_UNIQUE_ID uniqueId = Irp->AssociatedIrp.SystemBuffer; + + if (!partExt->VolumeInterfaceName.Buffer) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT))) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + uniqueId->UniqueIdLength = partExt->VolumeInterfaceName.Length; + + // return UniqueIdLength back + if (!VerifyIrpOutBufferSize(Irp, sizeof(USHORT) + uniqueId->UniqueIdLength)) + { + Irp->IoStatus.Information = sizeof(USHORT); + status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory(uniqueId->UniqueId, + partExt->VolumeInterfaceName.Buffer, + uniqueId->UniqueIdLength); + + status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof(USHORT) + uniqueId->UniqueIdLength; + break; + } + default: + return ForwardIrpAndForget(DeviceObject, Irp); + } + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; +} diff --git a/drivers/storage/partmgr/partmgr.c b/drivers/storage/partmgr/partmgr.c new file mode 100644 index 00000000000..8abfdccedd6 --- /dev/null +++ b/drivers/storage/partmgr/partmgr.c @@ -0,0 +1,1373 @@ +/* + * PROJECT: Partition manager driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Main file + * COPYRIGHT: 2020 Victor Perevertkin (victor.perevertkin@reactos.org) + */ + +/* The Partition Manager Driver in ReactOS complements disk.sys/classpnp.sys drivers + * (which are derived from Windows 10 drivers) so does not do exactly what Windows 2003 partmgr.sys + * does. Here is acts like both partition and volume manager, because volmgr.sys does not (yet) + * exist in ReactOS. Thus handles some IOCTL_VOLUME_*, and IOCTL_MOUNTMGR_* IOCTLs. + */ + +#include "partmgr.h" + + +static +CODE_SEG("PAGE") +PDRIVE_LAYOUT_INFORMATION +PartMgrConvertExtendedToLayout( + _In_ CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx) +{ + PDRIVE_LAYOUT_INFORMATION Layout; + PPARTITION_INFORMATION Partition; + PPARTITION_INFORMATION_EX PartitionEx; + + PAGED_CODE(); + + ASSERT(LayoutEx); + + if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) + { + ASSERT(FALSE); + return NULL; + } + + size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]) + + LayoutEx->PartitionCount * sizeof (PARTITION_INFORMATION); + + Layout = ExAllocatePoolWithTag(PagedPool, layoutSize, TAG_PARTMGR); + + if (Layout == NULL) + { + return NULL; + } + + Layout->Signature = LayoutEx->Mbr.Signature; + Layout->PartitionCount = LayoutEx->PartitionCount; + + for (UINT32 i = 0; i < LayoutEx->PartitionCount; i++) + { + Partition = &Layout->PartitionEntry[i]; + PartitionEx = &LayoutEx->PartitionEntry[i]; + + Partition->StartingOffset = PartitionEx->StartingOffset; + Partition->PartitionLength = PartitionEx->PartitionLength; + Partition->RewritePartition = PartitionEx->RewritePartition; + Partition->PartitionNumber = PartitionEx->PartitionNumber; + + Partition->PartitionType = PartitionEx->Mbr.PartitionType; + Partition->BootIndicator = PartitionEx->Mbr.BootIndicator; + Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition; + Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors; + } + + return Layout; +} + +static +CODE_SEG("PAGE") +PDRIVE_LAYOUT_INFORMATION_EX +PartMgrConvertLayoutToExtended( + _In_ CONST PDRIVE_LAYOUT_INFORMATION Layout) +{ + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + + PAGED_CODE(); + + ASSERT(Layout != NULL); + + size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]) + + Layout->PartitionCount * sizeof (PARTITION_INFORMATION_EX); + + layoutEx = ExAllocatePoolUninitialized(PagedPool, layoutSize, TAG_PARTMGR); + + if (layoutEx == NULL) + { + return NULL; + } + + layoutEx->PartitionStyle = PARTITION_STYLE_MBR; + layoutEx->PartitionCount = Layout->PartitionCount; + layoutEx->Mbr.Signature = Layout->Signature; + + for (UINT32 i = 0; i < Layout->PartitionCount; i++) + { + PPARTITION_INFORMATION part = &Layout->PartitionEntry[i]; + + layoutEx->PartitionEntry[i] = (PARTITION_INFORMATION_EX) { + .PartitionStyle = PARTITION_STYLE_MBR, + .StartingOffset = part->StartingOffset, + .PartitionLength = part->PartitionLength, + .RewritePartition = part->RewritePartition, + .PartitionNumber = part->PartitionNumber, + .Mbr = { + .PartitionType = part->PartitionType, + .BootIndicator = part->BootIndicator, + .RecognizedPartition = part->RecognizedPartition, + .HiddenSectors = part->HiddenSectors, + } + }; + } + + return layoutEx; +} + +static +CODE_SEG("PAGE") +VOID +PartMgrUpdatePartitionDevices( + _In_ PFDO_EXTENSION FdoExtension, + _Inout_ PDRIVE_LAYOUT_INFORMATION_EX NewLayout) +{ + NTSTATUS status; + PSINGLE_LIST_ENTRY curEntry, prevEntry; + UINT32 totalPartitions = 0; + + // Clear the partition numbers from the list entries + for (UINT32 i = 0; i < NewLayout->PartitionCount; i++) + { + NewLayout->PartitionEntry[i].PartitionNumber = 0; + } + + // iterate over old partition list + prevEntry = &FdoExtension->PartitionList; + curEntry = FdoExtension->PartitionList.Next; + while (curEntry != NULL) + { + PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, PARTITION_EXTENSION, ListEntry); + UINT32 partNumber = 0; // count detected partitions for device symlinks + BOOLEAN found = FALSE; + PPARTITION_INFORMATION_EX partEntry; + + // trying to find this partition in returned layout + for (UINT32 i = 0; i < NewLayout->PartitionCount; i++) + { + partEntry = &NewLayout->PartitionEntry[i]; + + // skip unused and container partitions + if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR && + (partEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partEntry->Mbr.PartitionType))) + { + continue; + } + + partNumber++; + + // skip already found partitions + if (partEntry->PartitionNumber) + { + continue; + } + + // skip if partitions are not equal + if (partEntry->StartingOffset.QuadPart != partExt->StartingOffset || + partEntry->PartitionLength.QuadPart != partExt->PartitionLength) + { + continue; + } + + // found matching partition - processing it + found = TRUE; + break; + } + + if (found) + { + // update (possibly changed) partition metadata + if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR) + { + partExt->Mbr.PartitionType = partEntry->Mbr.PartitionType; + partExt->Mbr.BootIndicator = partEntry->Mbr.BootIndicator; + } + else + { + partExt->Gpt.PartitionType = partEntry->Gpt.PartitionType; + partExt->Gpt.PartitionId = partEntry->Gpt.PartitionId; + partExt->Gpt.Attributes = partEntry->Gpt.Attributes; + + RtlCopyMemory(partExt->Gpt.Name, partEntry->Gpt.Name, sizeof(partExt->Gpt.Name)); + } + + partExt->OnDiskNumber = partNumber; + partEntry->PartitionNumber = partNumber; // mark it as a found one + totalPartitions++; + } + else + { + // detach the device from the list + prevEntry->Next = curEntry->Next; + curEntry = prevEntry; + partExt->Attached = FALSE; + + // enumerated PDOs will receive IRP_MN_REMOVE_DEVICE + if (!partExt->IsEnumerated) + { + PartitionHandleRemove(partExt, TRUE); + } + } + + prevEntry = curEntry; + curEntry = curEntry->Next; + } + + UINT32 partNumber = 0; + UINT32 pdoNumber = 1; + + // now looking through remaining "new" partitions + for (UINT32 i = 0; i < NewLayout->PartitionCount; i++) + { + PPARTITION_INFORMATION_EX partEntry = &NewLayout->PartitionEntry[i]; + + // again, skip unused and container partitions + if (NewLayout->PartitionStyle == PARTITION_STYLE_MBR && + (partEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED || + IsContainerPartition(partEntry->Mbr.PartitionType))) + { + continue; + } + + partNumber++; + + // and skip processed partitions + if (partEntry->PartitionNumber != 0) + { + continue; + } + + // find the first free PDO index + for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next; + curEntry != NULL; + curEntry = curEntry->Next) + { + PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, + PARTITION_EXTENSION, + ListEntry); + + if (partExt->DetectedNumber == pdoNumber) + { + // found a matching pdo number - restart the search + curEntry = FdoExtension->PartitionList.Next; + pdoNumber++; + } + } + + partEntry->PartitionNumber = partNumber; + + PDEVICE_OBJECT partitionDevice; + status = PartitionCreateDevice(FdoExtension->DeviceObject, + partEntry, + pdoNumber, + NewLayout->PartitionStyle, + &partitionDevice); + + if (!NT_SUCCESS(status)) + { + partEntry->PartitionNumber = 0; + continue; + } + + totalPartitions++; + + // insert the structure to the partition list + curEntry = FdoExtension->PartitionList.Next; + prevEntry = NULL; + while (curEntry != NULL) + { + PPARTITION_EXTENSION curPart = CONTAINING_RECORD(curEntry, + PARTITION_EXTENSION, + ListEntry); + if (curPart->OnDiskNumber < partNumber) + { + prevEntry = curEntry; + curEntry = curPart->ListEntry.Next; + } + else + { // we found where to put the partition + break; + } + } + + PPARTITION_EXTENSION partExt = partitionDevice->DeviceExtension; + + if (prevEntry) + { + // insert after prevEntry + partExt->ListEntry.Next = prevEntry->Next; + prevEntry->Next = &partExt->ListEntry; + } + else + { + // insert in the beginning + partExt->ListEntry.Next = FdoExtension->PartitionList.Next; + FdoExtension->PartitionList.Next = &partExt->ListEntry; + } + + partExt->Attached = TRUE; + } + + FdoExtension->EnumeratedPartitionsTotal = totalPartitions; +} + +// requires partitioning lock held +static +CODE_SEG("PAGE") +NTSTATUS +PartMgrGetDriveLayout( + _In_ PFDO_EXTENSION FdoExtension, + _Out_ PDRIVE_LAYOUT_INFORMATION_EX *DriveLayout) +{ + PAGED_CODE(); + + if (FdoExtension->LayoutValid) + { + *DriveLayout = FdoExtension->LayoutCache; + return STATUS_SUCCESS; + } + + PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL; + NTSTATUS status = IoReadPartitionTableEx(FdoExtension->LowerDevice, &layoutEx); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (FdoExtension->LayoutCache) + { + ExFreePool(FdoExtension->LayoutCache); + } + + FdoExtension->LayoutCache = layoutEx; + FdoExtension->LayoutValid = TRUE; + + *DriveLayout = layoutEx; + + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskGetDriveGeometryEx( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + + PAGED_CODE(); + + // We're patching the DISK_PARTITION_INFO part of the returned structure + // as disk.sys doesn't really know about the partition table on a disk + + PDISK_GEOMETRY_EX_INTERNAL geometryEx = Irp->AssociatedIrp.SystemBuffer; + size_t outBufferLength = ioStack->Parameters.DeviceIoControl.OutputBufferLength; + NTSTATUS status; + + status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + FdoExtension->LowerDevice, + NULL, + 0, + geometryEx, + outBufferLength, + FALSE); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // if DISK_PARTITION_INFO fits the output size + if (outBufferLength >= FIELD_OFFSET(DISK_GEOMETRY_EX_INTERNAL, Detection)) + { + PartMgrAcquireLayoutLock(FdoExtension); + + geometryEx->Partition.SizeOfPartitionInfo = sizeof(geometryEx->Partition); + geometryEx->Partition.PartitionStyle = FdoExtension->DiskData.PartitionStyle; + + switch (geometryEx->Partition.PartitionStyle) + { + case PARTITION_STYLE_MBR: + geometryEx->Partition.Mbr.Signature = FdoExtension->DiskData.Mbr.Signature; + // checksum? + break; + + case PARTITION_STYLE_GPT: + geometryEx->Partition.Gpt.DiskId = FdoExtension->DiskData.Gpt.DiskId; + break; + + default: + RtlZeroMemory(&geometryEx->Partition, sizeof(geometryEx->Partition)); + } + + PartMgrReleaseLayoutLock(FdoExtension); + } + + // the logic is copied from disk.sys + Irp->IoStatus.Information = min(outBufferLength, sizeof(DISK_GEOMETRY_EX_INTERNAL)); + + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskGetPartitionInfo( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PPARTITION_INFORMATION partInfo = Irp->AssociatedIrp.SystemBuffer; + + PAGED_CODE(); + + if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfo))) + { + return STATUS_BUFFER_TOO_SMALL; + } + + PartMgrAcquireLayoutLock(FdoExtension); + + *partInfo = (PARTITION_INFORMATION){ + .PartitionType = PARTITION_ENTRY_UNUSED, + .StartingOffset.QuadPart = 0, + .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize, + .HiddenSectors = 0, + .PartitionNumber = 0, + .BootIndicator = FALSE, + .RewritePartition = FALSE, + .RecognizedPartition = FALSE, + }; + + PartMgrReleaseLayoutLock(FdoExtension); + + Irp->IoStatus.Information = sizeof(*partInfo); + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskGetPartitionInfoEx( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PPARTITION_INFORMATION_EX partInfoEx = Irp->AssociatedIrp.SystemBuffer; + + PAGED_CODE(); + + if (!VerifyIrpOutBufferSize(Irp, sizeof(*partInfoEx))) + { + return STATUS_BUFFER_TOO_SMALL; + } + + PartMgrAcquireLayoutLock(FdoExtension); + + // most of the fields a zeroed for Partition0 + *partInfoEx = (PARTITION_INFORMATION_EX){ + .PartitionLength.QuadPart = FdoExtension->DiskData.DiskSize, + .PartitionStyle = FdoExtension->DiskData.PartitionStyle, + }; + + PartMgrReleaseLayoutLock(FdoExtension); + + Irp->IoStatus.Information = sizeof(*partInfoEx); + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskGetDriveLayout( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + PartMgrAcquireLayoutLock(FdoExtension); + + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx); + + if (!NT_SUCCESS(status)) + { + PartMgrReleaseLayoutLock(FdoExtension); + return status; + } + + // checking this value from layoutEx in case it has been changed + if (layoutEx->PartitionStyle != PARTITION_STYLE_MBR) + { + PartMgrReleaseLayoutLock(FdoExtension); + return STATUS_INVALID_DEVICE_REQUEST; + } + + size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]); + size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION); + + if (!VerifyIrpOutBufferSize(Irp, size)) + { + PartMgrReleaseLayoutLock(FdoExtension); + return STATUS_BUFFER_TOO_SMALL; + } + + PDRIVE_LAYOUT_INFORMATION partitionList = PartMgrConvertExtendedToLayout(layoutEx); + + PartMgrReleaseLayoutLock(FdoExtension); + + if (partitionList == NULL) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, partitionList, size); + ExFreePoolWithTag(partitionList, TAG_PARTMGR); + + Irp->IoStatus.Information = size; + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskGetDriveLayoutEx( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + PartMgrAcquireLayoutLock(FdoExtension); + + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + NTSTATUS status = PartMgrGetDriveLayout(FdoExtension, &layoutEx); + if (!NT_SUCCESS(status)) + { + PartMgrReleaseLayoutLock(FdoExtension); + return status; + } + + size_t size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]); + size += layoutEx->PartitionCount * sizeof(PARTITION_INFORMATION_EX); + + if (!VerifyIrpOutBufferSize(Irp, size)) + { + PartMgrReleaseLayoutLock(FdoExtension); + return STATUS_BUFFER_TOO_SMALL; + } + + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, layoutEx, size); + + PartMgrReleaseLayoutLock(FdoExtension); + + Irp->IoStatus.Information = size; + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskSetDriveLayout( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PDRIVE_LAYOUT_INFORMATION layoutInfo = Irp->AssociatedIrp.SystemBuffer; + + PAGED_CODE(); + + if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutInfo))) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]); + layoutSize += layoutInfo->PartitionCount * sizeof(PARTITION_INFORMATION); + + if (!VerifyIrpInBufferSize(Irp, layoutSize)) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + PDRIVE_LAYOUT_INFORMATION_EX layoutEx = PartMgrConvertLayoutToExtended(layoutInfo); + + if (layoutEx == NULL) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + PartMgrAcquireLayoutLock(FdoExtension); + + // this in fact updates the bus relations + PartMgrUpdatePartitionDevices(FdoExtension, layoutEx); + + // write the partition table to the disk + NTSTATUS status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx); + if (NT_SUCCESS(status)) + { + // save the layout cache + if (FdoExtension->LayoutCache) + { + ExFreePool(FdoExtension->LayoutCache); + } + FdoExtension->LayoutCache = layoutEx; + FdoExtension->LayoutValid = TRUE; + + // set updated partition numbers + for (UINT32 i = 0; i < layoutInfo->PartitionCount; i++) + { + PPARTITION_INFORMATION part = &layoutInfo->PartitionEntry[i]; + + part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber; + } + } + else + { + FdoExtension->LayoutValid = FALSE; + } + + PartMgrReleaseLayoutLock(FdoExtension); + + IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations); + + // notify everyone that the disk layout has changed + TARGET_DEVICE_CUSTOM_NOTIFICATION notification; + + notification.Event = GUID_IO_DISK_LAYOUT_CHANGE; + notification.Version = 1; + notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer); + notification.FileObject = NULL; + notification.NameBufferOffset = -1; + + IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO, + ¬ification, + NULL, + NULL); + + Irp->IoStatus.Information = layoutSize; + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskSetDriveLayoutEx( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PDRIVE_LAYOUT_INFORMATION_EX layoutEx, layoutUser = Irp->AssociatedIrp.SystemBuffer; + NTSTATUS status; + + PAGED_CODE(); + + if (!VerifyIrpInBufferSize(Irp, sizeof(*layoutUser))) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + size_t layoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]); + layoutSize += layoutUser->PartitionCount * sizeof(PARTITION_INFORMATION_EX); + + if (!VerifyIrpInBufferSize(Irp, layoutSize)) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + // we need to copy the structure from the IRP input buffer + layoutEx = ExAllocatePoolWithTag(PagedPool, layoutSize, TAG_PARTMGR); + if (!layoutEx) + { + Irp->IoStatus.Information = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(layoutEx, layoutUser, layoutSize); + + PartMgrAcquireLayoutLock(FdoExtension); + + // if partition count is 0, it's the same as IOCTL_DISK_CREATE_DISK + if (layoutEx->PartitionCount == 0) + { + CREATE_DISK createDisk = {0}; + createDisk.PartitionStyle = layoutEx->PartitionStyle; + if (createDisk.PartitionStyle == PARTITION_STYLE_MBR) + { + createDisk.Mbr.Signature = layoutEx->Mbr.Signature; + } + else if (createDisk.PartitionStyle == PARTITION_STYLE_GPT) + { + createDisk.Gpt.DiskId = layoutEx->Gpt.DiskId; + } + + status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk); + } + else + { + // this in fact updates the bus relations + PartMgrUpdatePartitionDevices(FdoExtension, layoutEx); + + // write the partition table to the disk + status = IoWritePartitionTableEx(FdoExtension->LowerDevice, layoutEx); + if (NT_SUCCESS(status)) + { + // set updated partition numbers + for (UINT32 i = 0; i < layoutEx->PartitionCount; i++) + { + PPARTITION_INFORMATION_EX part = &layoutEx->PartitionEntry[i]; + + part->PartitionNumber = layoutEx->PartitionEntry[i].PartitionNumber; + } + } + } + + // update the layout cache + if (NT_SUCCESS(status)) + { + if (FdoExtension->LayoutCache) + { + ExFreePool(FdoExtension->LayoutCache); + } + FdoExtension->LayoutCache = layoutEx; + FdoExtension->LayoutValid = TRUE; + } + else + { + FdoExtension->LayoutValid = FALSE; + } + + PartMgrReleaseLayoutLock(FdoExtension); + + IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations); + + // notify everyone that the disk layout has changed + TARGET_DEVICE_CUSTOM_NOTIFICATION notification; + + notification.Event = GUID_IO_DISK_LAYOUT_CHANGE; + notification.Version = 1; + notification.Size = FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer); + notification.FileObject = NULL; + notification.NameBufferOffset = -1; + + IoReportTargetDeviceChangeAsynchronous(FdoExtension->PhysicalDiskDO, + ¬ification, + NULL, + NULL); + + Irp->IoStatus.Information = layoutSize; + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskUpdateProperties( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + PartMgrAcquireLayoutLock(FdoExtension); + FdoExtension->LayoutValid = FALSE; + PartMgrReleaseLayoutLock(FdoExtension); + + IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations); + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskCreateDisk( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + PCREATE_DISK createDisk = Irp->AssociatedIrp.SystemBuffer; + if (!VerifyIrpInBufferSize(Irp, sizeof(*createDisk))) + { + return STATUS_INFO_LENGTH_MISMATCH; + } + + PartMgrAcquireLayoutLock(FdoExtension); + + NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, createDisk); + + FdoExtension->LayoutValid = FALSE; + PartMgrReleaseLayoutLock(FdoExtension); + + IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations); + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoIoctlDiskDeleteDriveLayout( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + CREATE_DISK createDisk = { .PartitionStyle = PARTITION_STYLE_RAW }; + + PAGED_CODE(); + + PartMgrAcquireLayoutLock(FdoExtension); + + NTSTATUS status = IoCreateDisk(FdoExtension->LowerDevice, &createDisk); + + FdoExtension->LayoutValid = FALSE; + PartMgrReleaseLayoutLock(FdoExtension); + + IoInvalidateDeviceRelations(FdoExtension->PhysicalDiskDO, BusRelations); + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoHandleStartDevice( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + // obtain the disk device number + // this is not expected to change thus not in PartMgrRefreshDiskData + STORAGE_DEVICE_NUMBER deviceNumber; + NTSTATUS status = IssueSyncIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER, + FdoExtension->LowerDevice, + NULL, + 0, + &deviceNumber, + sizeof(deviceNumber), + FALSE); + if (!NT_SUCCESS(status)) + { + return status; + } + + FdoExtension->DiskData.DeviceNumber = deviceNumber.DeviceNumber; + return status; +} + +// requires partitioning lock held +static +CODE_SEG("PAGE") +NTSTATUS +PartMgrRefreshDiskData( + _In_ PFDO_EXTENSION FdoExtension) +{ + NTSTATUS status; + + PAGED_CODE(); + + // get the DiskSize and BytesPerSector + DISK_GEOMETRY_EX geometryEx; + status = IssueSyncIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, + FdoExtension->LowerDevice, + NULL, + 0, + &geometryEx, + sizeof(geometryEx), + FALSE); + if (!NT_SUCCESS(status)) + { + return status; + } + + FdoExtension->DiskData.DiskSize = geometryEx.DiskSize.QuadPart; + FdoExtension->DiskData.BytesPerSector = geometryEx.Geometry.BytesPerSector; + + // get the partition style-related info + PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL; + status = PartMgrGetDriveLayout(FdoExtension, &layoutEx); + if (!NT_SUCCESS(status)) + { + return status; + } + + FdoExtension->DiskData.PartitionStyle = layoutEx->PartitionStyle; + if (FdoExtension->DiskData.PartitionStyle == PARTITION_STYLE_MBR) + { + FdoExtension->DiskData.Mbr.Signature = layoutEx->Mbr.Signature; + // FdoExtension->DiskData.Mbr.Checksum = geometryEx.Partition.Mbr.CheckSum; + } + else + { + FdoExtension->DiskData.Gpt.DiskId = layoutEx->Gpt.DiskId; + } + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoHandleDeviceRelations( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + DEVICE_RELATION_TYPE type = ioStack->Parameters.QueryDeviceRelations.Type; + + PAGED_CODE(); + + if (type == BusRelations) + { + PartMgrAcquireLayoutLock(FdoExtension); + + NTSTATUS status = PartMgrRefreshDiskData(FdoExtension); + if (!NT_SUCCESS(status)) + { + PartMgrReleaseLayoutLock(FdoExtension); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Irp->IoStatus.Status; + } + + INFO("Partition style %u\n", FdoExtension->DiskData.PartitionStyle); + + // PartMgrAcquireLayoutLock calls PartMgrGetDriveLayout inside + // so we're sure here that it returns only cached layout + PDRIVE_LAYOUT_INFORMATION_EX layoutEx; + PartMgrGetDriveLayout(FdoExtension, &layoutEx); + + PartMgrUpdatePartitionDevices(FdoExtension, layoutEx); + + // now fill the DeviceRelations structure + TRACE("Reporting %u partitions\n", FdoExtension->EnumeratedPartitionsTotal); + + PDEVICE_RELATIONS deviceRelations = + ExAllocatePoolWithTag(PagedPool, + sizeof(DEVICE_RELATIONS) + + sizeof(PDEVICE_OBJECT) + * (FdoExtension->EnumeratedPartitionsTotal - 1), + TAG_PARTMGR); + + if (!deviceRelations) + { + PartMgrReleaseLayoutLock(FdoExtension); + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Irp->IoStatus.Status; + } + + deviceRelations->Count = 0; + + PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next; + while (curEntry != NULL) + { + PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, + PARTITION_EXTENSION, + ListEntry); + + // mark the PDO to know that we don't need to manually delete it + partExt->IsEnumerated = TRUE; + deviceRelations->Objects[deviceRelations->Count++] = partExt->DeviceObject; + ObReferenceObject(partExt->DeviceObject); + + curEntry = partExt->ListEntry.Next; + } + + ASSERT(deviceRelations->Count == FdoExtension->EnumeratedPartitionsTotal); + + PartMgrReleaseLayoutLock(FdoExtension); + + Irp->IoStatus.Information = (ULONG_PTR)deviceRelations; + Irp->IoStatus.Status = STATUS_SUCCESS; + } + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(FdoExtension->LowerDevice, Irp); +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoHandleRemoveDevice( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next; + curEntry != NULL; + curEntry = curEntry->Next) + { + PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, + PARTITION_EXTENSION, + ListEntry); + + ASSERT(partExt->DeviceRemoved); + } + + // Send the IRP down the stack + IoSkipCurrentIrpStackLocation(Irp); + Irp->IoStatus.Status = STATUS_SUCCESS; + NTSTATUS status = IoCallDriver(FdoExtension->LowerDevice, Irp); + + IoDetachDevice(FdoExtension->LowerDevice); + IoDeleteDevice(FdoExtension->DeviceObject); + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +FdoHandleSurpriseRemoval( + _In_ PFDO_EXTENSION FdoExtension, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + // all enumerated child devices should receive IRP_MN_REMOVE_DEVICE + // removing only non-enumerated ones here + for (PSINGLE_LIST_ENTRY curEntry = FdoExtension->PartitionList.Next; + curEntry != NULL; + curEntry = curEntry->Next) + { + PPARTITION_EXTENSION partExt = CONTAINING_RECORD(curEntry, + PARTITION_EXTENSION, + ListEntry); + + if (partExt->IsEnumerated) + { + PartitionHandleRemove(partExt, TRUE); + } + } + + // Send the IRP down the stack + IoSkipCurrentIrpStackLocation(Irp); + Irp->IoStatus.Status = STATUS_SUCCESS; + return IoCallDriver(FdoExtension->LowerDevice, Irp); +} + +static +CODE_SEG("PAGE") +NTSTATUS +NTAPI +PartMgrAddDevice( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject) +{ + PDEVICE_OBJECT deviceObject; + + PAGED_CODE(); + + NTSTATUS status = IoCreateDevice(DriverObject, + sizeof(FDO_EXTENSION), + 0, + FILE_DEVICE_BUS_EXTENDER, + FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) + { + ERR("Failed to create FDO 0x%x\n", status); + return status; + } + + PFDO_EXTENSION deviceExtension = deviceObject->DeviceExtension; + RtlZeroMemory(deviceExtension, sizeof(*deviceExtension)); + + deviceExtension->IsFDO = TRUE; + deviceExtension->DeviceObject = deviceObject; + deviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); + deviceExtension->PhysicalDiskDO = PhysicalDeviceObject; + KeInitializeEvent(&deviceExtension->SyncEvent, SynchronizationEvent, TRUE); + + // the the attaching failed + if (!deviceExtension->LowerDevice) + { + IoDeleteDevice(deviceObject); + + return STATUS_DEVICE_REMOVED; + } + deviceObject->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE; + + // device is initialized + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return STATUS_SUCCESS; +} + +static +NTSTATUS +NTAPI +PartMgrDeviceControl( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + NTSTATUS status; + + // Note: IRP_MJ_DEVICE_CONTROL handler in the storage stack must be able to pass IOCTLs + // at an IRQL higher than PASSIVE_LEVEL + + INFO("IRP_MJ_DEVICE_CONTROL %p Irp %p IOCTL %x isFdo: %u\n", + DeviceObject, Irp, ioStack->Parameters.DeviceIoControl.IoControlCode, fdoExtension->IsFDO); + + if (!fdoExtension->IsFDO) + { + return PartitionHandleDeviceControl(DeviceObject, Irp); + } + + switch (ioStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: + status = FdoIoctlDiskGetDriveGeometryEx(fdoExtension, Irp); + break; + + case IOCTL_DISK_GET_PARTITION_INFO: + status = FdoIoctlDiskGetPartitionInfo(fdoExtension, Irp); + break; + + case IOCTL_DISK_GET_PARTITION_INFO_EX: + status = FdoIoctlDiskGetPartitionInfoEx(fdoExtension, Irp); + break; + + case IOCTL_DISK_GET_DRIVE_LAYOUT: + status = FdoIoctlDiskGetDriveLayout(fdoExtension, Irp); + break; + + case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: + status = FdoIoctlDiskGetDriveLayoutEx(fdoExtension, Irp); + break; + + case IOCTL_DISK_SET_DRIVE_LAYOUT: + status = FdoIoctlDiskSetDriveLayout(fdoExtension, Irp); + break; + + case IOCTL_DISK_SET_DRIVE_LAYOUT_EX: + status = FdoIoctlDiskSetDriveLayoutEx(fdoExtension, Irp); + break; + + case IOCTL_DISK_UPDATE_PROPERTIES: + status = FdoIoctlDiskUpdateProperties(fdoExtension, Irp); + break; + + case IOCTL_DISK_CREATE_DISK: + status = FdoIoctlDiskCreateDisk(fdoExtension, Irp); + break; + + case IOCTL_DISK_DELETE_DRIVE_LAYOUT: + status = FdoIoctlDiskDeleteDriveLayout(fdoExtension, Irp); + break; + // case IOCTL_DISK_GROW_PARTITION: // todo + default: + return ForwardIrpAndForget(DeviceObject, Irp); + } + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; +} + +static +CODE_SEG("PAGE") +NTSTATUS +NTAPI +PartMgrPnp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + PAGED_CODE(); + + PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + + INFO("IRP_MJ_PNP %p Irp %p %s isFDO: %u\n", + DeviceObject, Irp, GetIRPMinorFunctionString(ioStack->MinorFunction), fdoExtension->IsFDO); + + if (!fdoExtension->IsFDO) + { + return PartitionHandlePnp(DeviceObject, Irp); + } + + switch (ioStack->MinorFunction) { + + case IRP_MN_START_DEVICE: + { + NTSTATUS status; + + // if this is sent to the FDO so we should forward it down the + // attachment chain before we can start the FDO + + if (!IoForwardIrpSynchronously(fdoExtension->LowerDevice, Irp)) + { + status = STATUS_UNSUCCESSFUL; + } + else + { + status = FdoHandleStartDevice(fdoExtension, Irp); + } + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + return FdoHandleDeviceRelations(fdoExtension, Irp); + } + case IRP_MN_SURPRISE_REMOVAL: + { + return FdoHandleSurpriseRemoval(fdoExtension, Irp); + } + case IRP_MN_REMOVE_DEVICE: + { + return FdoHandleRemoveDevice(fdoExtension, Irp); + } + 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: + { + Irp->IoStatus.Status = STATUS_SUCCESS; + // fallthrough + } + default: + { + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(fdoExtension->LowerDevice, Irp); + } + } +} + +static +NTSTATUS +NTAPI +PartMgrReadWrite( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + + if (!partExt->IsFDO) + { + if (!partExt->IsEnumerated) + { + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + else + { + ioStack->Parameters.Read.ByteOffset.QuadPart += partExt->StartingOffset; + } + } + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(partExt->LowerDevice, Irp); +} + +DRIVER_DISPATCH PartMgrPower; +NTSTATUS +NTAPI +PartMgrPower( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension; + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + + PoStartNextPowerIrp(Irp); + + if (!partExt->IsFDO) + { + NTSTATUS status; + + if (!partExt->IsEnumerated) + { + status = STATUS_DEVICE_DOES_NOT_EXIST; + } + else if (ioStack->MinorFunction == IRP_MN_SET_POWER || + ioStack->MinorFunction == IRP_MN_QUERY_POWER) + { + status = STATUS_SUCCESS; + } + else + { + status = Irp->IoStatus.Status; + } + + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return status; + } + else + { + IoSkipCurrentIrpStackLocation(Irp); + return PoCallDriver(partExt->LowerDevice, Irp); + } +} + +DRIVER_DISPATCH PartMgrShutdownFlush; +NTSTATUS +NTAPI +PartMgrShutdownFlush( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + PPARTITION_EXTENSION partExt = DeviceObject->DeviceExtension; + PDEVICE_OBJECT lowerDevice; + + // forward to the partition0 device in both cases + if (!partExt->IsFDO) + { + if (!partExt->IsEnumerated) + { + Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return STATUS_DEVICE_DOES_NOT_EXIST; + } + else + { + PFDO_EXTENSION fdoExtension = partExt->LowerDevice->DeviceExtension; + lowerDevice = fdoExtension->LowerDevice; + } + } + else + { + lowerDevice = partExt->LowerDevice; + } + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(lowerDevice, Irp); +} + +CODE_SEG("PAGE") +VOID +NTAPI +PartMgrUnload( + _In_ PDRIVER_OBJECT DriverObject) +{ + +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) +{ + DriverObject->DriverUnload = PartMgrUnload; + DriverObject->DriverExtension->AddDevice = PartMgrAddDevice; + DriverObject->MajorFunction[IRP_MJ_CREATE] = ForwardIrpAndForget; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = ForwardIrpAndForget; + DriverObject->MajorFunction[IRP_MJ_READ] = PartMgrReadWrite; + DriverObject->MajorFunction[IRP_MJ_WRITE] = PartMgrReadWrite; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PartMgrDeviceControl; + DriverObject->MajorFunction[IRP_MJ_PNP] = PartMgrPnp; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = PartMgrShutdownFlush; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = PartMgrShutdownFlush; + DriverObject->MajorFunction[IRP_MJ_POWER] = PartMgrPower; + + return STATUS_SUCCESS; +} diff --git a/drivers/storage/partmgr/partmgr.h b/drivers/storage/partmgr/partmgr.h new file mode 100644 index 00000000000..05a0ee6bbbd --- /dev/null +++ b/drivers/storage/partmgr/partmgr.h @@ -0,0 +1,192 @@ +/* + * PROJECT: Partition manager driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Main header + * COPYRIGHT: 2020 Victor Perevertkin (victor.perevertkin@reactos.org) + */ + +#ifndef _PARTMGR_H_ +#define _PARTMGR_H_ + +#include <ntifs.h> +#include <mountdev.h> +#include <ntddvol.h> +#include <ntdddisk.h> +#include <ndk/psfuncs.h> +#include <ndk/section_attribs.h> +#include <ioevent.h> +#include <stdio.h> +#include <debug/driverdbg.h> + +#include "debug.h" + +#define TAG_PARTMGR 'MtrP' + +// from disk.sys +typedef struct _DISK_GEOMETRY_EX_INTERNAL +{ + DISK_GEOMETRY Geometry; + INT64 DiskSize; + DISK_PARTITION_INFO Partition; + DISK_DETECTION_INFO Detection; +} DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL; + +typedef struct _FDO_EXTENSION +{ + BOOLEAN IsFDO; + PDEVICE_OBJECT DeviceObject; + PDEVICE_OBJECT LowerDevice; + PDEVICE_OBJECT PhysicalDiskDO; + KEVENT SyncEvent; + + BOOLEAN LayoutValid; + PDRIVE_LAYOUT_INFORMATION_EX LayoutCache; + + SINGLE_LIST_ENTRY PartitionList; + UINT32 EnumeratedPartitionsTotal; + + struct { + UINT64 DiskSize; + UINT32 DeviceNumber; + UINT32 BytesPerSector; + PARTITION_STYLE PartitionStyle; + union { + struct { + UINT32 Signature; + } Mbr; + struct { + GUID DiskId; + } Gpt; + }; + } DiskData; +} FDO_EXTENSION, *PFDO_EXTENSION; + +typedef struct _PARTITION_EXTENSION +{ + BOOLEAN IsFDO; + PDEVICE_OBJECT DeviceObject; + PDEVICE_OBJECT LowerDevice; + PDEVICE_OBJECT Part0Device; + + UINT64 StartingOffset; + UINT64 PartitionLength; + SINGLE_LIST_ENTRY ListEntry; + + UINT32 DetectedNumber; + UINT32 OnDiskNumber; // partition number for issuing Io requests to the kernel + BOOLEAN IsEnumerated; // reported via IRP_MN_QUERY_DEVICE_RELATIONS + BOOLEAN SymlinkCreated; + BOOLEAN DeviceRemoved; // !!! + BOOLEAN Attached; // attached to PartitionList of the FDO + union + { + struct + { + GUID PartitionType; + GUID PartitionId; + UINT64 Attributes; + WCHAR Name[36]; + } Gpt; + struct + { + UINT8 PartitionType; + BOOLEAN BootIndicator; + BOOLEAN RecognizedPartition; + UINT32 HiddenSectors; + } Mbr; + }; + UNICODE_STRING PartitionInterfaceName; + UNICODE_STRING VolumeInterfaceName; + UNICODE_STRING DeviceName; +} PARTITION_EXTENSION, *PPARTITION_EXTENSION; + +NTSTATUS +PartitionCreateDevice( + _In_ PDEVICE_OBJECT FDObject, + _In_ PPARTITION_INFORMATION_EX PartitionEntry, + _In_ UINT32 OnDiskNumber, + _In_ PARTITION_STYLE PartitionStyle, + _Out_ PDEVICE_OBJECT *PDO); + +NTSTATUS +PartitionHandleRemove( + _In_ PPARTITION_EXTENSION PartExt, + _In_ BOOLEAN FinalRemove); + +NTSTATUS +PartitionHandlePnp( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp); + +NTSTATUS +PartitionHandleDeviceControl( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp); + +NTSTATUS +NTAPI +ForwardIrpAndForget( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp); + +NTSTATUS +IssueSyncIoControlRequest( + _In_ UINT32 IoControlCode, + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _In_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl); + +inline +BOOLEAN +VerifyIrpOutBufferSize( + _In_ PIRP Irp, + _In_ SIZE_T Size) +{ + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + if (ioStack->Parameters.DeviceIoControl.OutputBufferLength < Size) + { + Irp->IoStatus.Information = Size; + return FALSE; + } + return TRUE; +} + +inline +BOOLEAN +VerifyIrpInBufferSize( + _In_ PIRP Irp, + _In_ SIZE_T Size) +{ + PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp); + if (ioStack->Parameters.DeviceIoControl.InputBufferLength < Size) + { + Irp->IoStatus.Information = Size; + return FALSE; + } + return TRUE; +} + +inline +VOID +PartMgrAcquireLayoutLock( + _In_ PFDO_EXTENSION FDOExtension) +{ + PAGED_CODE(); + + KeWaitForSingleObject(&FDOExtension->SyncEvent, Executive, KernelMode, FALSE, NULL); +} + +inline +VOID +PartMgrReleaseLayoutLock( + _In_ PFDO_EXTENSION FDOExtension) +{ + PAGED_CODE(); + + KeSetEvent(&FDOExtension->SyncEvent, IO_NO_INCREMENT, FALSE); +} + +#endif // _PARTMGR_H_ diff --git a/drivers/storage/partmgr/partmgr.rc b/drivers/storage/partmgr/partmgr.rc new file mode 100644 index 00000000000..29c32a5ad4e --- /dev/null +++ b/drivers/storage/partmgr/partmgr.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Partition Manager" +#define REACTOS_STR_INTERNAL_NAME "partmgr" +#define REACTOS_STR_ORIGINAL_FILENAME "partmgr.sys" +#include <reactos/version.rc> diff --git a/drivers/storage/partmgr/partmgr_reg.inf b/drivers/storage/partmgr/partmgr_reg.inf new file mode 100644 index 00000000000..de0e3c5178c --- /dev/null +++ b/drivers/storage/partmgr/partmgr_reg.inf @@ -0,0 +1,7 @@ +[AddReg] +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","ErrorControl",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","DisplayName",0x00000000,"Partition Manager" +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","Group",0x00000000,"Boot Bus Extender" +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","ImagePath",0x00020000,"system32\drivers\partmgr.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\partmgr","Type",0x00010001,0x00000001 diff --git a/drivers/storage/partmgr/utils.c b/drivers/storage/partmgr/utils.c new file mode 100644 index 00000000000..38751850dae --- /dev/null +++ b/drivers/storage/partmgr/utils.c @@ -0,0 +1,73 @@ +#include "partmgr.h" + +NTSTATUS +NTAPI +ForwardIrpAndForget( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp) +{ + // this part of a structure is identical in both FDO and PDO + PDEVICE_OBJECT LowerDevice = ((PFDO_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice; + + ASSERT(LowerDevice); + + IoSkipCurrentIrpStackLocation(Irp); + return IoCallDriver(LowerDevice, Irp); +} + +NTSTATUS +IssueSyncIoControlRequest( + _In_ UINT32 IoControlCode, + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _In_ PVOID OutputBuffer, + _In_ ULONG OutputBufferLength, + _In_ BOOLEAN InternalDeviceIoControl) +{ + PIRP Irp; + IO_STATUS_BLOCK IoStatusBlock; + PKEVENT Event; + NTSTATUS Status; + PAGED_CODE(); + + /* Allocate a non-paged event */ + Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Event), TAG_PARTMGR); + if (!Event) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Initialize it */ + KeInitializeEvent(Event, NotificationEvent, FALSE); + + /* Build the IRP */ + Irp = IoBuildDeviceIoControlRequest(IoControlCode, + DeviceObject, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + InternalDeviceIoControl, + Event, + &IoStatusBlock); + if (!Irp) + { + /* Fail, free the event */ + ExFreePoolWithTag(Event, TAG_PARTMGR); + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Call the driver and check if it's pending */ + Status = IoCallDriver(DeviceObject, Irp); + if (Status == STATUS_PENDING) + { + /* Wait on the driver */ + KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL); + Status = IoStatusBlock.Status; + } + + /* Free the event and return the Status */ + ExFreePoolWithTag(Event, TAG_PARTMGR); + return Status; +}