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