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)
27 PIRP_CONTEXT IrpContext
,
34 PIRP_CONTEXT IrpContext
,
41 PIRP_CONTEXT IrpContext
,
48 PIRP_CONTEXT IrpContext
,
54 CdPnpCompletionRoutine (
55 IN PDEVICE_OBJECT DeviceObject
,
61 #pragma alloc_text(PAGE, CdCommonPnp)
62 #pragma alloc_text(PAGE, CdPnpCancelRemove)
63 #pragma alloc_text(PAGE, CdPnpQueryRemove)
64 #pragma alloc_text(PAGE, CdPnpRemove)
65 #pragma alloc_text(PAGE, CdPnpSurpriseRemove)
71 IN PIRP_CONTEXT IrpContext
,
79 This is the common routine for doing PnP operations called
80 by both the fsd and fsp threads
84 Irp - Supplies the Irp to process
88 NTSTATUS - The return status for the operation
94 BOOLEAN PassThrough
= FALSE
;
96 PIO_STACK_LOCATION IrpSp
;
98 PVOLUME_DEVICE_OBJECT OurDeviceObject
;
102 // Get the current Irp stack location.
105 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
108 // Find our Vcb. This is tricky since we have no file object in the Irp.
111 OurDeviceObject
= (PVOLUME_DEVICE_OBJECT
) IrpSp
->DeviceObject
;
114 // IO holds a handle reference on our VDO and holds the device lock, which
115 // syncs us against mounts/verifies. However we hold no reference on the
116 // volume, which may already have been torn down (and the Vpb freed), for
117 // example by a force dismount. Check for this condition. We must hold this
118 // lock until the pnp worker functions take additional locks/refs on the Vcb.
121 CdAcquireCdData( IrpContext
);
124 // Make sure this device object really is big enough to be a volume device
125 // object. If it isn't, we need to get out before we try to reference some
126 // field that takes us past the end of an ordinary device object.
129 if (OurDeviceObject
->DeviceObject
.Size
!= sizeof(VOLUME_DEVICE_OBJECT
) ||
130 NodeType( &OurDeviceObject
->Vcb
) != CDFS_NTC_VCB
) {
133 // We were called with something we don't understand.
136 Status
= STATUS_INVALID_PARAMETER
;
137 CdReleaseCdData( IrpContext
);
138 CdCompleteRequest( IrpContext
, Irp
, Status
);
143 // Force all PnP operations to be synchronous.
146 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
148 Vcb
= &OurDeviceObject
->Vcb
;
151 // Check that the Vcb hasn't already been deleted. If so, just pass the
152 // request through to the driver below, we don't need to do anything.
155 if (NULL
== Vcb
->Vpb
) {
162 // Case on the minor code.
165 switch ( IrpSp
->MinorFunction
) {
167 case IRP_MN_QUERY_REMOVE_DEVICE
:
169 Status
= CdPnpQueryRemove( IrpContext
, Irp
, Vcb
);
172 case IRP_MN_SURPRISE_REMOVAL
:
174 Status
= CdPnpSurpriseRemove( IrpContext
, Irp
, Vcb
);
177 case IRP_MN_REMOVE_DEVICE
:
179 Status
= CdPnpRemove( IrpContext
, Irp
, Vcb
);
182 case IRP_MN_CANCEL_REMOVE_DEVICE
:
184 Status
= CdPnpCancelRemove( IrpContext
, Irp
, Vcb
);
196 CdReleaseCdData( IrpContext
);
199 // Just pass the IRP on. As we do not need to be in the
200 // way on return, ellide ourselves out of the stack.
203 IoSkipCurrentIrpStackLocation( Irp
);
205 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
208 // Cleanup our Irp Context. The driver has completed the Irp.
211 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
220 PIRP_CONTEXT IrpContext
,
229 This routine handles the PnP query remove operation. The filesystem
230 is responsible for answering whether there are any reasons it sees
231 that the volume can not go away (and the device removed). Initiation
232 of the dismount begins when we answer yes to this question.
234 Query will be followed by a Cancel or Remove.
238 Irp - Supplies the Irp to process
240 Vcb - Supplies the volume being queried.
244 NTSTATUS - The return status for the operation
251 BOOLEAN VcbPresent
= TRUE
;
253 ASSERT_EXCLUSIVE_CDDATA
;
256 // Having said yes to a QUERY, any communication with the
257 // underlying storage stack is undefined (and may block)
258 // until the bounding CANCEL or REMOVE is sent.
260 // Acquire the global resource so that we can try to vaporize the volume,
261 // and the vcb resource itself.
264 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
267 // Drop a reference on the Vcb to keep it around after we drop the locks.
270 CdLockVcb( IrpContext
, Vcb
);
271 Vcb
->VcbReference
+= 1;
272 CdUnlockVcb( IrpContext
, Vcb
);
274 CdReleaseCdData( IrpContext
);
276 Status
= CdLockVolumeInternal( IrpContext
, Vcb
, NULL
);
279 // Reacquire the global lock, which means dropping the Vcb resource.
282 CdReleaseVcb( IrpContext
, Vcb
);
284 CdAcquireCdData( IrpContext
);
285 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
288 // Remove our extra reference.
291 CdLockVcb( IrpContext
, Vcb
);
292 Vcb
->VcbReference
-= 1;
293 CdUnlockVcb( IrpContext
, Vcb
);
295 if (NT_SUCCESS( Status
)) {
298 // We need to pass this down before starting the dismount, which
299 // could disconnect us immediately from the stack.
303 // Get the next stack location, and copy over the stack location
306 IoCopyCurrentIrpStackLocationToNext( Irp
);
309 // Set up the completion routine
312 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
313 IoSetCompletionRoutine( Irp
,
314 CdPnpCompletionRoutine
,
321 // Send the request and wait.
324 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
326 if (Status
== STATUS_PENDING
) {
328 KeWaitForSingleObject( &Event
,
334 Status
= Irp
->IoStatus
.Status
;
338 // Now if no one below us failed already, initiate the dismount
339 // on this volume, make it go away. PnP needs to see our internal
340 // streams close and drop their references to the target device.
342 // Since we were able to lock the volume, we are guaranteed to
343 // move this volume into dismount state and disconnect it from
344 // the underlying storage stack. The force on our part is actually
345 // unnecessary, though complete.
347 // What is not strictly guaranteed, though, is that the closes
348 // for the metadata streams take effect synchronously underneath
349 // of this call. This would leave references on the target device
350 // even though we are disconnected!
353 if (NT_SUCCESS( Status
)) {
355 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
357 ASSERT( !VcbPresent
|| Vcb
->VcbCondition
== VcbDismountInProgress
);
361 // Note: Normally everything will complete and the internal streams will
362 // vaporise. However there is some code in the system which drops additional
363 // references on fileobjects, including our internal stream file objects,
364 // for (WMI) tracing purposes. If that happens to run concurrently with our
365 // teardown, our internal streams will not vaporise until those references
366 // are removed. So it's possible that the volume still remains at this
367 // point. The pnp query remove will fail due to our references on the device.
368 // To be cleaner we will return an error here. We could pend the pnp
369 // IRP until the volume goes away, but since we don't know when that will
370 // be, and this is a very rare case, we'll just fail the query.
372 // The reason this is the case is that handles/fileobjects place a reference
373 // on the device objects they overly. In the filesystem case, these references
374 // are on our target devices. PnP correctly thinks that if references remain
375 // on the device objects in the stack that someone has a handle, and that this
376 // counts as a reason to not succeed the query - even though every interrogated
377 // driver thinks that it is OK.
380 if (NT_SUCCESS( Status
) && VcbPresent
&& (Vcb
->VcbReference
!= 0)) {
382 Status
= STATUS_DEVICE_BUSY
;
387 // Release the Vcb if it could still remain.
392 CdReleaseVcb( IrpContext
, Vcb
);
395 CdReleaseCdData( IrpContext
);
398 // Cleanup our IrpContext and complete the IRP if necessary.
401 CdCompleteRequest( IrpContext
, Irp
, Status
);
409 PIRP_CONTEXT IrpContext
,
418 This routine handles the PnP remove operation. This is our notification
419 that the underlying storage device for the volume we have is gone, and
420 an excellent indication that the volume will never reappear. The filesystem
421 is responsible for initiation or completion the dismount.
425 Irp - Supplies the Irp to process
427 Vcb - Supplies the volume being removed.
431 NTSTATUS - The return status for the operation
438 BOOLEAN VcbPresent
= TRUE
;
440 ASSERT_EXCLUSIVE_CDDATA
;
443 // REMOVE - a storage device is now gone. We either got
444 // QUERY'd and said yes OR got a SURPRISE OR a storage
445 // stack failed to spin back up from a sleep/stop state
446 // (the only case in which this will be the first warning).
448 // Note that it is entirely unlikely that we will be around
449 // for a REMOVE in the first two cases, as we try to initiate
454 // Acquire the global resource so that we can try to vaporize
455 // the volume, and the vcb resource itself.
458 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
461 // The device will be going away. Remove our lock and find
462 // out if we ever had one in the first place.
465 Status
= CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
468 // If the volume had not been locked, we must invalidate the
469 // volume to ensure it goes away properly. The remove will
473 if (!NT_SUCCESS( Status
)) {
475 CdLockVcb( IrpContext
, Vcb
);
477 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
479 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
482 CdUnlockVcb( IrpContext
, Vcb
);
484 Status
= STATUS_SUCCESS
;
488 // We need to pass this down before starting the dismount, which
489 // could disconnect us immediately from the stack.
493 // Get the next stack location, and copy over the stack location
496 IoCopyCurrentIrpStackLocationToNext( Irp
);
499 // Set up the completion routine
502 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
503 IoSetCompletionRoutine( Irp
,
504 CdPnpCompletionRoutine
,
511 // Send the request and wait.
514 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
516 if (Status
== STATUS_PENDING
) {
518 KeWaitForSingleObject( &Event
,
524 Status
= Irp
->IoStatus
.Status
;
528 // Now make our dismount happen. This may not vaporize the
529 // Vcb, of course, since there could be any number of handles
530 // outstanding if we were not preceded by a QUERY.
532 // PnP will take care of disconnecting this stack if we
533 // couldn't get off of it immediately.
537 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
540 // Release the Vcb if it could still remain.
545 CdReleaseVcb( IrpContext
, Vcb
);
548 CdReleaseCdData( IrpContext
);
551 // Cleanup our IrpContext and complete the IRP.
554 CdCompleteRequest( IrpContext
, Irp
, Status
);
561 CdPnpSurpriseRemove (
562 PIRP_CONTEXT IrpContext
,
571 This routine handles the PnP surprise remove operation. This is another
572 type of notification that the underlying storage device for the volume we
573 have is gone, and is excellent indication that the volume will never reappear.
574 The filesystem is responsible for initiation or completion the dismount.
576 For the most part, only "real" drivers care about the distinction of a
577 surprise remove, which is a result of our noticing that a user (usually)
578 physically reached into the machine and pulled something out.
580 Surprise will be followed by a Remove when all references have been shut down.
584 Irp - Supplies the Irp to process
586 Vcb - Supplies the volume being removed.
590 NTSTATUS - The return status for the operation
597 BOOLEAN VcbPresent
= TRUE
;
599 ASSERT_EXCLUSIVE_CDDATA
;
602 // SURPRISE - a device was physically yanked away without
603 // any warning. This means external forces.
606 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
609 // Invalidate the volume right now.
611 // The intent here is to make every subsequent operation
612 // on the volume fail and grease the rails toward dismount.
613 // By definition there is no going back from a SURPRISE.
616 CdLockVcb( IrpContext
, Vcb
);
618 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
620 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
623 CdUnlockVcb( IrpContext
, Vcb
);
626 // We need to pass this down before starting the dismount, which
627 // could disconnect us immediately from the stack.
631 // Get the next stack location, and copy over the stack location
634 IoCopyCurrentIrpStackLocationToNext( Irp
);
637 // Set up the completion routine
640 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
641 IoSetCompletionRoutine( Irp
,
642 CdPnpCompletionRoutine
,
649 // Send the request and wait.
652 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
654 if (Status
== STATUS_PENDING
) {
656 KeWaitForSingleObject( &Event
,
662 Status
= Irp
->IoStatus
.Status
;
666 // Now make our dismount happen. This may not vaporize the
667 // Vcb, of course, since there could be any number of handles
668 // outstanding since this is an out of band notification.
672 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
675 // Release the Vcb if it could still remain.
680 CdReleaseVcb( IrpContext
, Vcb
);
683 CdReleaseCdData( IrpContext
);
686 // Cleanup our IrpContext and complete the IRP.
689 CdCompleteRequest( IrpContext
, Irp
, Status
);
697 PIRP_CONTEXT IrpContext
,
706 This routine handles the PnP cancel remove operation. This is our
707 notification that a previously proposed remove (query) was eventually
708 vetoed by a component. The filesystem is responsible for cleaning up
709 and getting ready for more IO.
713 Irp - Supplies the Irp to process
715 Vcb - Supplies the volume being removed.
719 NTSTATUS - The return status for the operation
726 ASSERT_EXCLUSIVE_CDDATA
;
729 // CANCEL - a previous QUERY has been rescinded as a result
730 // of someone vetoing. Since PnP cannot figure out who may
731 // have gotten the QUERY (think about it: stacked drivers),
732 // we must expect to deal with getting a CANCEL without having
735 // For CDFS, this is quite easy. In fact, we can't get a
736 // CANCEL if the underlying drivers succeeded the QUERY since
737 // we disconnect the Vpb on our dismount initiation. This is
738 // actually pretty important because if PnP could get to us
739 // after the disconnect we'd be thoroughly unsynchronized
740 // with respect to the Vcb getting torn apart - merely referencing
741 // the volume device object is insufficient to keep us intact.
744 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
745 CdReleaseCdData( IrpContext
);
748 // Unlock the volume. This is benign if we never had seen
752 (VOID
) CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
754 CdReleaseVcb( IrpContext
, Vcb
);
757 // Send the request. The underlying driver will complete the
758 // IRP. Since we don't need to be in the way, simply ellide
759 // ourselves out of the IRP stack.
762 IoSkipCurrentIrpStackLocation( Irp
);
764 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
766 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
773 // Local support routine
777 CdPnpCompletionRoutine (
778 IN PDEVICE_OBJECT DeviceObject
,
783 PKEVENT Event
= (PKEVENT
) Contxt
;
785 KeSetEvent( Event
, 0, FALSE
);
787 return STATUS_MORE_PROCESSING_REQUIRED
;
789 UNREFERENCED_PARAMETER( DeviceObject
);
790 UNREFERENCED_PARAMETER( Contxt
);