1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 VerfySup.c
8
9 Abstract:
10
11 This module implements the Cdfs Verification routines.
12
13
14 --*/
15
16 #include "cdprocs.h"
17
18 //
19 // The Bug check file id for this module
20 //
21
22 #define BugCheckFileId (CDFS_BUG_CHECK_VERFYSUP)
23
24 #ifdef ALLOC_PRAGMA
25 #pragma alloc_text(PAGE, CdVerifyFcbOperation)
26 #pragma alloc_text(PAGE, CdVerifyVcb)
27 #endif
28
_Requires_lock_held_(_Global_critical_region_)29 _Requires_lock_held_(_Global_critical_region_)
30 NTSTATUS
31 CdPerformVerify (
32 _Inout_ PIRP_CONTEXT IrpContext,
33 _Inout_ PIRP Irp,
34 _In_ PDEVICE_OBJECT DeviceToVerify
35 )
36
37 /*++
38
39 Routine Description:
40
41 This routines performs an IoVerifyVolume operation and takes the
42 appropriate action. If the verify is successful then we send the originating
43 Irp off to an Ex Worker Thread. This routine is called from the exception handler.
44
45 No file system resources are held when this routine is called.
46
47 Arguments:
48
49 Irp - The irp to send off after all is well and done.
50
51 Device - The real device needing verification.
52
53 Return Value:
54
55 None.
56
57 --*/
58
59 {
60 PVCB Vcb;
61 NTSTATUS Status = STATUS_SUCCESS;
62 PIO_STACK_LOCATION IrpSp;
63
64 ASSERT_IRP_CONTEXT( IrpContext );
65 ASSERT_IRP( Irp );
66
67 //
68 // Check if this Irp has a status of Verify required and if it does
69 // then call the I/O system to do a verify.
70 //
71 // Skip the IoVerifyVolume if this is a mount or verify request
72 // itself. Trying a recursive mount will cause a deadlock with
73 // the DeviceObject->DeviceLock.
74 //
75
76 if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
77 ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) ||
78 (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) {
79
80 return CdFsdPostRequest( IrpContext, Irp );
81 }
82
83 //
84 // Extract a pointer to the Vcb from the VolumeDeviceObject.
85 // Note that since we have specifically excluded mount,
86 // requests, we know that IrpSp->DeviceObject is indeed a
87 // volume device object.
88 //
89
90 IrpSp = IoGetCurrentIrpStackLocation( Irp );
91
92 Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject,
93 VOLUME_DEVICE_OBJECT,
94 DeviceObject )->Vcb;
95 _SEH2_TRY {
96
97 //
98 // Send down the verify FSCTL. Note that this is sent to the
99 // currently mounted volume, which may not be this one.
100 //
101 // We will allow Raw to mount this volume if we were doing a
102 // an absolute DASD open.
103 //
104
105 Status = IoVerifyVolume( DeviceToVerify, CdOperationIsDasdOpen( IrpContext));
106
107 //
108 // Acquire the Vcb so we're working with a stable VcbCondition.
109 //
110
111 CdAcquireVcbShared( IrpContext, Vcb, FALSE);
112
113 //
114 // If the verify operation completed it will return
115 // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
116 //
117 // If CdVerifyVolume encountered an error during
118 // processing, it will return that error. If we got
119 // STATUS_WRONG_VOLUME from the verify, and our volume
120 // is now mounted, commute the status to STATUS_SUCCESS.
121 //
122
123 if ((Status == STATUS_WRONG_VOLUME) &&
124 (Vcb->VcbCondition == VcbMounted)) {
125
126 Status = STATUS_SUCCESS;
127 }
128 else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbMounted)) {
129
130 //
131 // If the verify succeeded, but our volume is not mounted,
132 // then some other volume is on the device.
133 //
134
135 Status = STATUS_WRONG_VOLUME;
136 }
137
138 //
139 // Do a quick unprotected check here. The routine will do
140 // a safe check. After here we can release the resource.
141 // Note that if the volume really went away, we will be taking
142 // the Reparse path.
143 //
144
145 //
146 // If the device might need to go away then call our dismount routine.
147 //
148
149 if (((Vcb->VcbCondition == VcbNotMounted) ||
150 (Vcb->VcbCondition == VcbInvalid) ||
151 (Vcb->VcbCondition == VcbDismountInProgress)) &&
152 (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE)) {
153
154 CdReleaseVcb( IrpContext, Vcb);
155
156 CdAcquireCdData( IrpContext );
157 CdCheckForDismount( IrpContext, Vcb, FALSE );
158 CdReleaseCdData( IrpContext );
159 }
160 else {
161
162 CdReleaseVcb( IrpContext, Vcb);
163 }
164
165 //
166 // If this is a create and the verify succeeded then complete the
167 // request with a REPARSE status.
168 //
169
170 if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
171 (IrpSp->FileObject->RelatedFileObject == NULL) &&
172 ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) {
173
174 Irp->IoStatus.Information = IO_REMOUNT;
175
176 CdCompleteRequest( IrpContext, Irp, STATUS_REPARSE );
177 Status = STATUS_REPARSE;
178 Irp = NULL;
179 IrpContext = NULL;
180
181 //
182 // If there is still an error to process then call the Io system
183 // for a popup.
184 //
185
186 } else if ((Irp != NULL) && !NT_SUCCESS( Status )) {
187
188 //
189 // Fill in the device object if required.
190 //
191
192 if (IoIsErrorUserInduced( Status ) ) {
193
194 IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify );
195 }
196
197 CdNormalizeAndRaiseStatus( IrpContext, Status );
198 }
199
200 //
201 // If there is still an Irp, send it off to an Ex Worker thread.
202 //
203
204 if (IrpContext != NULL) {
205
206 Status = CdFsdPostRequest( IrpContext, Irp );
207 }
208
209 } _SEH2_EXCEPT(CdExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
210
211 //
212 // We had some trouble trying to perform the verify or raised
213 // an error ourselves. So we'll abort the I/O request with
214 // the error status that we get back from the execption code.
215 //
216
217 Status = CdProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
218 } _SEH2_END;
219
220 return Status;
221 }
222
223
224
_Requires_lock_held_(_Global_critical_region_)225 _Requires_lock_held_(_Global_critical_region_)
226 BOOLEAN
227 CdCheckForDismount (
228 _In_ PIRP_CONTEXT IrpContext,
229 _Inout_ PVCB Vcb,
230 _In_ BOOLEAN Force
231 )
232
233 /*++
234
235 Routine Description:
236
237 This routine is called to check if a volume is ready for dismount. This
238 occurs when only file system references are left on the volume.
239
240 If the dismount is not currently underway and the user reference count
241 has gone to zero then we can begin the dismount.
242
243 If the dismount is in progress and there are no references left on the
244 volume (we check the Vpb for outstanding references as well to catch
245 any create calls dispatched to the file system) then we can delete
246 the Vcb.
247
248 Arguments:
249
250 Vcb - Vcb for the volume to try to dismount.
251
252 Force - Whether we will force this volume to be dismounted.
253
254 Return Value:
255
256 BOOLEAN - True if the Vcb was not gone by the time this function finished,
257 False if it was deleted.
258
259 This is only a trustworthy indication to the caller if it had the vcb
260 exclusive itself.
261
262 --*/
263
264 {
265 BOOLEAN UnlockVcb = TRUE;
266 BOOLEAN VcbPresent = TRUE;
267 KIRQL SavedIrql;
268
269 ASSERT_IRP_CONTEXT( IrpContext );
270 ASSERT_VCB( Vcb );
271
272 ASSERT_EXCLUSIVE_CDDATA;
273
274 //
275 // Acquire and lock this Vcb to check the dismount state.
276 //
277
278 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
279
280 //
281 // Lets get rid of any pending closes for this volume.
282 //
283
284 CdFspClose( Vcb );
285
286 CdLockVcb( IrpContext, Vcb );
287
288 //
289 // If the dismount is not already underway then check if the
290 // user reference count has gone to zero or we are being forced
291 // to disconnect. If so start the teardown on the Vcb.
292 //
293
294 if (Vcb->VcbCondition != VcbDismountInProgress) {
295
296 if (Vcb->VcbUserReference <= CDFS_RESIDUAL_USER_REFERENCE || Force) {
297
298 CdUnlockVcb( IrpContext, Vcb );
299 UnlockVcb = FALSE;
300 VcbPresent = CdDismountVcb( IrpContext, Vcb );
301 }
302
303 //
304 // If the teardown is underway and there are absolutely no references
305 // remaining then delete the Vcb. References here include the
306 // references in the Vcb and Vpb.
307 //
308
309 } else if (Vcb->VcbReference == 0) {
310
311 IoAcquireVpbSpinLock( &SavedIrql );
312
313 //
314 // If there are no file objects and no reference counts in the
315 // Vpb we can delete the Vcb. Don't forget that we have the
316 // last reference in the Vpb.
317 //
318
319 if (Vcb->Vpb->ReferenceCount == 1) {
320
321 IoReleaseVpbSpinLock( SavedIrql );
322 CdUnlockVcb( IrpContext, Vcb );
323 UnlockVcb = FALSE;
324 CdDeleteVcb( IrpContext, Vcb );
325 VcbPresent = FALSE;
326
327 } else {
328
329 IoReleaseVpbSpinLock( SavedIrql );
330 }
331 }
332
333 //
334 // Unlock the Vcb if still held.
335 //
336
337 if (UnlockVcb) {
338
339 CdUnlockVcb( IrpContext, Vcb );
340 }
341
342 //
343 // Release any resources still acquired.
344 //
345
346 if (VcbPresent) {
347
348 CdReleaseVcb( IrpContext, Vcb );
349 }
350 else {
351 _Analysis_assume_lock_not_held_(Vcb->VcbResource);
352 }
353
354 return VcbPresent;
355 }
356
357
358 BOOLEAN
CdMarkDevForVerifyIfVcbMounted(_Inout_ PVCB Vcb)359 CdMarkDevForVerifyIfVcbMounted (
360 _Inout_ PVCB Vcb
361 )
362
363 /*++
364
365 Routine Description:
366
367 This routine checks to see if the specified Vcb is currently mounted on
368 the device or not. If it is, it sets the verify flag on the device, if
369 not then the state is noted in the Vcb.
370
371 Arguments:
372
373 Vcb - This is the volume to check.
374
375 Return Value:
376
377 TRUE if the device has been marked for verify here, FALSE otherwise.
378
379 --*/
380
381 {
382 BOOLEAN Marked = FALSE;
383 KIRQL SavedIrql;
384
385 IoAcquireVpbSpinLock( &SavedIrql );
386
387 #ifdef _MSC_VER
388 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
389 #endif
390 if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) {
391
392 CdMarkRealDevForVerify( Vcb->Vpb->RealDevice);
393 Marked = TRUE;
394 }
395 else {
396
397 //
398 // Flag this to avoid the VPB spinlock in future passes.
399 //
400
401 SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
402 }
403
404 IoReleaseVpbSpinLock( SavedIrql );
405
406 return Marked;
407 }
408
409
410 VOID
CdVerifyVcb(_In_ PIRP_CONTEXT IrpContext,_Inout_ PVCB Vcb)411 CdVerifyVcb (
412 _In_ PIRP_CONTEXT IrpContext,
413 _Inout_ PVCB Vcb
414 )
415
416 /*++
417
418 Routine Description:
419
420 This routine checks that the current Vcb is valid and currently mounted
421 on the device. It will raise on an error condition.
422
423 We check whether the volume needs verification and the current state
424 of the Vcb.
425
426 Arguments:
427
428 Vcb - This is the volume to verify.
429
430 Return Value:
431
432 None
433
434 --*/
435
436 {
437 NTSTATUS Status = STATUS_SUCCESS;
438 IO_STATUS_BLOCK Iosb;
439 ULONG MediaChangeCount = 0;
440 BOOLEAN ForceVerify = FALSE;
441 BOOLEAN DevMarkedForVerify;
442
443 PAGED_CODE();
444
445 //
446 // Fail immediately if the volume is in the progress of being dismounted
447 // or has been marked invalid.
448 //
449
450 if ((Vcb->VcbCondition == VcbInvalid) ||
451 ((Vcb->VcbCondition == VcbDismountInProgress) &&
452 (IrpContext->MajorFunction != IRP_MJ_CREATE))) {
453
454 if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) {
455
456 CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
457
458 } else {
459
460 CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
461 }
462 }
463
464 //
465 // Capture the real device verify state.
466 //
467
468 DevMarkedForVerify = CdRealDevNeedsVerify( Vcb->Vpb->RealDevice);
469
470 if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ) && !DevMarkedForVerify) {
471
472 //
473 // If the media is removable and the verify volume flag in the
474 // device object is not set then we want to ping the device
475 // to see if it needs to be verified.
476 //
477
478 if (Vcb->VcbCondition != VcbMountInProgress) {
479
480 Status = CdPerformDevIoCtrl( IrpContext,
481 IOCTL_CDROM_CHECK_VERIFY,
482 Vcb->TargetDeviceObject,
483 &MediaChangeCount,
484 sizeof(ULONG),
485 FALSE,
486 FALSE,
487 &Iosb );
488
489 if (Iosb.Information != sizeof(ULONG)) {
490
491 //
492 // Be safe about the count in case the driver didn't fill it in
493 //
494
495 MediaChangeCount = 0;
496 }
497
498 //
499 // There are four cases when we want to do a verify. These are the
500 // first three.
501 //
502 // 1. We are mounted, and the device has become empty
503 // 2. The device has returned verify required (=> DO_VERIFY_VOL flag is
504 // set, but could be due to hardware condition)
505 // 3. Media change count doesn't match the one in the Vcb
506 //
507
508 if (((Vcb->VcbCondition == VcbMounted) &&
509 CdIsRawDevice( IrpContext, Status ))
510 ||
511 (Status == STATUS_VERIFY_REQUIRED)
512 ||
513 (NT_SUCCESS(Status) &&
514 (Vcb->MediaChangeCount != MediaChangeCount))) {
515
516 //
517 // If we are currently the volume on the device then it is our
518 // responsibility to set the verify flag. If we're not on the device,
519 // then we shouldn't touch the flag.
520 //
521
522 if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) &&
523 !DevMarkedForVerify) {
524
525 DevMarkedForVerify = CdMarkDevForVerifyIfVcbMounted( Vcb);
526 }
527
528 ForceVerify = TRUE;
529
530 //
531 // NOTE that we no longer update the media change count here. We
532 // do so only when we've actually completed a verify at a particular
533 // change count value.
534 //
535 }
536 }
537
538 //
539 // This is the 4th verify case.
540 //
541 // We ALWAYS force CREATE requests on unmounted volumes through the
542 // verify path. These requests could have been in limbo between
543 // IoCheckMountedVpb and us when a verify/mount took place and caused
544 // a completely different fs/volume to be mounted. In this case the
545 // checks above may not have caught the condition, since we may already
546 // have verified (wrong volume) and decided that we have nothing to do.
547 // We want the requests to be re routed to the currently mounted volume,
548 // since they were directed at the 'drive', not our volume.
549 //
550
551 if (NT_SUCCESS( Status) && !ForceVerify && !DevMarkedForVerify &&
552 (IrpContext->MajorFunction == IRP_MJ_CREATE)) {
553
554 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp);
555
556 ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) &&
557 ((Vcb->VcbCondition == VcbDismountInProgress) ||
558 (Vcb->VcbCondition == VcbNotMounted));
559
560 //
561 // Note that we don't touch the device verify flag here. It required
562 // it would have been caught and set by the first set of checks.
563 //
564 }
565 }
566
567 //
568 // Raise the verify / error if neccessary.
569 //
570
571 if (ForceVerify || DevMarkedForVerify || !NT_SUCCESS( Status)) {
572
573 IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
574 Vcb->Vpb->RealDevice );
575
576 CdRaiseStatus( IrpContext, (ForceVerify || DevMarkedForVerify)
577 ? STATUS_VERIFY_REQUIRED
578 : Status);
579 }
580
581 //
582 // Based on the condition of the Vcb we'll either return to our
583 // caller or raise an error condition
584 //
585
586 switch (Vcb->VcbCondition) {
587
588 case VcbNotMounted:
589
590 IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice );
591
592 CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
593 break;
594
595 case VcbInvalid:
596 case VcbDismountInProgress:
597
598 if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) {
599
600 CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
601
602 } else {
603
604 CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
605 }
606 break;
607
608 /* ReactOS Change: GCC "enumeration value not handled in switch" */
609 default: break;
610 }
611 }
612
613
614 BOOLEAN
CdVerifyFcbOperation(_In_opt_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb)615 CdVerifyFcbOperation (
616 _In_opt_ PIRP_CONTEXT IrpContext,
617 _In_ PFCB Fcb
618 )
619
620 /*++
621
622 Routine Description:
623
624 This routine is called to verify that the state of the Fcb is valid
625 to allow the current operation to continue. We use the state of the
626 Vcb, target device and type of operation to determine this.
627
628 Arguments:
629
630 IrpContext - IrpContext for the request. If not present then we
631 were called from the fast IO path.
632
633 Fcb - Fcb to perform the request on.
634
635 Return Value:
636
637 BOOLEAN - TRUE if the request can continue, FALSE otherwise.
638
639 --*/
640
641 {
642 PVCB Vcb = Fcb->Vcb;
643 PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice;
644 PIRP Irp;
645
646 PAGED_CODE();
647
648 //
649 // Check that the fileobject has not been cleaned up.
650 //
651
652 if ( ARGUMENT_PRESENT( IrpContext )) {
653
654 PFILE_OBJECT FileObject;
655
656 Irp = IrpContext->Irp;
657 FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject;
658
659 if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE)) {
660
661 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
662
663 //
664 // Following FAT, we allow certain operations even on cleaned up
665 // file objects. Everything else, we fail.
666 //
667
668 if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
669 (IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
670 (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
671 ( (IrpSp->MajorFunction == IRP_MJ_READ) &&
672 FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {
673
674 NOTHING;
675
676 } else {
677
678 CdRaiseStatus( IrpContext, STATUS_FILE_CLOSED );
679 }
680 }
681 }
682
683 //
684 // Fail immediately if the volume is in the progress of being dismounted
685 // or has been marked invalid.
686 //
687
688 if ((Vcb->VcbCondition == VcbInvalid) ||
689 (Vcb->VcbCondition == VcbDismountInProgress)) {
690
691 if (ARGUMENT_PRESENT( IrpContext )) {
692
693 if (FlagOn( Vcb->VcbState, VCB_STATE_DISMOUNTED )) {
694
695 CdRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED );
696
697 } else {
698
699 CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
700 }
701 }
702
703 return FALSE;
704 }
705
706 //
707 // Always fail if the volume needs to be verified.
708 //
709
710 if (CdRealDevNeedsVerify( RealDevice)) {
711
712 if (ARGUMENT_PRESENT( IrpContext )) {
713
714 IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
715 RealDevice );
716
717 CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
718 }
719
720 return FALSE;
721
722 //
723 //
724 // All operations are allowed on mounted.
725 //
726
727 } else if ((Vcb->VcbCondition == VcbMounted) ||
728 (Vcb->VcbCondition == VcbMountInProgress)) {
729
730 return TRUE;
731
732 //
733 // Fail all requests for fast Io on other Vcb conditions.
734 //
735
736 } else if (!ARGUMENT_PRESENT( IrpContext )) {
737
738 return FALSE;
739
740 //
741 // The remaining case is VcbNotMounted.
742 // Mark the device to be verified and raise WRONG_VOLUME.
743 //
744
745 } else if (Vcb->VcbCondition == VcbNotMounted) {
746
747 IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice );
748 CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
749
750 // return FALSE; // unreachable code
751 }
752
753 return TRUE;
754 }
755
756
757
_Requires_lock_held_(_Global_critical_region_)758 _Requires_lock_held_(_Global_critical_region_)
759 BOOLEAN
760 CdDismountVcb (
761 _In_ PIRP_CONTEXT IrpContext,
762 _Inout_ PVCB Vcb
763 )
764
765 /*++
766
767 Routine Description:
768
769 This routine is called when all of the user references to a volume are
770 gone. We will initiate all of the teardown any system resources.
771
772 If all of the references to this volume are gone at the end of this routine
773 then we will complete the teardown of this Vcb and mark the current Vpb
774 as not mounted. Otherwise we will allocated a new Vpb for this device
775 and keep the current Vpb attached to the Vcb.
776
777 Arguments:
778
779 Vcb - Vcb for the volume to dismount.
780
781 Return Value:
782
783 BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise.
784
785 --*/
786
787 {
788 PVPB OldVpb;
789 BOOLEAN VcbPresent = TRUE;
790 KIRQL SavedIrql;
791
792 BOOLEAN FinalReference;
793
794 ASSERT_EXCLUSIVE_CDDATA;
795 ASSERT_EXCLUSIVE_VCB( Vcb );
796
797 CdLockVcb( IrpContext, Vcb );
798
799 //
800 // We should only take this path once.
801 //
802
803 NT_ASSERT( Vcb->VcbCondition != VcbDismountInProgress );
804
805 //
806 // Mark the Vcb as DismountInProgress.
807 //
808
809 Vcb->VcbCondition = VcbDismountInProgress;
810
811 if (Vcb->XASector != NULL) {
812
813 CdFreePool( &Vcb->XASector );
814 Vcb->XASector = 0;
815 Vcb->XADiskOffset = 0;
816 }
817
818 //
819 // Remove our reference to the internal Fcb's. The Fcb's will then
820 // be removed in the purge path below.
821 //
822
823 if (Vcb->RootIndexFcb != NULL) {
824
825 Vcb->RootIndexFcb->FcbReference -= 1;
826 Vcb->RootIndexFcb->FcbUserReference -= 1;
827 }
828
829 if (Vcb->PathTableFcb != NULL) {
830
831 Vcb->PathTableFcb->FcbReference -= 1;
832 Vcb->PathTableFcb->FcbUserReference -= 1;
833 }
834
835 if (Vcb->VolumeDasdFcb != NULL) {
836
837 Vcb->VolumeDasdFcb->FcbReference -= 1;
838 Vcb->VolumeDasdFcb->FcbUserReference -= 1;
839 }
840
841 CdUnlockVcb( IrpContext, Vcb );
842
843 //
844 // Purge the volume.
845 //
846
847 CdPurgeVolume( IrpContext, Vcb, TRUE );
848
849 //
850 // Empty the delayed and async close queues.
851 //
852
853 CdFspClose( Vcb );
854
855 OldVpb = Vcb->Vpb;
856
857 //
858 // Remove the mount volume reference.
859 //
860
861 CdLockVcb( IrpContext, Vcb );
862 Vcb->VcbReference -= 1;
863
864 //
865 // Acquire the Vpb spinlock to check for Vpb references.
866 //
867
868 IoAcquireVpbSpinLock( &SavedIrql );
869
870 //
871 // Remember if this is the last reference on this Vcb. We incremented
872 // the count on the Vpb earlier so we get one last crack it. If our
873 // reference has gone to zero but the vpb reference count is greater
874 // than zero then the Io system will be responsible for deleting the
875 // Vpb.
876 //
877
878 FinalReference = (BOOLEAN) ((Vcb->VcbReference == 0) &&
879 (OldVpb->ReferenceCount == 1));
880
881 //
882 // There is a reference count in the Vpb and in the Vcb. We have
883 // incremented the reference count in the Vpb to make sure that
884 // we have last crack at it. If this is a failed mount then we
885 // want to return the Vpb to the IO system to use for the next
886 // mount request.
887 //
888
889 #ifdef _MSC_VER
890 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
891 #endif
892 if (OldVpb->RealDevice->Vpb == OldVpb) {
893
894 //
895 // If not the final reference then swap out the Vpb. We must
896 // preserve the REMOVE_PENDING flag so that the device is
897 // not remounted in the middle of a PnP remove operation.
898 //
899
900 if (!FinalReference) {
901
902 NT_ASSERT( Vcb->SwapVpb != NULL );
903
904 Vcb->SwapVpb->Type = IO_TYPE_VPB;
905 Vcb->SwapVpb->Size = sizeof( VPB );
906
907 #ifdef _MSC_VER
908 #pragma prefast(push)
909 #pragma prefast(disable: 28175, "this is a filesystem driver, touching the vpb is allowed")
910 #endif
911 Vcb->SwapVpb->RealDevice = OldVpb->RealDevice;
912 Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb;
913 #ifdef _MSC_VER
914 #pragma prefast(pop)
915 #endif
916
917 Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING );
918
919 IoReleaseVpbSpinLock( SavedIrql );
920
921 //
922 // Indicate we used up the swap.
923 //
924
925 Vcb->SwapVpb = NULL;
926
927 CdUnlockVcb( IrpContext, Vcb );
928
929 //
930 // We want to leave the Vpb for the IO system. Mark it
931 // as being not mounted. Go ahead and delete the Vcb as
932 // well.
933 //
934
935 } else {
936
937 //
938 // Make sure to remove the last reference on the Vpb.
939 //
940
941 OldVpb->ReferenceCount -= 1;
942
943 OldVpb->DeviceObject = NULL;
944 ClearFlag( Vcb->Vpb->Flags, VPB_MOUNTED );
945 ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED );
946
947 //
948 // Clear the Vpb flag so we know not to delete it.
949 //
950
951 Vcb->Vpb = NULL;
952
953 IoReleaseVpbSpinLock( SavedIrql );
954 CdUnlockVcb( IrpContext, Vcb );
955 CdDeleteVcb( IrpContext, Vcb );
956 VcbPresent = FALSE;
957 }
958
959 //
960 // Someone has already swapped in a new Vpb. If this is the final reference
961 // then the file system is responsible for deleting the Vpb.
962 //
963
964 } else if (FinalReference) {
965
966 //
967 // Make sure to remove the last reference on the Vpb.
968 //
969
970 OldVpb->ReferenceCount -= 1;
971
972 IoReleaseVpbSpinLock( SavedIrql );
973 CdUnlockVcb( IrpContext, Vcb );
974 CdDeleteVcb( IrpContext, Vcb );
975 VcbPresent = FALSE;
976
977 //
978 // The current Vpb is no longer the Vpb for the device (the IO system
979 // has already allocated a new one). We leave our reference in the
980 // Vpb and will be responsible for deleting it at a later time.
981 //
982
983 } else {
984
985 IoReleaseVpbSpinLock( SavedIrql );
986 CdUnlockVcb( IrpContext, Vcb );
987 }
988
989 //
990 // Let our caller know whether the Vcb is still present.
991 //
992
993 return VcbPresent;
994 }
995
996