https://git.reactos.org/?p=reactos.git;a=commitdiff;h=8ee88d3bd5bb246442449…
commit 8ee88d3bd5bb2464424490bcbca8510d2116df51
Author: Victor Perevertkin <victor.perevertkin(a)reactos.org>
AuthorDate: Sun Dec 27 18:40:33 2020 +0300
Commit: Victor Perevertkin <victor.perevertkin(a)reactos.org>
CommitDate: Sun Dec 27 18:40:33 2020 +0300
[NTOS:PNP] Send removal IRPs to a file system device object for mounted devices
If a DeviceObject has VPB attached, it should be treated in a special way
CORE-16106
---
ntoskrnl/io/pnpmgr/devaction.c | 111 +++++++++++++++++++++++++++++------------
1 file changed, 80 insertions(+), 31 deletions(-)
diff --git a/ntoskrnl/io/pnpmgr/devaction.c b/ntoskrnl/io/pnpmgr/devaction.c
index 4f5fb6b3884..34d71e18a2e 100644
--- a/ntoskrnl/io/pnpmgr/devaction.c
+++ b/ntoskrnl/io/pnpmgr/devaction.c
@@ -1494,27 +1494,96 @@ IopStopDevice(
/* PUBLIC FUNCTIONS **********************************************************/
+/**
+ * @brief Sends one of the remove IRPs to the device stack
+ *
+ * If there is a mounted VPB attached to a one of the stack devices, the IRP
+ * should be send to a VPB's DeviceObject first (which belongs to a FS driver).
+ * FS driver will then forward it down to the volume device.
+ * While walking the device stack, the function sets (or unsets) VPB_REMOVE_PENDING flag
+ * thus blocking all further mounts on a soon-to-be-removed devices
+ */
+static
+NTSTATUS
+PiIrpSendRemoveCheckVpb(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ UCHAR MinorFunction)
+{
+ KIRQL oldIrql;
+
+ ASSERT(MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE ||
+ MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE ||
+ MinorFunction == IRP_MN_SURPRISE_REMOVAL ||
+ MinorFunction == IRP_MN_REMOVE_DEVICE);
+
+ PDEVICE_OBJECT vpbDevObj = DeviceObject, targetDevice = DeviceObject;
+
+ // walk the device stack down, stop on a first mounted device
+ do
+ {
+ if (vpbDevObj->Vpb)
+ {
+ // two locks are needed here
+ KeWaitForSingleObject(&vpbDevObj->DeviceLock, Executive, KernelMode,
FALSE, NULL);
+ IoAcquireVpbSpinLock(&oldIrql);
+
+ if (MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE)
+ {
+ vpbDevObj->Vpb->Flags &= ~VPB_REMOVE_PENDING;
+ }
+ else
+ {
+ vpbDevObj->Vpb->Flags |= VPB_REMOVE_PENDING;
+ }
+
+ BOOLEAN isMounted = (_Bool)(vpbDevObj->Vpb->Flags & VPB_MOUNTED);
+
+ if (isMounted)
+ {
+ targetDevice = vpbDevObj->Vpb->DeviceObject;
+ }
+
+ IoReleaseVpbSpinLock(oldIrql);
+ KeSetEvent(&vpbDevObj->DeviceLock, IO_NO_INCREMENT, FALSE);
+
+ if (isMounted)
+ {
+ break;
+ }
+ }
+
+ oldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
+ vpbDevObj = vpbDevObj->AttachedDevice;
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, oldIrql);
+ } while (vpbDevObj);
+
+ ASSERT(targetDevice);
+
+ PVOID info;
+ IO_STACK_LOCATION stack = {.MajorFunction = IRP_MJ_PNP, .MinorFunction =
MinorFunction};
+
+ return IopSynchronousCall(targetDevice, &stack, &info);
+}
+
static
VOID
NTAPI
IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
- IO_STACK_LOCATION Stack;
- PVOID Dummy;
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
/* Drop all our state for this device in case it isn't really going away */
DeviceNode->Flags &= DNF_ENUMERATED | DNF_PROCESSED;
- RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
- Stack.MajorFunction = IRP_MJ_PNP;
- Stack.MinorFunction = IRP_MN_REMOVE_DEVICE;
-
/* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
- IopSynchronousCall(DeviceObject, &Stack, &Dummy);
+ PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_REMOVE_DEVICE);
PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_COMPLETE, DeviceObject,
NULL);
- ObDereferenceObject(DeviceObject);
+ LONG_PTR refCount = ObDereferenceObject(DeviceObject);
+ if (refCount != 0)
+ {
+ DPRINT1("Leaking device %wZ, refCount = %d\n",
&DeviceNode->InstancePath, (INT32)refCount);
+ }
}
static
@@ -1562,15 +1631,8 @@ VOID
NTAPI
IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject)
{
- IO_STACK_LOCATION Stack;
- PVOID Dummy;
-
- RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
- Stack.MajorFunction = IRP_MJ_PNP;
- Stack.MinorFunction = IRP_MN_SURPRISE_REMOVAL;
-
/* Drivers should never fail a IRP_MN_SURPRISE_REMOVAL request */
- IopSynchronousCall(DeviceObject, &Stack, &Dummy);
+ PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_SURPRISE_REMOVAL);
}
static
@@ -1578,15 +1640,8 @@ VOID
NTAPI
IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
- IO_STACK_LOCATION Stack;
- PVOID Dummy;
-
- RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
- Stack.MajorFunction = IRP_MJ_PNP;
- Stack.MinorFunction = IRP_MN_CANCEL_REMOVE_DEVICE;
-
/* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */
- IopSynchronousCall(DeviceObject, &Stack, &Dummy);
+ PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_CANCEL_REMOVE_DEVICE);
PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_REMOVE_CANCELLED, DeviceObject,
NULL);
}
@@ -1669,8 +1724,6 @@ NTAPI
IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
- IO_STACK_LOCATION Stack;
- PVOID Dummy;
NTSTATUS Status;
ASSERT(DeviceNode);
@@ -1678,11 +1731,7 @@ IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING,
&DeviceNode->InstancePath);
- RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
- Stack.MajorFunction = IRP_MJ_PNP;
- Stack.MinorFunction = IRP_MN_QUERY_REMOVE_DEVICE;
-
- Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
+ Status = PiIrpSendRemoveCheckVpb(DeviceObject, IRP_MN_QUERY_REMOVE_DEVICE);
PiNotifyTargetDeviceChange(&GUID_TARGET_DEVICE_QUERY_REMOVE, DeviceObject,
NULL);