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 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
55 CdPnpCompletionRoutine (
56 IN PDEVICE_OBJECT DeviceObject
,
62 #pragma alloc_text(PAGE, CdCommonPnp)
63 #pragma alloc_text(PAGE, CdPnpCancelRemove)
64 #pragma alloc_text(PAGE, CdPnpQueryRemove)
65 #pragma alloc_text(PAGE, CdPnpRemove)
66 #pragma alloc_text(PAGE, CdPnpSurpriseRemove)
72 IN PIRP_CONTEXT IrpContext
,
80 This is the common routine for doing PnP operations called
81 by both the fsd and fsp threads
85 Irp - Supplies the Irp to process
89 NTSTATUS - The return status for the operation
95 BOOLEAN PassThrough
= FALSE
;
97 PIO_STACK_LOCATION IrpSp
;
99 PVOLUME_DEVICE_OBJECT OurDeviceObject
;
103 // Get the current Irp stack location.
106 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
109 // Find our Vcb. This is tricky since we have no file object in the Irp.
112 OurDeviceObject
= (PVOLUME_DEVICE_OBJECT
) IrpSp
->DeviceObject
;
115 // IO holds a handle reference on our VDO and holds the device lock, which
116 // syncs us against mounts/verifies. However we hold no reference on the
117 // volume, which may already have been torn down (and the Vpb freed), for
118 // example by a force dismount. Check for this condition. We must hold this
119 // lock until the pnp worker functions take additional locks/refs on the Vcb.
122 CdAcquireCdData( IrpContext
);
125 // Make sure this device object really is big enough to be a volume device
126 // object. If it isn't, we need to get out before we try to reference some
127 // field that takes us past the end of an ordinary device object.
130 if (OurDeviceObject
->DeviceObject
.Size
!= sizeof(VOLUME_DEVICE_OBJECT
) ||
131 NodeType( &OurDeviceObject
->Vcb
) != CDFS_NTC_VCB
) {
134 // We were called with something we don't understand.
137 Status
= STATUS_INVALID_PARAMETER
;
138 CdReleaseCdData( IrpContext
);
139 CdCompleteRequest( IrpContext
, Irp
, Status
);
144 // Force all PnP operations to be synchronous.
147 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
149 Vcb
= &OurDeviceObject
->Vcb
;
152 // Check that the Vcb hasn't already been deleted. If so, just pass the
153 // request through to the driver below, we don't need to do anything.
156 if (NULL
== Vcb
->Vpb
) {
163 // Case on the minor code.
166 switch ( IrpSp
->MinorFunction
) {
168 case IRP_MN_QUERY_REMOVE_DEVICE
:
170 Status
= CdPnpQueryRemove( IrpContext
, Irp
, Vcb
);
173 case IRP_MN_SURPRISE_REMOVAL
:
175 Status
= CdPnpSurpriseRemove( IrpContext
, Irp
, Vcb
);
178 case IRP_MN_REMOVE_DEVICE
:
180 Status
= CdPnpRemove( IrpContext
, Irp
, Vcb
);
183 case IRP_MN_CANCEL_REMOVE_DEVICE
:
185 Status
= CdPnpCancelRemove( IrpContext
, Irp
, Vcb
);
197 CdReleaseCdData( IrpContext
);
200 // Just pass the IRP on. As we do not need to be in the
201 // way on return, ellide ourselves out of the stack.
204 IoSkipCurrentIrpStackLocation( Irp
);
206 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
209 // Cleanup our Irp Context. The driver has completed the Irp.
212 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
221 PIRP_CONTEXT IrpContext
,
230 This routine handles the PnP query remove operation. The filesystem
231 is responsible for answering whether there are any reasons it sees
232 that the volume can not go away (and the device removed). Initiation
233 of the dismount begins when we answer yes to this question.
235 Query will be followed by a Cancel or Remove.
239 Irp - Supplies the Irp to process
241 Vcb - Supplies the volume being queried.
245 NTSTATUS - The return status for the operation
252 BOOLEAN VcbPresent
= TRUE
;
254 ASSERT_EXCLUSIVE_CDDATA
;
257 // Having said yes to a QUERY, any communication with the
258 // underlying storage stack is undefined (and may block)
259 // until the bounding CANCEL or REMOVE is sent.
261 // Acquire the global resource so that we can try to vaporize the volume,
262 // and the vcb resource itself.
265 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
268 // Drop a reference on the Vcb to keep it around after we drop the locks.
271 CdLockVcb( IrpContext
, Vcb
);
272 Vcb
->VcbReference
+= 1;
273 CdUnlockVcb( IrpContext
, Vcb
);
275 CdReleaseCdData( IrpContext
);
277 Status
= CdLockVolumeInternal( IrpContext
, Vcb
, NULL
);
280 // Reacquire the global lock, which means dropping the Vcb resource.
283 CdReleaseVcb( IrpContext
, Vcb
);
285 CdAcquireCdData( IrpContext
);
286 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
289 // Remove our extra reference.
292 CdLockVcb( IrpContext
, Vcb
);
293 Vcb
->VcbReference
-= 1;
294 CdUnlockVcb( IrpContext
, Vcb
);
296 if (NT_SUCCESS( Status
)) {
299 // We need to pass this down before starting the dismount, which
300 // could disconnect us immediately from the stack.
304 // Get the next stack location, and copy over the stack location
307 IoCopyCurrentIrpStackLocationToNext( Irp
);
310 // Set up the completion routine
313 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
314 IoSetCompletionRoutine( Irp
,
315 CdPnpCompletionRoutine
,
322 // Send the request and wait.
325 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
327 if (Status
== STATUS_PENDING
) {
329 KeWaitForSingleObject( &Event
,
335 Status
= Irp
->IoStatus
.Status
;
339 // Now if no one below us failed already, initiate the dismount
340 // on this volume, make it go away. PnP needs to see our internal
341 // streams close and drop their references to the target device.
343 // Since we were able to lock the volume, we are guaranteed to
344 // move this volume into dismount state and disconnect it from
345 // the underlying storage stack. The force on our part is actually
346 // unnecessary, though complete.
348 // What is not strictly guaranteed, though, is that the closes
349 // for the metadata streams take effect synchronously underneath
350 // of this call. This would leave references on the target device
351 // even though we are disconnected!
354 if (NT_SUCCESS( Status
)) {
356 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
358 ASSERT( !VcbPresent
|| Vcb
->VcbCondition
== VcbDismountInProgress
);
362 // Note: Normally everything will complete and the internal streams will
363 // vaporise. However there is some code in the system which drops additional
364 // references on fileobjects, including our internal stream file objects,
365 // for (WMI) tracing purposes. If that happens to run concurrently with our
366 // teardown, our internal streams will not vaporise until those references
367 // are removed. So it's possible that the volume still remains at this
368 // point. The pnp query remove will fail due to our references on the device.
369 // To be cleaner we will return an error here. We could pend the pnp
370 // IRP until the volume goes away, but since we don't know when that will
371 // be, and this is a very rare case, we'll just fail the query.
373 // The reason this is the case is that handles/fileobjects place a reference
374 // on the device objects they overly. In the filesystem case, these references
375 // are on our target devices. PnP correctly thinks that if references remain
376 // on the device objects in the stack that someone has a handle, and that this
377 // counts as a reason to not succeed the query - even though every interrogated
378 // driver thinks that it is OK.
381 if (NT_SUCCESS( Status
) && VcbPresent
&& (Vcb
->VcbReference
!= 0)) {
383 Status
= STATUS_DEVICE_BUSY
;
388 // Release the Vcb if it could still remain.
393 CdReleaseVcb( IrpContext
, Vcb
);
396 CdReleaseCdData( IrpContext
);
399 // Cleanup our IrpContext and complete the IRP if necessary.
402 CdCompleteRequest( IrpContext
, Irp
, Status
);
410 PIRP_CONTEXT IrpContext
,
419 This routine handles the PnP remove operation. This is our notification
420 that the underlying storage device for the volume we have is gone, and
421 an excellent indication that the volume will never reappear. The filesystem
422 is responsible for initiation or completion the dismount.
426 Irp - Supplies the Irp to process
428 Vcb - Supplies the volume being removed.
432 NTSTATUS - The return status for the operation
439 BOOLEAN VcbPresent
= TRUE
;
441 ASSERT_EXCLUSIVE_CDDATA
;
444 // REMOVE - a storage device is now gone. We either got
445 // QUERY'd and said yes OR got a SURPRISE OR a storage
446 // stack failed to spin back up from a sleep/stop state
447 // (the only case in which this will be the first warning).
449 // Note that it is entirely unlikely that we will be around
450 // for a REMOVE in the first two cases, as we try to initiate
455 // Acquire the global resource so that we can try to vaporize
456 // the volume, and the vcb resource itself.
459 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
462 // The device will be going away. Remove our lock and find
463 // out if we ever had one in the first place.
466 Status
= CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
469 // If the volume had not been locked, we must invalidate the
470 // volume to ensure it goes away properly. The remove will
474 if (!NT_SUCCESS( Status
)) {
476 CdLockVcb( IrpContext
, Vcb
);
478 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
480 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
483 CdUnlockVcb( IrpContext
, Vcb
);
485 Status
= STATUS_SUCCESS
;
489 // We need to pass this down before starting the dismount, which
490 // could disconnect us immediately from the stack.
494 // Get the next stack location, and copy over the stack location
497 IoCopyCurrentIrpStackLocationToNext( Irp
);
500 // Set up the completion routine
503 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
504 IoSetCompletionRoutine( Irp
,
505 CdPnpCompletionRoutine
,
512 // Send the request and wait.
515 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
517 if (Status
== STATUS_PENDING
) {
519 KeWaitForSingleObject( &Event
,
525 Status
= Irp
->IoStatus
.Status
;
529 // Now make our dismount happen. This may not vaporize the
530 // Vcb, of course, since there could be any number of handles
531 // outstanding if we were not preceded by a QUERY.
533 // PnP will take care of disconnecting this stack if we
534 // couldn't get off of it immediately.
538 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
541 // Release the Vcb if it could still remain.
546 CdReleaseVcb( IrpContext
, Vcb
);
549 CdReleaseCdData( IrpContext
);
552 // Cleanup our IrpContext and complete the IRP.
555 CdCompleteRequest( IrpContext
, Irp
, Status
);
562 CdPnpSurpriseRemove (
563 PIRP_CONTEXT IrpContext
,
572 This routine handles the PnP surprise remove operation. This is another
573 type of notification that the underlying storage device for the volume we
574 have is gone, and is excellent indication that the volume will never reappear.
575 The filesystem is responsible for initiation or completion the dismount.
577 For the most part, only "real" drivers care about the distinction of a
578 surprise remove, which is a result of our noticing that a user (usually)
579 physically reached into the machine and pulled something out.
581 Surprise will be followed by a Remove when all references have been shut down.
585 Irp - Supplies the Irp to process
587 Vcb - Supplies the volume being removed.
591 NTSTATUS - The return status for the operation
598 BOOLEAN VcbPresent
= TRUE
;
600 ASSERT_EXCLUSIVE_CDDATA
;
603 // SURPRISE - a device was physically yanked away without
604 // any warning. This means external forces.
607 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
610 // Invalidate the volume right now.
612 // The intent here is to make every subsequent operation
613 // on the volume fail and grease the rails toward dismount.
614 // By definition there is no going back from a SURPRISE.
617 CdLockVcb( IrpContext
, Vcb
);
619 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
621 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
624 CdUnlockVcb( IrpContext
, Vcb
);
627 // We need to pass this down before starting the dismount, which
628 // could disconnect us immediately from the stack.
632 // Get the next stack location, and copy over the stack location
635 IoCopyCurrentIrpStackLocationToNext( Irp
);
638 // Set up the completion routine
641 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
642 IoSetCompletionRoutine( Irp
,
643 CdPnpCompletionRoutine
,
650 // Send the request and wait.
653 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
655 if (Status
== STATUS_PENDING
) {
657 KeWaitForSingleObject( &Event
,
663 Status
= Irp
->IoStatus
.Status
;
667 // Now make our dismount happen. This may not vaporize the
668 // Vcb, of course, since there could be any number of handles
669 // outstanding since this is an out of band notification.
673 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
676 // Release the Vcb if it could still remain.
681 CdReleaseVcb( IrpContext
, Vcb
);
684 CdReleaseCdData( IrpContext
);
687 // Cleanup our IrpContext and complete the IRP.
690 CdCompleteRequest( IrpContext
, Irp
, Status
);
698 PIRP_CONTEXT IrpContext
,
707 This routine handles the PnP cancel remove operation. This is our
708 notification that a previously proposed remove (query) was eventually
709 vetoed by a component. The filesystem is responsible for cleaning up
710 and getting ready for more IO.
714 Irp - Supplies the Irp to process
716 Vcb - Supplies the volume being removed.
720 NTSTATUS - The return status for the operation
727 ASSERT_EXCLUSIVE_CDDATA
;
730 // CANCEL - a previous QUERY has been rescinded as a result
731 // of someone vetoing. Since PnP cannot figure out who may
732 // have gotten the QUERY (think about it: stacked drivers),
733 // we must expect to deal with getting a CANCEL without having
736 // For CDFS, this is quite easy. In fact, we can't get a
737 // CANCEL if the underlying drivers succeeded the QUERY since
738 // we disconnect the Vpb on our dismount initiation. This is
739 // actually pretty important because if PnP could get to us
740 // after the disconnect we'd be thoroughly unsynchronized
741 // with respect to the Vcb getting torn apart - merely referencing
742 // the volume device object is insufficient to keep us intact.
745 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
746 CdReleaseCdData( IrpContext
);
749 // Unlock the volume. This is benign if we never had seen
753 (VOID
) CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
755 CdReleaseVcb( IrpContext
, Vcb
);
758 // Send the request. The underlying driver will complete the
759 // IRP. Since we don't need to be in the way, simply ellide
760 // ourselves out of the IRP stack.
763 IoSkipCurrentIrpStackLocation( Irp
);
765 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
767 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
774 // Local support routine
778 NTAPI
/* ReactOS Change: GCC Does not support STDCALL by default */
779 CdPnpCompletionRoutine (
780 IN PDEVICE_OBJECT DeviceObject
,
785 PKEVENT Event
= (PKEVENT
) Contxt
;
787 KeSetEvent( Event
, 0, FALSE
);
789 return STATUS_MORE_PROCESSING_REQUIRED
;
791 UNREFERENCED_PARAMETER( DeviceObject
);
792 UNREFERENCED_PARAMETER( Contxt
);