[CDFS_NEW] Bugfix for f88fe43: don't delete devices twice on shutdown.
[reactos.git] / drivers / filesystems / cdfs_new / pnp.c
1 /*++
2
3 Copyright (c) 1997-2000 Microsoft Corporation
4
5 Module Name:
6
7 Pnp.c
8
9 Abstract:
10
11 This module implements the Plug and Play routines for CDFS called by
12 the dispatch driver.
13
14
15 --*/
16
17 #include "cdprocs.h"
18
19 //
20 // The Bug check file id for this module
21 //
22
23 #define BugCheckFileId (CDFS_BUG_CHECK_PNP)
24
25 NTSTATUS
26 CdPnpQueryRemove (
27 PIRP_CONTEXT IrpContext,
28 PIRP Irp,
29 PVCB Vcb
30 );
31
32 NTSTATUS
33 CdPnpRemove (
34 PIRP_CONTEXT IrpContext,
35 PIRP Irp,
36 PVCB Vcb
37 );
38
39 NTSTATUS
40 CdPnpSurpriseRemove (
41 PIRP_CONTEXT IrpContext,
42 PIRP Irp,
43 PVCB Vcb
44 );
45
46 NTSTATUS
47 CdPnpCancelRemove (
48 PIRP_CONTEXT IrpContext,
49 PIRP Irp,
50 PVCB Vcb
51 );
52
53 NTSTATUS
54 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
55 CdPnpCompletionRoutine (
56 IN PDEVICE_OBJECT DeviceObject,
57 IN PIRP Irp,
58 IN PVOID Contxt
59 );
60
61 #ifdef ALLOC_PRAGMA
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)
67 #endif
68
69
70 NTSTATUS
71 CdCommonPnp (
72 IN PIRP_CONTEXT IrpContext,
73 IN PIRP Irp
74 )
75
76 /*++
77
78 Routine Description:
79
80 This is the common routine for doing PnP operations called
81 by both the fsd and fsp threads
82
83 Arguments:
84
85 Irp - Supplies the Irp to process
86
87 Return Value:
88
89 NTSTATUS - The return status for the operation
90
91 --*/
92
93 {
94 NTSTATUS Status;
95 BOOLEAN PassThrough = FALSE;
96
97 PIO_STACK_LOCATION IrpSp;
98
99 PVOLUME_DEVICE_OBJECT OurDeviceObject;
100 PVCB Vcb;
101
102 //
103 // Get the current Irp stack location.
104 //
105
106 IrpSp = IoGetCurrentIrpStackLocation( Irp );
107
108 //
109 // Find our Vcb. This is tricky since we have no file object in the Irp.
110 //
111
112 OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
113
114 //
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.
120 //
121
122 CdAcquireCdData( IrpContext);
123
124 //
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.
128 //
129
130 if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
131 NodeType( &OurDeviceObject->Vcb ) != CDFS_NTC_VCB) {
132
133 //
134 // We were called with something we don't understand.
135 //
136
137 Status = STATUS_INVALID_PARAMETER;
138 CdReleaseCdData( IrpContext);
139 CdCompleteRequest( IrpContext, Irp, Status );
140 return Status;
141 }
142
143 //
144 // Force all PnP operations to be synchronous.
145 //
146
147 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
148
149 Vcb = &OurDeviceObject->Vcb;
150
151 //
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.
154 //
155
156 if (NULL == Vcb->Vpb) {
157
158 PassThrough = TRUE;
159 }
160 else {
161
162 //
163 // Case on the minor code.
164 //
165
166 switch ( IrpSp->MinorFunction ) {
167
168 case IRP_MN_QUERY_REMOVE_DEVICE:
169
170 Status = CdPnpQueryRemove( IrpContext, Irp, Vcb );
171 break;
172
173 case IRP_MN_SURPRISE_REMOVAL:
174
175 Status = CdPnpSurpriseRemove( IrpContext, Irp, Vcb );
176 break;
177
178 case IRP_MN_REMOVE_DEVICE:
179
180 Status = CdPnpRemove( IrpContext, Irp, Vcb );
181 break;
182
183 case IRP_MN_CANCEL_REMOVE_DEVICE:
184
185 Status = CdPnpCancelRemove( IrpContext, Irp, Vcb );
186 break;
187
188 default:
189
190 PassThrough = TRUE;
191 break;
192 }
193 }
194
195 if (PassThrough) {
196
197 CdReleaseCdData( IrpContext);
198
199 //
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.
202 //
203
204 IoSkipCurrentIrpStackLocation( Irp );
205
206 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
207
208 //
209 // Cleanup our Irp Context. The driver has completed the Irp.
210 //
211
212 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
213 }
214
215 return Status;
216 }
217
218
219 NTSTATUS
220 CdPnpQueryRemove (
221 PIRP_CONTEXT IrpContext,
222 PIRP Irp,
223 PVCB Vcb
224 )
225
226 /*++
227
228 Routine Description:
229
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.
234
235 Query will be followed by a Cancel or Remove.
236
237 Arguments:
238
239 Irp - Supplies the Irp to process
240
241 Vcb - Supplies the volume being queried.
242
243 Return Value:
244
245 NTSTATUS - The return status for the operation
246
247 --*/
248
249 {
250 NTSTATUS Status;
251 KEVENT Event;
252 BOOLEAN VcbPresent = TRUE;
253
254 ASSERT_EXCLUSIVE_CDDATA;
255
256 //
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.
260 //
261 // Acquire the global resource so that we can try to vaporize the volume,
262 // and the vcb resource itself.
263 //
264
265 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
266
267 //
268 // Drop a reference on the Vcb to keep it around after we drop the locks.
269 //
270
271 CdLockVcb( IrpContext, Vcb);
272 Vcb->VcbReference += 1;
273 CdUnlockVcb( IrpContext, Vcb);
274
275 CdReleaseCdData( IrpContext);
276
277 Status = CdLockVolumeInternal( IrpContext, Vcb, NULL );
278
279 //
280 // Reacquire the global lock, which means dropping the Vcb resource.
281 //
282
283 CdReleaseVcb( IrpContext, Vcb );
284
285 CdAcquireCdData( IrpContext );
286 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
287
288 //
289 // Remove our extra reference.
290 //
291
292 CdLockVcb( IrpContext, Vcb);
293 Vcb->VcbReference -= 1;
294 CdUnlockVcb( IrpContext, Vcb);
295
296 if (NT_SUCCESS( Status )) {
297
298 //
299 // We need to pass this down before starting the dismount, which
300 // could disconnect us immediately from the stack.
301 //
302
303 //
304 // Get the next stack location, and copy over the stack location
305 //
306
307 IoCopyCurrentIrpStackLocationToNext( Irp );
308
309 //
310 // Set up the completion routine
311 //
312
313 KeInitializeEvent( &Event, NotificationEvent, FALSE );
314 IoSetCompletionRoutine( Irp,
315 CdPnpCompletionRoutine,
316 &Event,
317 TRUE,
318 TRUE,
319 TRUE );
320
321 //
322 // Send the request and wait.
323 //
324
325 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
326
327 if (Status == STATUS_PENDING) {
328
329 KeWaitForSingleObject( &Event,
330 Executive,
331 KernelMode,
332 FALSE,
333 NULL );
334
335 Status = Irp->IoStatus.Status;
336 }
337
338 //
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.
342 //
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.
347 //
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!
352 //
353
354 if (NT_SUCCESS( Status )) {
355
356 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
357
358 ASSERT( !VcbPresent || Vcb->VcbCondition == VcbDismountInProgress );
359 }
360
361 //
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.
372 //
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.
379 //
380
381 if (NT_SUCCESS( Status) && VcbPresent && (Vcb->VcbReference != 0)) {
382
383 Status = STATUS_DEVICE_BUSY;
384 }
385 }
386
387 //
388 // Release the Vcb if it could still remain.
389 //
390
391 if (VcbPresent) {
392
393 CdReleaseVcb( IrpContext, Vcb );
394 }
395
396 CdReleaseCdData( IrpContext );
397
398 //
399 // Cleanup our IrpContext and complete the IRP if necessary.
400 //
401
402 CdCompleteRequest( IrpContext, Irp, Status );
403
404 return Status;
405 }
406
407
408 NTSTATUS
409 CdPnpRemove (
410 PIRP_CONTEXT IrpContext,
411 PIRP Irp,
412 PVCB Vcb
413 )
414
415 /*++
416
417 Routine Description:
418
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.
423
424 Arguments:
425
426 Irp - Supplies the Irp to process
427
428 Vcb - Supplies the volume being removed.
429
430 Return Value:
431
432 NTSTATUS - The return status for the operation
433
434 --*/
435
436 {
437 NTSTATUS Status;
438 KEVENT Event;
439 BOOLEAN VcbPresent = TRUE;
440
441 ASSERT_EXCLUSIVE_CDDATA;
442
443 //
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).
448 //
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
451 // dismount.
452 //
453
454 //
455 // Acquire the global resource so that we can try to vaporize
456 // the volume, and the vcb resource itself.
457 //
458
459 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
460
461 //
462 // The device will be going away. Remove our lock and find
463 // out if we ever had one in the first place.
464 //
465
466 Status = CdUnlockVolumeInternal( IrpContext, Vcb, NULL );
467
468 //
469 // If the volume had not been locked, we must invalidate the
470 // volume to ensure it goes away properly. The remove will
471 // succeed.
472 //
473
474 if (!NT_SUCCESS( Status )) {
475
476 CdLockVcb( IrpContext, Vcb );
477
478 if (Vcb->VcbCondition != VcbDismountInProgress) {
479
480 CdUpdateVcbCondition( Vcb, VcbInvalid);
481 }
482
483 CdUnlockVcb( IrpContext, Vcb );
484
485 Status = STATUS_SUCCESS;
486 }
487
488 //
489 // We need to pass this down before starting the dismount, which
490 // could disconnect us immediately from the stack.
491 //
492
493 //
494 // Get the next stack location, and copy over the stack location
495 //
496
497 IoCopyCurrentIrpStackLocationToNext( Irp );
498
499 //
500 // Set up the completion routine
501 //
502
503 KeInitializeEvent( &Event, NotificationEvent, FALSE );
504 IoSetCompletionRoutine( Irp,
505 CdPnpCompletionRoutine,
506 &Event,
507 TRUE,
508 TRUE,
509 TRUE );
510
511 //
512 // Send the request and wait.
513 //
514
515 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
516
517 if (Status == STATUS_PENDING) {
518
519 KeWaitForSingleObject( &Event,
520 Executive,
521 KernelMode,
522 FALSE,
523 NULL );
524
525 Status = Irp->IoStatus.Status;
526 }
527
528 //
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.
532 //
533 // PnP will take care of disconnecting this stack if we
534 // couldn't get off of it immediately.
535 //
536
537
538 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
539
540 //
541 // Release the Vcb if it could still remain.
542 //
543
544 if (VcbPresent) {
545
546 CdReleaseVcb( IrpContext, Vcb );
547 }
548
549 CdReleaseCdData( IrpContext );
550
551 //
552 // Cleanup our IrpContext and complete the IRP.
553 //
554
555 CdCompleteRequest( IrpContext, Irp, Status );
556
557 return Status;
558 }
559
560
561 NTSTATUS
562 CdPnpSurpriseRemove (
563 PIRP_CONTEXT IrpContext,
564 PIRP Irp,
565 PVCB Vcb
566 )
567
568 /*++
569
570 Routine Description:
571
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.
576
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.
580
581 Surprise will be followed by a Remove when all references have been shut down.
582
583 Arguments:
584
585 Irp - Supplies the Irp to process
586
587 Vcb - Supplies the volume being removed.
588
589 Return Value:
590
591 NTSTATUS - The return status for the operation
592
593 --*/
594
595 {
596 NTSTATUS Status;
597 KEVENT Event;
598 BOOLEAN VcbPresent = TRUE;
599
600 ASSERT_EXCLUSIVE_CDDATA;
601
602 //
603 // SURPRISE - a device was physically yanked away without
604 // any warning. This means external forces.
605 //
606
607 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
608
609 //
610 // Invalidate the volume right now.
611 //
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.
615 //
616
617 CdLockVcb( IrpContext, Vcb );
618
619 if (Vcb->VcbCondition != VcbDismountInProgress) {
620
621 CdUpdateVcbCondition( Vcb, VcbInvalid);
622 }
623
624 CdUnlockVcb( IrpContext, Vcb );
625
626 //
627 // We need to pass this down before starting the dismount, which
628 // could disconnect us immediately from the stack.
629 //
630
631 //
632 // Get the next stack location, and copy over the stack location
633 //
634
635 IoCopyCurrentIrpStackLocationToNext( Irp );
636
637 //
638 // Set up the completion routine
639 //
640
641 KeInitializeEvent( &Event, NotificationEvent, FALSE );
642 IoSetCompletionRoutine( Irp,
643 CdPnpCompletionRoutine,
644 &Event,
645 TRUE,
646 TRUE,
647 TRUE );
648
649 //
650 // Send the request and wait.
651 //
652
653 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
654
655 if (Status == STATUS_PENDING) {
656
657 KeWaitForSingleObject( &Event,
658 Executive,
659 KernelMode,
660 FALSE,
661 NULL );
662
663 Status = Irp->IoStatus.Status;
664 }
665
666 //
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.
670 //
671
672
673 VcbPresent = CdCheckForDismount( IrpContext, Vcb, TRUE );
674
675 //
676 // Release the Vcb if it could still remain.
677 //
678
679 if (VcbPresent) {
680
681 CdReleaseVcb( IrpContext, Vcb );
682 }
683
684 CdReleaseCdData( IrpContext );
685
686 //
687 // Cleanup our IrpContext and complete the IRP.
688 //
689
690 CdCompleteRequest( IrpContext, Irp, Status );
691
692 return Status;
693 }
694
695
696 NTSTATUS
697 CdPnpCancelRemove (
698 PIRP_CONTEXT IrpContext,
699 PIRP Irp,
700 PVCB Vcb
701 )
702
703 /*++
704
705 Routine Description:
706
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.
711
712 Arguments:
713
714 Irp - Supplies the Irp to process
715
716 Vcb - Supplies the volume being removed.
717
718 Return Value:
719
720 NTSTATUS - The return status for the operation
721
722 --*/
723
724 {
725 NTSTATUS Status;
726
727 ASSERT_EXCLUSIVE_CDDATA;
728
729 //
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
734 // seen the QUERY.
735 //
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.
743 //
744
745 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
746 CdReleaseCdData( IrpContext);
747
748 //
749 // Unlock the volume. This is benign if we never had seen
750 // a QUERY.
751 //
752
753 (VOID) CdUnlockVolumeInternal( IrpContext, Vcb, NULL );
754
755 CdReleaseVcb( IrpContext, Vcb );
756
757 //
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.
761 //
762
763 IoSkipCurrentIrpStackLocation( Irp );
764
765 Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
766
767 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
768
769 return Status;
770 }
771
772
773 //
774 // Local support routine
775 //
776
777 NTSTATUS
778 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
779 CdPnpCompletionRoutine (
780 IN PDEVICE_OBJECT DeviceObject,
781 IN PIRP Irp,
782 IN PVOID Contxt
783 )
784 {
785 PKEVENT Event = (PKEVENT) Contxt;
786
787 KeSetEvent( Event, 0, FALSE );
788
789 return STATUS_MORE_PROCESSING_REQUIRED;
790
791 UNREFERENCED_PARAMETER( DeviceObject );
792 UNREFERENCED_PARAMETER( Contxt );
793 }
794
795