3 Copyright (c) 1997-2000 Microsoft Corporation
11 This module implements the Plug and Play routines for CDFS called by
20 // The Bug check file id for this module
23 #define BugCheckFileId (CDFS_BUG_CHECK_PNP)
25 _Requires_lock_held_(_Global_critical_region_
)
26 _Releases_nonreentrant_lock_(CdData
.DataResource
)
29 _Inout_ PIRP_CONTEXT IrpContext
,
34 _Requires_lock_held_(_Global_critical_region_
)
35 _Releases_nonreentrant_lock_(CdData
.DataResource
)
38 _Inout_ PIRP_CONTEXT IrpContext
,
43 _Requires_lock_held_(_Global_critical_region_
)
44 _Releases_nonreentrant_lock_(CdData
.DataResource
)
47 _Inout_ PIRP_CONTEXT IrpContext
,
52 _Requires_lock_held_(_Global_critical_region_
)
53 _Releases_nonreentrant_lock_(CdData
.DataResource
)
56 _Inout_ PIRP_CONTEXT IrpContext
,
61 // Tell prefast this is a completion routine.
62 IO_COMPLETION_ROUTINE CdPnpCompletionRoutine
;
65 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
66 CdPnpCompletionRoutine (
67 _In_ PDEVICE_OBJECT DeviceObject
,
69 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
73 #pragma alloc_text(PAGE, CdCommonPnp)
74 #pragma alloc_text(PAGE, CdPnpCancelRemove)
75 #pragma alloc_text(PAGE, CdPnpQueryRemove)
76 #pragma alloc_text(PAGE, CdPnpRemove)
77 #pragma alloc_text(PAGE, CdPnpSurpriseRemove)
80 _Requires_lock_held_(_Global_critical_region_
)
83 _Inout_ PIRP_CONTEXT IrpContext
,
91 This is the common routine for doing PnP operations called
92 by both the fsd and fsp threads
96 Irp - Supplies the Irp to process
100 NTSTATUS - The return status for the operation
105 NTSTATUS Status
= STATUS_SUCCESS
;
106 BOOLEAN PassThrough
= FALSE
;
108 PIO_STACK_LOCATION IrpSp
;
110 PVOLUME_DEVICE_OBJECT OurDeviceObject
;
115 // Global lock object is acquired based on internal book-keeping
116 _Analysis_suppress_lock_checking_(CdData
.DataResource
);
119 // Get the current Irp stack location.
122 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
125 // Find our Vcb. This is tricky since we have no file object in the Irp.
128 OurDeviceObject
= (PVOLUME_DEVICE_OBJECT
) IrpSp
->DeviceObject
;
131 // IO holds a handle reference on our VDO and holds the device lock, which
132 // syncs us against mounts/verifies. However we hold no reference on the
133 // volume, which may already have been torn down (and the Vpb freed), for
134 // example by a force dismount. Check for this condition. We must hold this
135 // lock until the pnp worker functions take additional locks/refs on the Vcb.
138 CdAcquireCdData( IrpContext
);
141 // Make sure this device object really is big enough to be a volume device
142 // object. If it isn't, we need to get out before we try to reference some
143 // field that takes us past the end of an ordinary device object.
147 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the size member is allowed")
149 if (OurDeviceObject
->DeviceObject
.Size
!= sizeof(VOLUME_DEVICE_OBJECT
) ||
150 NodeType( &OurDeviceObject
->Vcb
) != CDFS_NTC_VCB
) {
153 // We were called with something we don't understand.
156 Status
= STATUS_INVALID_PARAMETER
;
157 CdReleaseCdData( IrpContext
);
158 CdCompleteRequest( IrpContext
, Irp
, Status
);
163 // Force all PnP operations to be synchronous.
166 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
168 Vcb
= &OurDeviceObject
->Vcb
;
171 // Check that the Vcb hasn't already been deleted. If so, just pass the
172 // request through to the driver below, we don't need to do anything.
175 if (NULL
== Vcb
->Vpb
) {
182 // Case on the minor code.
185 switch ( IrpSp
->MinorFunction
) {
187 case IRP_MN_QUERY_REMOVE_DEVICE
:
189 Status
= CdPnpQueryRemove( IrpContext
, Irp
, Vcb
);
192 case IRP_MN_SURPRISE_REMOVAL
:
194 Status
= CdPnpSurpriseRemove( IrpContext
, Irp
, Vcb
);
197 case IRP_MN_REMOVE_DEVICE
:
199 Status
= CdPnpRemove( IrpContext
, Irp
, Vcb
);
202 case IRP_MN_CANCEL_REMOVE_DEVICE
:
204 Status
= CdPnpCancelRemove( IrpContext
, Irp
, Vcb
);
216 CdReleaseCdData( IrpContext
);
219 // Just pass the IRP on. As we do not need to be in the
220 // way on return, ellide ourselves out of the stack.
223 IoSkipCurrentIrpStackLocation( Irp
);
225 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
228 // Cleanup our Irp Context. The driver has completed the Irp.
231 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
237 _Requires_lock_held_(_Global_critical_region_
)
238 _Releases_nonreentrant_lock_(CdData
.DataResource
)
241 _Inout_ PIRP_CONTEXT IrpContext
,
250 This routine handles the PnP query remove operation. The filesystem
251 is responsible for answering whether there are any reasons it sees
252 that the volume can not go away (and the device removed). Initiation
253 of the dismount begins when we answer yes to this question.
255 Query will be followed by a Cancel or Remove.
259 Irp - Supplies the Irp to process
261 Vcb - Supplies the volume being queried.
265 NTSTATUS - The return status for the operation
272 BOOLEAN VcbPresent
= TRUE
;
276 ASSERT_EXCLUSIVE_CDDATA
;
279 // Having said yes to a QUERY, any communication with the
280 // underlying storage stack is undefined (and may block)
281 // until the bounding CANCEL or REMOVE is sent.
283 // Acquire the global resource so that we can try to vaporize the volume,
284 // and the vcb resource itself.
287 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
290 // Drop a reference on the Vcb to keep it around after we drop the locks.
293 CdLockVcb( IrpContext
, Vcb
);
294 Vcb
->VcbReference
+= 1;
295 CdUnlockVcb( IrpContext
, Vcb
);
297 CdReleaseCdData( IrpContext
);
299 Status
= CdLockVolumeInternal( IrpContext
, Vcb
, NULL
);
302 // Reacquire the global lock, which means dropping the Vcb resource.
305 CdReleaseVcb( IrpContext
, Vcb
);
307 CdAcquireCdData( IrpContext
);
308 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
311 // Remove our extra reference.
314 CdLockVcb( IrpContext
, Vcb
);
315 Vcb
->VcbReference
-= 1;
316 CdUnlockVcb( IrpContext
, Vcb
);
318 if (NT_SUCCESS( Status
)) {
321 // We need to pass this down before starting the dismount, which
322 // could disconnect us immediately from the stack.
326 // Get the next stack location, and copy over the stack location
329 IoCopyCurrentIrpStackLocationToNext( Irp
);
332 // Set up the completion routine
335 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
336 IoSetCompletionRoutine( Irp
,
337 CdPnpCompletionRoutine
,
344 // Send the request and wait.
347 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
349 if (Status
== STATUS_PENDING
) {
351 (VOID
)KeWaitForSingleObject( &Event
,
357 Status
= Irp
->IoStatus
.Status
;
361 // Now if no one below us failed already, initiate the dismount
362 // on this volume, make it go away. PnP needs to see our internal
363 // streams close and drop their references to the target device.
365 // Since we were able to lock the volume, we are guaranteed to
366 // move this volume into dismount state and disconnect it from
367 // the underlying storage stack. The force on our part is actually
368 // unnecesary, though complete.
370 // What is not strictly guaranteed, though, is that the closes
371 // for the metadata streams take effect synchronously underneath
372 // of this call. This would leave references on the target device
373 // even though we are disconnected!
376 if (NT_SUCCESS( Status
)) {
378 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
380 NT_ASSERT( !VcbPresent
|| Vcb
->VcbCondition
== VcbDismountInProgress
);
384 // Note: Normally everything will complete and the internal streams will
385 // vaporise. However there is some code in the system which drops additional
386 // references on fileobjects, including our internal stream file objects,
387 // for (WMI) tracing purposes. If that happens to run concurrently with our
388 // teardown, our internal streams will not vaporise until those references
389 // are removed. So it's possible that the volume still remains at this
390 // point. The pnp query remove will fail due to our references on the device.
391 // To be cleaner we will return an error here. We could pend the pnp
392 // IRP until the volume goes away, but since we don't know when that will
393 // be, and this is a very rare case, we'll just fail the query.
395 // The reason this is the case is that handles/fileobjects place a reference
396 // on the device objects they overly. In the filesystem case, these references
397 // are on our target devices. PnP correcly thinks that if references remain
398 // on the device objects in the stack that someone has a handle, and that this
399 // counts as a reason to not succeed the query - even though every interrogated
400 // driver thinks that it is OK.
403 if (NT_SUCCESS( Status
) && VcbPresent
&& (Vcb
->VcbReference
!= 0)) {
405 Status
= STATUS_DEVICE_BUSY
;
410 // Release the Vcb if it could still remain.
415 CdReleaseVcb( IrpContext
, Vcb
);
418 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
421 CdReleaseCdData( IrpContext
);
424 // Cleanup our IrpContext and complete the IRP if neccesary.
427 CdCompleteRequest( IrpContext
, Irp
, Status
);
432 _Requires_lock_held_(_Global_critical_region_
)
433 _Releases_nonreentrant_lock_(CdData
.DataResource
)
436 _Inout_ PIRP_CONTEXT IrpContext
,
445 This routine handles the PnP remove operation. This is our notification
446 that the underlying storage device for the volume we have is gone, and
447 an excellent indication that the volume will never reappear. The filesystem
448 is responsible for initiation or completion the dismount.
452 Irp - Supplies the Irp to process
454 Vcb - Supplies the volume being removed.
458 NTSTATUS - The return status for the operation
465 BOOLEAN VcbPresent
= TRUE
;
469 ASSERT_EXCLUSIVE_CDDATA
;
472 // REMOVE - a storage device is now gone. We either got
473 // QUERY'd and said yes OR got a SURPRISE OR a storage
474 // stack failed to spin back up from a sleep/stop state
475 // (the only case in which this will be the first warning).
477 // Note that it is entirely unlikely that we will be around
478 // for a REMOVE in the first two cases, as we try to intiate
483 // Acquire the global resource so that we can try to vaporize
484 // the volume, and the vcb resource itself.
487 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
490 // The device will be going away. Remove our lock and find
491 // out if we ever had one in the first place.
494 Status
= CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
497 // If the volume had not been locked, we must invalidate the
498 // volume to ensure it goes away properly. The remove will
502 if (!NT_SUCCESS( Status
)) {
504 CdLockVcb( IrpContext
, Vcb
);
506 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
508 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
511 CdUnlockVcb( IrpContext
, Vcb
);
513 Status
= STATUS_SUCCESS
;
517 // We need to pass this down before starting the dismount, which
518 // could disconnect us immediately from the stack.
522 // Get the next stack location, and copy over the stack location
525 IoCopyCurrentIrpStackLocationToNext( Irp
);
528 // Set up the completion routine
531 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
532 IoSetCompletionRoutine( Irp
,
533 CdPnpCompletionRoutine
,
540 // Send the request and wait.
543 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
545 if (Status
== STATUS_PENDING
) {
547 (VOID
)KeWaitForSingleObject( &Event
,
553 Status
= Irp
->IoStatus
.Status
;
557 // Now make our dismount happen. This may not vaporize the
558 // Vcb, of course, since there could be any number of handles
559 // outstanding if we were not preceeded by a QUERY.
561 // PnP will take care of disconnecting this stack if we
562 // couldn't get off of it immediately.
566 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
569 // Release the Vcb if it could still remain.
574 CdReleaseVcb( IrpContext
, Vcb
);
577 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
580 CdReleaseCdData( IrpContext
);
583 // Cleanup our IrpContext and complete the IRP.
586 CdCompleteRequest( IrpContext
, Irp
, Status
);
591 _Requires_lock_held_(_Global_critical_region_
)
592 _Releases_nonreentrant_lock_(CdData
.DataResource
)
594 CdPnpSurpriseRemove (
595 _Inout_ PIRP_CONTEXT IrpContext
,
604 This routine handles the PnP surprise remove operation. This is another
605 type of notification that the underlying storage device for the volume we
606 have is gone, and is excellent indication that the volume will never reappear.
607 The filesystem is responsible for initiation or completion the dismount.
609 For the most part, only "real" drivers care about the distinction of a
610 surprise remove, which is a result of our noticing that a user (usually)
611 physically reached into the machine and pulled something out.
613 Surprise will be followed by a Remove when all references have been shut down.
617 Irp - Supplies the Irp to process
619 Vcb - Supplies the volume being removed.
623 NTSTATUS - The return status for the operation
630 BOOLEAN VcbPresent
= TRUE
;
634 ASSERT_EXCLUSIVE_CDDATA
;
637 // SURPRISE - a device was physically yanked away without
638 // any warning. This means external forces.
641 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
644 // Invalidate the volume right now.
646 // The intent here is to make every subsequent operation
647 // on the volume fail and grease the rails toward dismount.
648 // By definition there is no going back from a SURPRISE.
651 CdLockVcb( IrpContext
, Vcb
);
653 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
655 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
658 CdUnlockVcb( IrpContext
, Vcb
);
661 // We need to pass this down before starting the dismount, which
662 // could disconnect us immediately from the stack.
666 // Get the next stack location, and copy over the stack location
669 IoCopyCurrentIrpStackLocationToNext( Irp
);
672 // Set up the completion routine
675 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
676 IoSetCompletionRoutine( Irp
,
677 CdPnpCompletionRoutine
,
684 // Send the request and wait.
687 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
689 if (Status
== STATUS_PENDING
) {
691 (VOID
)KeWaitForSingleObject( &Event
,
697 Status
= Irp
->IoStatus
.Status
;
701 // Now make our dismount happen. This may not vaporize the
702 // Vcb, of course, since there could be any number of handles
703 // outstanding since this is an out of band notification.
707 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
710 // Release the Vcb if it could still remain.
715 CdReleaseVcb( IrpContext
, Vcb
);
718 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
721 CdReleaseCdData( IrpContext
);
724 // Cleanup our IrpContext and complete the IRP.
727 CdCompleteRequest( IrpContext
, Irp
, Status
);
732 _Requires_lock_held_(_Global_critical_region_
)
733 _Releases_nonreentrant_lock_(CdData
.DataResource
)
736 _Inout_ PIRP_CONTEXT IrpContext
,
745 This routine handles the PnP cancel remove operation. This is our
746 notification that a previously proposed remove (query) was eventually
747 vetoed by a component. The filesystem is responsible for cleaning up
748 and getting ready for more IO.
752 Irp - Supplies the Irp to process
754 Vcb - Supplies the volume being removed.
758 NTSTATUS - The return status for the operation
767 ASSERT_EXCLUSIVE_CDDATA
;
770 // CANCEL - a previous QUERY has been rescinded as a result
771 // of someone vetoing. Since PnP cannot figure out who may
772 // have gotten the QUERY (think about it: stacked drivers),
773 // we must expect to deal with getting a CANCEL without having
776 // For CDFS, this is quite easy. In fact, we can't get a
777 // CANCEL if the underlying drivers succeeded the QUERY since
778 // we disconnect the Vpb on our dismount initiation. This is
779 // actually pretty important because if PnP could get to us
780 // after the disconnect we'd be thoroughly unsynchronized
781 // with respect to the Vcb getting torn apart - merely referencing
782 // the volume device object is insufficient to keep us intact.
785 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
786 CdReleaseCdData( IrpContext
);
789 // Unlock the volume. This is benign if we never had seen
793 (VOID
) CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
795 CdReleaseVcb( IrpContext
, Vcb
);
798 // Send the request. The underlying driver will complete the
799 // IRP. Since we don't need to be in the way, simply ellide
800 // ourselves out of the IRP stack.
803 IoSkipCurrentIrpStackLocation( Irp
);
805 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
807 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
814 // Local support routine
818 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
819 CdPnpCompletionRoutine (
820 _In_ PDEVICE_OBJECT DeviceObject
,
822 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
825 PKEVENT Event
= (PKEVENT
) Contxt
;
826 _Analysis_assume_(Contxt
!= NULL
);
828 KeSetEvent( Event
, 0, FALSE
);
830 return STATUS_MORE_PROCESSING_REQUIRED
;
832 UNREFERENCED_PARAMETER( DeviceObject
);
833 UNREFERENCED_PARAMETER( Irp
);
834 UNREFERENCED_PARAMETER( Contxt
);