xref: /reactos/drivers/filesystems/cdfs/verfysup.c (revision 34593d93)
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