1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*
7 
8  Module Name: VerfySup.cpp
9 
10  Abstract:
11 
12     This module implements the UDF verification routines.
13 
14  Environment:
15 
16     Kernel mode only
17 
18 */
19 
20 #include            "udffs.h"
21 
22 // define the file specific bug-check id
23 #define         UDF_BUG_CHECK_ID    UDF_FILE_VERIFY_FS_CONTROL
24 
25 /*
26 Routine Description:
27     This routine checks that the current Vcb is valid and currently mounted
28     on the device.  It will raise on an error condition.
29     We check whether the volume needs verification and the current state
30     of the Vcb.
31 Arguments:
32 
33     Vcb - This is the volume to verify.
34 */
35 
36 NTSTATUS
UDFVerifyVcb(IN PtrUDFIrpContext IrpContext,IN PVCB Vcb)37 UDFVerifyVcb(
38     IN PtrUDFIrpContext IrpContext,
39     IN PVCB Vcb
40     )
41 {
42     NTSTATUS RC = STATUS_SUCCESS;
43     IO_STATUS_BLOCK Iosb;
44     ULONG MediaChangeCount = 0;
45     BOOLEAN Nop = TRUE;
46     BOOLEAN UnsafeIoctl = (Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL) ? TRUE : FALSE;
47 
48     UDFPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified));
49     //  Fail immediately if the volume is in the progress of being dismounted
50     //  or has been marked invalid.
51     if (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) {
52         return STATUS_FILE_INVALID;
53     }
54 
55     //  If the media is removable and the verify volume flag in the
56     //  device object is not set then we want to ping the device
57     //  to see if it needs to be verified
58     if ( (Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) &&
59         !(Vcb->Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) &&
60         (!(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) || UnsafeIoctl) ) {
61         UDFPrint(("UDFVerifyVCB: UnsafeIoctl=%d, locked=%d\n", UnsafeIoctl, (Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) ? 0 : 1));
62         Vcb->VCBFlags &= ~UDF_VCB_FLAGS_UNSAFE_IOCTL;
63         RC = UDFTSendIOCTL( IOCTL_STORAGE_CHECK_VERIFY,
64                                      Vcb,
65                                      NULL,0,
66                                      &MediaChangeCount,sizeof(ULONG),
67                                      FALSE,&Iosb );
68 
69         //  Be safe about the count in case the driver didn't fill it in
70         if (Iosb.Information != sizeof(ULONG))  MediaChangeCount = 0;
71         UDFPrint(("  MediaChangeCount %d -> %d\n", Vcb->MediaChangeCount, MediaChangeCount));
72 
73         //  If the volume is now an empty device, or we have receieved a
74         //  bare STATUS_VERIFY_REQUIRED (various hardware conditions such
75         //  as bus resets, etc., will trigger this in the drivers), or the
76         //  media change count has moved since we last inspected the device,
77         //  then mark the volume to be verified.
78 
79         if ( (RC == STATUS_VERIFY_REQUIRED) ||
80              (UDFIsRawDevice(RC) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) ||
81              (NT_SUCCESS(RC) && (Vcb->MediaChangeCount != MediaChangeCount)) ||
82              UnsafeIoctl) {
83 
84             UDFPrint(("  set DO_VERIFY_VOLUME\n"));
85             Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME;
86 
87             //  If the volume is not mounted and we got a media change count,
88             //  update the Vcb so we do not trigger a verify again at this
89             //  count value.  If the verify->mount path detects that the media
90             //  has actually changed and this Vcb is valid again, this will have
91             //  done nothing.  We are already synchronized since the caller has
92             //  the Vcb.
93             if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) &&
94                 NT_SUCCESS(RC) ) {
95                 Vcb->MediaChangeCount = MediaChangeCount;
96             }
97 
98         } else if (!NT_SUCCESS(RC)) {
99 //            Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME;
100             UDFPrint(("  UDFNormalizeAndRaiseStatus(%x)\n", RC));
101             UDFNormalizeAndRaiseStatus(IrpContext,RC);
102             ASSERT(Nop);
103         }
104     }
105 
106     UDFPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified));
107     //  The Vcb may be mounted but the underlying real device may need to be verified.
108     //  If it does then we'll set the Iosb in the irp to be our real device
109     if (Vcb->Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) {
110 
111         UDFPrint(("  DO_VERIFY_VOLUME -> IoSetHardErrorOrVerifyDevice()\n"));
112         IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
113                                       Vcb->Vpb->RealDevice );
114 
115         RC = STATUS_VERIFY_REQUIRED;
116         UDFPrint(("  UDFRaiseStatus()\n"));
117         UDFRaiseStatus(IrpContext, RC);
118         ASSERT(Nop);
119     }
120 
121     UDFPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified));
122     if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) {
123         UDFPrint(("  !UDF_VCB_FLAGS_VOLUME_MOUNTED -> IoSetHardErrorOrVerifyDevice()\n"));
124         Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME;
125         IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice );
126         RC = STATUS_WRONG_VOLUME;
127         UDFPrint(("  UDFRaiseStatus()\n"));
128         UDFRaiseStatus(IrpContext, RC);
129 //        UDFRaiseStatus(IrpContext, STATUS_UNRECOGNIZED_VOLUME);
130         ASSERT(Nop);
131     }
132     if ((Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) {
133         UDFPrint(("  UDF_VCB_FLAGS_BEING_DISMOUNTED\n"));
134         RC = STATUS_FILE_INVALID;
135         UDFRaiseStatus( IrpContext, RC );
136         ASSERT(Nop);
137     }
138     UDFPrint(("UDFVerifyVcb: RC = %x\n", RC));
139 
140     return RC;
141 } // end UDFVerifyVcb()
142 
143 /*
144 
145 Routine Description:
146     This routine performs the verify volume operation.  It is responsible for
147     either completing of enqueuing the input Irp.
148 
149 Arguments:
150     Irp - Supplies the Irp to process
151 
152 Return Value:
153 
154     NTSTATUS - The return status for the operation
155 
156 --*/
157 NTSTATUS
UDFVerifyVolume(IN PIRP Irp)158 UDFVerifyVolume(
159     IN PIRP Irp
160     )
161 {
162     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
163     PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
164     PVCB Vcb = (PVCB)IrpSp->Parameters.VerifyVolume.DeviceObject->DeviceExtension;
165     PVCB NewVcb = NULL;
166     IO_STATUS_BLOCK Iosb;
167     ULONG MediaChangeCount = 0;
168     NTSTATUS RC;
169     ULONG Mode;
170     BOOLEAN UnsafeIoctl = (Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL) ? TRUE : FALSE;
171 
172     //  Update the real device in the IrpContext from the Vpb.  There was no available
173     //  file object when the IrpContext was created.
174     //    IrpContext->RealDevice = Vpb->RealDevice;
175     UDFPrint(("UDFVerifyVolume:\n"));
176 
177     //  Acquire shared global access, the termination handler for the
178     //  following try statement will free the access.
179 
180     UDFAcquireResourceShared(&(UDFGlobalData.GlobalDataResource),TRUE);
181     UDFAcquireResourceExclusive(&(Vcb->VCBResource),TRUE);
182 
183     _SEH2_TRY {
184 
185         UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
186         // Check if the real device still needs to be verified.  If it doesn't
187         // then obviously someone beat us here and already did the work
188         // so complete the verify irp with success.  Otherwise reenable
189         // the real device and get to work.
190         if( !(Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) &&
191             ((Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) && !UnsafeIoctl) ) {
192             UDFPrint(("UDFVerifyVolume: STATUS_SUCCESS (1)\n"));
193             try_return(RC = STATUS_SUCCESS);
194         }
195         Vcb->VCBFlags &= ~UDF_VCB_FLAGS_UNSAFE_IOCTL;
196         // Verify that there is a disk here.
197         RC = UDFPhSendIOCTL( IOCTL_STORAGE_CHECK_VERIFY,
198                                  Vcb->TargetDeviceObject,
199                                  NULL,0,
200                                  &MediaChangeCount,sizeof(ULONG),
201                                  TRUE,&Iosb );
202 
203         if(!NT_SUCCESS( RC )) {
204             // If we will allow a raw mount then return WRONG_VOLUME to
205             // allow the volume to be mounted by raw.
206             if(FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) {
207                 UDFPrint(("UDFVerifyVolume: STATUS_WRONG_VOLUME (1)\n"));
208                 RC = STATUS_WRONG_VOLUME;
209             }
210 
211             if(UDFIsRawDevice(RC)) {
212                 UDFPrint(("UDFVerifyVolume: STATUS_WRONG_VOLUME (2)\n"));
213                 RC = STATUS_WRONG_VOLUME;
214             }
215             try_return( RC );
216         }
217 
218         if(Iosb.Information != sizeof(ULONG)) {
219             // Be safe about the count in case the driver didn't fill it in
220             MediaChangeCount = 0;
221         }
222 
223         UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
224         UDFPrint(("UDFVerifyVolume: MediaChangeCount=%x, Vcb->MediaChangeCount=%x, UnsafeIoctl=%x\n",
225             MediaChangeCount, Vcb->MediaChangeCount, UnsafeIoctl));
226         // Verify that the device actually saw a change. If the driver does not
227         // support the MCC, then we must verify the volume in any case.
228         if(MediaChangeCount == 0 ||
229             (Vcb->MediaChangeCount != MediaChangeCount) ||
230            UnsafeIoctl ) {
231 
232             UDFPrint(("UDFVerifyVolume: compare\n"));
233 
234             NewVcb = (PVCB)MyAllocatePool__(NonPagedPool,sizeof(VCB));
235             if(!NewVcb)
236                 try_return(RC=STATUS_INSUFFICIENT_RESOURCES);
237             RtlZeroMemory(NewVcb,sizeof(VCB));
238 
239             NewVcb->TargetDeviceObject = Vcb->TargetDeviceObject;
240             NewVcb->Vpb = Vpb;
241 
242             // Set the removable media flag based on the real device's
243             // characteristics
244             if(Vpb->RealDevice->Characteristics & FILE_REMOVABLE_MEDIA) {
245                 UDFSetFlag( NewVcb->VCBFlags, UDF_VCB_FLAGS_REMOVABLE_MEDIA );
246             }
247 
248             RC = UDFGetDiskInfo(NewVcb->TargetDeviceObject,NewVcb);
249             if(!NT_SUCCESS(RC)) try_return(RC);
250             // Prevent modification attempts durring Verify
251             NewVcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY |
252                                 UDF_VCB_FLAGS_MEDIA_READ_ONLY;
253             // Compare physical parameters (phase 1)
254             UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
255             RC = UDFCompareVcb(Vcb,NewVcb, TRUE);
256             if(!NT_SUCCESS(RC)) try_return(RC);
257 
258             if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
259                 Vcb->MountPhErrorCount > MOUNT_ERR_THRESHOLD ) {
260                 UDFPrint(("UDFVerifyVolume: it was very BAD volume. Do not perform Logical check\n"));
261                 goto skip_logical_check;
262             }
263             // Initialize internal cache
264             // in *** READ ONLY *** mode
265             Mode = WCACHE_MODE_ROM;
266 
267             RC = WCacheInit__(&(NewVcb->FastCache),
268                               UDFGlobalData.WCacheMaxFrames,
269                               UDFGlobalData.WCacheMaxBlocks,
270                               NewVcb->WriteBlockSize,
271                               5, NewVcb->BlockSizeBits,
272                               UDFGlobalData.WCacheBlocksPerFrameSh,
273                               0/*NewVcb->FirstLBA*/, NewVcb->LastPossibleLBA, Mode,
274                                   /*WCACHE_CACHE_WHOLE_PACKET*/ 0 |
275                                   (Vcb->DoNotCompareBeforeWrite ? WCACHE_DO_NOT_COMPARE : 0) |
276                                   WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS, // speed up mount on bad disks
277                               UDFGlobalData.WCacheFramesToKeepFree,
278                               UDFTWrite, UDFTRead,
279 #ifdef UDF_ASYNC_IO
280                           UDFTWriteAsync, UDFTReadAsync,
281 #else  //UDF_ASYNC_IO
282                           NULL, NULL,
283 #endif //UDF_ASYNC_IO
284                               UDFIsBlockAllocated, UDFUpdateVAT,
285                               UDFWCacheErrorHandler);
286             if(!NT_SUCCESS(RC)) try_return(RC);
287 
288             UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
289             RC = UDFGetDiskInfoAndVerify(NewVcb->TargetDeviceObject,NewVcb);
290             UDFPrint(("  NewVcb->NSRDesc=%x\n", NewVcb->NSRDesc));
291             if(!NT_SUCCESS(RC)) {
292                 if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
293                    (NewVcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
294                    !(NewVcb->NSRDesc & VRS_ISO9660_FOUND)) {
295                     UDFPrint(("UDFVerifyVolume: both are RAW -> remount\n", Vcb->Modified));
296                     RC = STATUS_SUCCESS;
297                     goto skip_logical_check;
298                 }
299                 if(RC == STATUS_UNRECOGNIZED_VOLUME) {
300                     try_return(RC = STATUS_WRONG_VOLUME);
301                 }
302                 try_return(RC);
303             }
304 
305             WCacheChFlags__(&(Vcb->FastCache),
306                             WCACHE_CACHE_WHOLE_PACKET, // enable cache whole packet
307                             WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS);  // let user retry request on Bad Blocks
308 
309             NewVcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_MOUNTED;
310             // Compare logical parameters (phase 2)
311             UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
312             RC = UDFCompareVcb(Vcb,NewVcb, FALSE);
313             if(!NT_SUCCESS(RC)) try_return(RC);
314             // We have unitialized WCache, so it is better to
315             // force MOUNT_VOLUME call
316             if(!WCacheIsInitialized__(&(Vcb->FastCache)))
317                 try_return(RC = STATUS_WRONG_VOLUME);
318 
319 skip_logical_check:;
320 
321         }
322 
323         UDFPrint(("UDFVerifyVolume: compared\n"));
324         UDFPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified));
325         if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) {
326             UDFPrint(("UDFVerifyVolume: set UDF_VCB_FLAGS_VOLUME_MOUNTED\n"));
327             Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_MOUNTED;
328             Vcb->SoftEjectReq = FALSE;
329         }
330         UDFClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
331 
332 try_exit: NOTHING;
333 
334     } _SEH2_FINALLY {
335 
336         // Update the media change count to note that we have verified the volume
337         // at this value
338         Vcb->MediaChangeCount = MediaChangeCount;
339 
340         // If we got the wrong volume, mark the Vcb as not mounted.
341         if(RC == STATUS_WRONG_VOLUME) {
342             UDFPrint(("UDFVerifyVolume: clear UDF_VCB_FLAGS_VOLUME_MOUNTED\n"));
343             Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;
344             Vcb->WriteSecurity = FALSE;
345 //            ASSERT(!(Vcb->EjectWaiter));
346             if(Vcb->EjectWaiter) {
347                 UDFReleaseResource(&(Vcb->VCBResource));
348                 UDFStopEjectWaiter(Vcb);
349                 UDFAcquireResourceExclusive(&(Vcb->VCBResource),TRUE);
350             }
351         } else
352         if(NT_SUCCESS(RC) &&
353            (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)){
354             BOOLEAN CacheInitialized = FALSE;
355             UDFPrint(("    !!! VerifyVolume - QUICK REMOUNT !!!\n"));
356             // Initialize internal cache
357             CacheInitialized = WCacheIsInitialized__(&(Vcb->FastCache));
358             if(!CacheInitialized) {
359                 Mode = WCACHE_MODE_ROM;
360                 RC = WCacheInit__(&(Vcb->FastCache),
361                                   Vcb->WCacheMaxFrames,
362                                   Vcb->WCacheMaxBlocks,
363                                   Vcb->WriteBlockSize,
364                                   5, Vcb->BlockSizeBits,
365                               Vcb->WCacheBlocksPerFrameSh,
366                               0/*Vcb->FirstLBA*/, Vcb->LastPossibleLBA, Mode,
367                                   /*WCACHE_CACHE_WHOLE_PACKET*/ 0 |
368                                   (Vcb->DoNotCompareBeforeWrite ? WCACHE_DO_NOT_COMPARE : 0) |
369                                   (Vcb->CacheChainedIo ? WCACHE_CHAINED_IO : 0),
370                               Vcb->WCacheFramesToKeepFree,
371 //                              UDFTWrite, UDFTRead,
372                               UDFTWriteVerify, UDFTReadVerify,
373 #ifdef UDF_ASYNC_IO
374                                   UDFTWriteAsync, UDFTReadAsync,
375 #else  //UDF_ASYNC_IO
376                                   NULL, NULL,
377 #endif //UDF_ASYNC_IO
378                                   UDFIsBlockAllocated, UDFUpdateVAT,
379                                   UDFWCacheErrorHandler);
380             }
381             if(NT_SUCCESS(RC)) {
382                 if(!Vcb->VerifyCtx.VInited) {
383                     RC = UDFVInit(Vcb);
384                 }
385             }
386             if(NT_SUCCESS(RC)) {
387 
388                 if(!CacheInitialized) {
389                     if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY)) {
390                         if(!Vcb->CDR_Mode) {
391                             if((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_DISK) ||
392                                CdrwMediaClassEx_IsRAM(Vcb->MediaClassEx)) {
393                                 UDFPrint(("UDFMountVolume: RAM mode\n"));
394                                 Mode = WCACHE_MODE_RAM;
395                             } else {
396                                 UDFPrint(("UDFMountVolume: RW mode\n"));
397                                 Mode = WCACHE_MODE_RW;
398                             }
399         /*                    if(FsDeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) {
400                             } else {
401                                 Vcb->WriteSecurity = TRUE;
402                             }*/
403                         } else {
404                             Mode = WCACHE_MODE_R;
405                         }
406                     }
407                     WCacheSetMode__(&(Vcb->FastCache), Mode);
408 
409                     WCacheChFlags__(&(Vcb->FastCache),
410                                     WCACHE_CACHE_WHOLE_PACKET, // enable cache whole packet
411                                     WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS);  // let user retry request on Bad Blocks
412                 }
413                 // we can't record ACL on old format disks
414                 if(!UDFNtAclSupported(Vcb)) {
415                     Vcb->WriteSecurity = FALSE;
416                     Vcb->UseExtendedFE = FALSE;
417                 }
418                 UDFPrint(("UDFVerifyVolume: try start EjectWaiter\n"));
419                 RC = UDFStartEjectWaiter(Vcb);
420                 if(!NT_SUCCESS(RC)) {
421                     UDFPrint(("UDFVerifyVolume: start EjectWaiter failed\n"));
422                     Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;
423                     Vcb->WriteSecurity = FALSE;
424                 }
425             }
426         }
427 
428         if(NewVcb) {
429             // Release internal cache
430             UDFPrint(("UDFVerifyVolume: delete NewVcb\n"));
431             WCacheFlushAll__(&(NewVcb->FastCache),NewVcb);
432             WCacheRelease__(&(NewVcb->FastCache));
433 
434             ASSERT(!(NewVcb->EjectWaiter));
435             // Waiter thread should be already stopped
436             // if MediaChangeCount have changed
437             ASSERT(!(Vcb->EjectWaiter));
438 
439             UDFCleanupVCB(NewVcb);
440             MyFreePool__(NewVcb);
441         }
442         UDFReleaseResource(&(Vcb->VCBResource));
443         UDFReleaseResource(&(UDFGlobalData.GlobalDataResource));
444     } _SEH2_END;
445 
446     // Complete the request if no exception.
447     Irp->IoStatus.Information = 0;
448 
449     Irp->IoStatus.Status = RC;
450     IoCompleteRequest(Irp,IO_DISK_INCREMENT);
451 
452     UDFPrint(("UDFVerifyVolume: RC = %x\n", RC));
453 
454     return RC;
455 } // end UDFVerifyVolume ()
456 
457 /*
458 Routine Description:
459 
460     This routines performs an IoVerifyVolume operation and takes the
461     appropriate action.  If the verify is successful then we send the originating
462     Irp off to an Ex Worker Thread.  This routine is called from the exception handler.
463     No file system resources are held when this routine is called.
464 
465 Arguments:
466 
467     Irp - The irp to send off after all is well and done.
468     Device - The real device needing verification.
469 
470 */
471 NTSTATUS
UDFPerformVerify(IN PtrUDFIrpContext IrpContext,IN PIRP Irp,IN PDEVICE_OBJECT DeviceToVerify)472 UDFPerformVerify(
473     IN PtrUDFIrpContext IrpContext,
474     IN PIRP Irp,
475     IN PDEVICE_OBJECT DeviceToVerify
476     )
477 {
478 
479     PVCB Vcb;
480     NTSTATUS RC = STATUS_SUCCESS;
481     PIO_STACK_LOCATION IrpSp;
482 
483     UDFPrint(("UDFPerformVerify:\n"));
484     if(!IrpContext) return STATUS_INVALID_PARAMETER;
485     if(!Irp) return STATUS_INVALID_PARAMETER;
486 
487     //  Check if this Irp has a status of Verify required and if it does
488     //  then call the I/O system to do a verify.
489     //
490     //  Skip the IoVerifyVolume if this is a mount or verify request
491     //  itself.  Trying a recursive mount will cause a deadlock with
492     //  the DeviceObject->DeviceLock.
493     if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
494        ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) ||
495         (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) {
496 
497         return UDFPostRequest(IrpContext, Irp);
498     }
499 
500     //  Extract a pointer to the Vcb from the VolumeDeviceObject.
501     //  Note that since we have specifically excluded mount,
502     //  requests, we know that IrpSp->DeviceObject is indeed a
503     //  volume device object.
504 
505     IrpSp = IoGetCurrentIrpStackLocation(Irp);
506 
507     Vcb = (PVCB)IrpSp->DeviceObject->DeviceExtension;
508 
509     UDFPrint(("UDFPerformVerify: check\n"));
510     //  Check if the volume still thinks it needs to be verified,
511     //  if it doesn't then we can skip doing a verify because someone
512     //  else beat us to it.
513     _SEH2_TRY {
514 
515         if (DeviceToVerify->Flags & DO_VERIFY_VOLUME) {
516 
517             //  If the IopMount in IoVerifyVolume did something, and
518             //  this is an absolute open, force a reparse.
519             RC = IoVerifyVolume( DeviceToVerify, FALSE );
520 
521             // Bug?
522 /*            if (UDFIsRawDevice(RC)) {
523                 RC = STATUS_WRONG_VOLUME;
524             }*/
525 
526             //  If the verify operation completed it will return
527             //  either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
528             if (RC == STATUS_SUCCESS) {
529                 IrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_EXCEPTION;
530             }
531             //  If UDFVerifyVolume encountered an error during
532             //  processing, it will return that error.  If we got
533             //  STATUS_WRONG_VOLUME from the verify, and our volume
534             //  is now mounted, commute the status to STATUS_SUCCESS.
535             if ((RC == STATUS_WRONG_VOLUME) &&
536                 (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) {
537                 RC = STATUS_SUCCESS;
538             }
539 
540             //  Do a quick unprotected check here.  The routine will do
541             //  a safe check.  After here we can release the resource.
542             //  Note that if the volume really went away, we will be taking
543             //  the Reparse path.
544 
545             //  If the device might need to go away then call our dismount routine.
546             if ( (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) ||
547                    (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) &&
548                    (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) )
549             {
550                 UDFPrint(("UDFPerformVerify: UDFCheckForDismount\n"));
551                 UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE);
552                 UDFCheckForDismount( IrpContext, Vcb, FALSE );
553                 UDFReleaseResource(&(UDFGlobalData.GlobalDataResource));
554             }
555 
556             //  If this is a create and the verify succeeded then complete the
557             //  request with a REPARSE status.
558             if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
559                 (IrpSp->FileObject->RelatedFileObject == NULL) &&
560                 ((RC == STATUS_SUCCESS) || (RC == STATUS_WRONG_VOLUME)) ) {
561 
562                 UDFPrint(("UDFPerformVerify: IO_REMOUNT\n"));
563 
564                 Irp->IoStatus.Information = IO_REMOUNT;
565 
566                 Irp->IoStatus.Status = STATUS_REPARSE;
567                 IoCompleteRequest(Irp,IO_DISK_INCREMENT);
568 
569                 UDFReleaseIrpContext(IrpContext);
570 
571                 RC = STATUS_REPARSE;
572                 Irp = NULL;
573                 IrpContext = NULL;
574 
575             //  If there is still an error to process then call the Io system
576             //  for a popup.
577             } else if ((Irp != NULL) && !NT_SUCCESS( RC )) {
578 
579                 UDFPrint(("UDFPerformVerify: check IoIsErrorUserInduced\n"));
580                 //  Fill in the device object if required.
581                 if (IoIsErrorUserInduced( RC ) ) {
582                     IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify );
583                 }
584                 UDFPrint(("UDFPerformVerify: UDFNormalizeAndRaiseStatus\n"));
585                 UDFNormalizeAndRaiseStatus( IrpContext, RC );
586             }
587         }
588 
589         //  If there is still an Irp, send it off to an Ex Worker thread.
590         if (IrpContext != NULL) {
591 
592             RC = UDFPostRequest( IrpContext, Irp );
593         }
594 
595     } _SEH2_EXCEPT(UDFExceptionFilter( IrpContext, _SEH2_GetExceptionInformation())) {
596         //  We had some trouble trying to perform the verify or raised
597         //  an error ourselves.  So we'll abort the I/O request with
598         //  the error status that we get back from the execption code.
599         RC = UDFExceptionHandler( IrpContext, Irp);
600     } _SEH2_END;
601 
602     UDFPrint(("UDFPerformVerify: RC = %x\n", RC));
603 
604     return RC;
605 
606 } // end UDFPerformVerify()
607 
608 /*
609 
610 Routine Description:
611 
612     This routine is called to check if a volume is ready for dismount.  This
613     occurs when only file system references are left on the volume.
614 
615     If the dismount is not currently underway and the user reference count
616     has gone to zero then we can begin the dismount.
617 
618     If the dismount is in progress and there are no references left on the
619     volume (we check the Vpb for outstanding references as well to catch
620     any create calls dispatched to the file system) then we can delete
621     the Vcb.
622 
623 Arguments:
624 
625     Vcb - Vcb for the volume to try to dismount.
626 
627 */
628 BOOLEAN
UDFCheckForDismount(IN PtrUDFIrpContext IrpContext,IN PVCB Vcb,IN BOOLEAN _VcbAcquired)629 UDFCheckForDismount(
630     IN PtrUDFIrpContext IrpContext,
631     IN PVCB Vcb,
632     IN BOOLEAN _VcbAcquired
633     )
634 {
635     BOOLEAN VcbPresent = TRUE;
636     KIRQL SavedIrql;
637     BOOLEAN VcbAcquired;
638     ULONG ResidualReferenceCount;
639 
640     UDFPrint(("UDFCheckForDismount:\n"));
641     if(!Vcb) return FALSE;
642 
643     //  GlobalDataResource is already acquired
644     if(!_VcbAcquired) {
645         VcbAcquired = UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE/*FALSE*/ );
646         if(!VcbAcquired)
647             return TRUE;
648     } else {
649         VcbAcquired = TRUE;
650     }
651 
652     if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
653         (IrpContext->TargetDeviceObject == Vcb->TargetDeviceObject)) {
654 
655         ResidualReferenceCount = 2;
656 
657     } else {
658 
659         ResidualReferenceCount = 1;
660     }
661 
662     //  If the dismount is not already underway then check if the
663     //  user reference count has gone to zero.  If so start the teardown
664     //  on the Vcb.
665     if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) {
666         if (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) {
667             VcbPresent = UDFDismountVcb(Vcb, VcbAcquired);
668         }
669         VcbAcquired = VcbAcquired && VcbPresent;
670 
671     //  If the teardown is underway and there are absolutely no references
672     //  remaining then delete the Vcb.  References here include the
673     //  references in the Vcb and Vpb.
674     } else if (!(Vcb->VCBOpenCount)) {
675 
676         IoAcquireVpbSpinLock( &SavedIrql );
677         //  If there are no file objects and no reference counts in the
678         //  Vpb we can delete the Vcb.  Don't forget that we have the
679         //  last reference in the Vpb.
680         if (Vcb->Vpb->ReferenceCount <= ResidualReferenceCount) {
681 
682             IoReleaseVpbSpinLock( SavedIrql );
683             if(VcbAcquired)
684                 UDFReleaseResource(&(Vcb->VCBResource));
685             UDFStopEjectWaiter(Vcb);
686             UDFReleaseVCB(Vcb);
687             VcbAcquired =
688             VcbPresent = FALSE;
689 
690         } else {
691 
692             IoReleaseVpbSpinLock( SavedIrql );
693         }
694     }
695 
696     //  Release any resources still acquired.
697     if (!_VcbAcquired && VcbAcquired) {
698          UDFReleaseResource(&(Vcb->VCBResource));
699     }
700 
701     return VcbPresent;
702 } // end UDFCheckForDismount()
703 
704 
705 /*
706 
707 Routine Description:
708 
709     This routine is called when all of the user references to a volume are
710     gone.  We will initiate all of the teardown any system resources.
711 
712     If all of the references to this volume are gone at the end of this routine
713     then we will complete the teardown of this Vcb and mark the current Vpb
714     as not mounted.  Otherwise we will allocated a new Vpb for this device
715     and keep the current Vpb attached to the Vcb.
716 
717 Arguments:
718 
719     Vcb - Vcb for the volume to dismount.
720 
721 Return Value:
722 
723     BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise.
724 
725 */
726 BOOLEAN
UDFDismountVcb(IN PVCB Vcb,IN BOOLEAN VcbAcquired)727 UDFDismountVcb(
728     IN PVCB Vcb,
729     IN BOOLEAN VcbAcquired
730     )
731 {
732 
733     PVPB OldVpb;
734     PVPB NewVpb;
735     BOOLEAN VcbPresent = TRUE;
736     KIRQL SavedIrql;
737 
738     BOOLEAN FinalReference;
739 
740     UDFPrint(("UDFDismountVcb:\n"));
741     //  We should only take this path once.
742     ASSERT( !(Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) );
743 
744     //  Mark the Vcb as DismountInProgress.
745     Vcb->VCBFlags |= UDF_VCB_FLAGS_BEING_DISMOUNTED;
746 
747     //  Allocate a new Vpb in case we will need it.
748     NewVpb = (PVPB)DbgAllocatePoolWithTag( NonPagedPool, sizeof( VPB ), 'bpvU' );
749     if(!NewVpb) {
750         Vcb->VCBFlags &= ~UDF_VCB_FLAGS_BEING_DISMOUNTED;
751         return TRUE;
752     }
753 
754     RtlZeroMemory( NewVpb, sizeof(VPB) );
755 
756     OldVpb = Vcb->Vpb;
757 
758     //  Remove the mount volume reference.
759     UDFCloseResidual(Vcb);
760     // the only residual reference is cleaned above
761 
762     //  Acquire the Vpb spinlock to check for Vpb references.
763     IoAcquireVpbSpinLock(&SavedIrql);
764 
765     //  Remember if this is the last reference on this Vcb.  We incremented
766     //  the count on the Vpb earlier so we get one last crack it.  If our
767     //  reference has gone to zero but the vpb reference count is greater
768     //  than zero then the Io system will be responsible for deleting the
769     //  Vpb.
770     FinalReference = (BOOLEAN)(OldVpb->ReferenceCount == 1);
771 
772     //  There is a reference count in the Vpb and in the Vcb.  We have
773     //  incremented the reference count in the Vpb to make sure that
774     //  we have last crack at it.  If this is a failed mount then we
775     //  want to return the Vpb to the IO system to use for the next
776     //  mount request.
777     if (OldVpb->RealDevice->Vpb == OldVpb) {
778 
779         //  If not the final reference then swap out the Vpb.
780         if (!FinalReference) {
781 
782             NewVpb->Type = IO_TYPE_VPB;
783             NewVpb->Size = sizeof( VPB );
784             NewVpb->RealDevice = OldVpb->RealDevice;
785 
786             NewVpb->RealDevice->Vpb = NewVpb;
787 
788             NewVpb = NULL;
789             IoReleaseVpbSpinLock(SavedIrql);
790         //  We want to leave the Vpb for the IO system.  Mark it
791         //  as being not mounted.  Go ahead and delete the Vcb as
792         //  well.
793         } else {
794 
795             //  Make sure to remove the last reference on the Vpb.
796 
797             OldVpb->ReferenceCount--;
798 
799             OldVpb->DeviceObject = NULL;
800             Vcb->Vpb->Flags &= ~VPB_MOUNTED;
801 
802             //  Clear the Vpb flag so we know not to delete it.
803             Vcb->Vpb = NULL;
804 
805             IoReleaseVpbSpinLock(SavedIrql);
806             if(VcbAcquired)
807                 UDFReleaseResource(&(Vcb->VCBResource));
808             UDFStopEjectWaiter(Vcb);
809             UDFReleaseVCB(Vcb);
810             VcbPresent = FALSE;
811         }
812 
813     //  Someone has already swapped in a new Vpb.  If this is the final reference
814     //  then the file system is responsible for deleting the Vpb.
815     } else if (FinalReference) {
816 
817         //  Make sure to remove the last reference on the Vpb.
818         OldVpb->ReferenceCount--;
819 
820         IoReleaseVpbSpinLock( SavedIrql );
821         if(VcbAcquired)
822             UDFReleaseResource(&(Vcb->VCBResource));
823         UDFStopEjectWaiter(Vcb);
824         UDFReleaseVCB(Vcb);
825         VcbPresent = FALSE;
826 
827     //  The current Vpb is no longer the Vpb for the device (the IO system
828     //  has already allocated a new one).  We leave our reference in the
829     //  Vpb and will be responsible for deleting it at a later time.
830     } else {
831 
832         OldVpb->DeviceObject = NULL;
833         Vcb->Vpb->Flags &= ~VPB_MOUNTED;
834 
835         IoReleaseVpbSpinLock( SavedIrql );
836     }
837 
838     //  Deallocate the new Vpb if we don't need it.
839     if (NewVpb != NULL) {
840         DbgFreePool( NewVpb );
841     }
842 
843     //  Let our caller know whether the Vcb is still present.
844     return VcbPresent;
845 } // end UDFDismountVcb()
846 
847 
848 NTSTATUS
UDFCompareVcb(IN PVCB OldVcb,IN PVCB NewVcb,IN BOOLEAN PhysicalOnly)849 UDFCompareVcb(
850     IN PVCB OldVcb,
851     IN PVCB NewVcb,
852     IN BOOLEAN PhysicalOnly
853     )
854 {
855     NTSTATUS RC;
856     UDF_FILE_INFO    RootFileInfo;
857     BOOLEAN SimpleLogicalCheck = FALSE;
858 
859     UDFPrint(("UDFCompareVcb:\n"));
860     if(UDFGlobalData.UDFFlags & UDF_DATA_FLAGS_BEING_UNLOADED) {
861         UDFPrint(("  WRONG_VOLUME\n"));
862         return STATUS_WRONG_VOLUME;
863     }
864 
865 #define VCB_NE(x)   (OldVcb->x != NewVcb->x)
866 
867     // compare physical parameters
868     if(PhysicalOnly) {
869         UDFPrint(("  PhysicalOnly\n"));
870         if(VCB_NE(FirstLBA) ||
871            VCB_NE(LastLBA) ||
872            VCB_NE(FirstTrackNum) ||
873            VCB_NE(LastTrackNum) ||
874            VCB_NE(NWA) ||
875            VCB_NE(LastPossibleLBA) ||
876            VCB_NE(PhSerialNumber) ||
877            VCB_NE(PhErasable) ||
878            VCB_NE(PhDiskType) ||
879            VCB_NE(MediaClassEx) ||
880 
881           /* We cannot compare these flags, because NewVcb is in unconditional ReadOnly */
882 
883           /*((OldVcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) != (NewVcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)) ||
884           ((OldVcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY)  != (NewVcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY)) ||*/
885 
886            VCB_NE(TargetDeviceObject) ||
887     //       VCB_NE(xxx) ||
888     //       VCB_NE(xxx) ||
889            VCB_NE(LastSession) ) {
890 
891             UDFPrint(("  WRONG_VOLUME (2)\n"));
892             return STATUS_WRONG_VOLUME;
893         }
894         // Note, MRWStatus can change while media is mounted (stoppped/in-progress/complete)
895         // We can compare only (Vcb->MRWStatus == 0) values
896         if((OldVcb->MRWStatus == 0) != (NewVcb->MRWStatus == 0)) {
897             UDFPrint(("  WRONG_VOLUME (4), missmatch MRW status\n"));
898         }
899         for(uint32 i=OldVcb->FirstTrackNum; i<=OldVcb->LastTrackNum; i++) {
900             if(VCB_NE(TrackMap[i].FirstLba) ||
901                VCB_NE(TrackMap[i].LastLba) ||
902                VCB_NE(TrackMap[i].PacketSize) ||
903                VCB_NE(TrackMap[i].TrackParam) ||
904                VCB_NE(TrackMap[i].DataParam) ||
905                VCB_NE(TrackMap[i].NWA_V) ) {
906                 UDFPrint(("  WRONG_VOLUME (3), missmatch trk %d\n", i));
907                 return STATUS_WRONG_VOLUME;
908             }
909         }
910         UDFPrint(("  Vcb compare Ok\n"));
911         return STATUS_SUCCESS;
912     }
913 
914     // Something is nasty!!! We perform verify for not flushed volume
915     // This should never happen, but some devices/buses and their drivers
916     // can lead us to such condition. For example with help of RESET.
917     // Now, we hope, that nobody changed media.
918     // We shall make simplified logical structure check
919     if(OldVcb->Modified) {
920         UDFPrint(("  Vcb SIMPLE compare on !!!MODIFIED!!! volume\n"));
921         ASSERT(FALSE);
922         SimpleLogicalCheck = TRUE;
923     }
924 
925     // compare logical structure
926     if(!SimpleLogicalCheck && (OldVcb->InitVatCount != NewVcb->InitVatCount)) {
927         UDFPrint(("  InitVatCount %d != %d \n", OldVcb->InitVatCount, NewVcb->InitVatCount));
928         return STATUS_WRONG_VOLUME;
929     }
930 
931     // Compare volume creation time
932     if(OldVcb->VolCreationTime != NewVcb->VolCreationTime) {
933         UDFPrint(("  VolCreationTime %I64x != %I64x \n", OldVcb->VolCreationTime, NewVcb->VolCreationTime));
934         return STATUS_WRONG_VOLUME;
935     }
936     // Compare serial numbers
937     if(OldVcb->SerialNumber != NewVcb->SerialNumber) {
938         UDFPrint(("  SerialNumber %x != %x \n", OldVcb->SerialNumber, NewVcb->SerialNumber));
939         return STATUS_WRONG_VOLUME;
940     }
941     // Compare volume idents
942     if(!SimpleLogicalCheck &&
943        RtlCompareUnicodeString(&(OldVcb->VolIdent),&(NewVcb->VolIdent),FALSE)) {
944         UDFPrint(("  VolIdent missmatch \n"));
945         return STATUS_WRONG_VOLUME;
946     }
947     if(SimpleLogicalCheck) {
948         // do not touch RootDir. It can be partially recorded
949         UDFPrint(("  SimpleLogicalCheck Ok\n"));
950         return STATUS_SUCCESS;
951     }
952 
953     RC = UDFOpenRootFile__(NewVcb, &(NewVcb->RootLbAddr), &RootFileInfo);
954     if(!NT_SUCCESS(RC)) {
955         UDFPrint(("  Can't open root file, status %x\n", RC));
956         UDFCleanUpFile__(NewVcb, &RootFileInfo);
957         return STATUS_WRONG_VOLUME;
958     }
959     // perform exhaustive check
960     if(!(OldVcb->RootDirFCB)) {
961         UDFPrint(("  !(OldVcb->RootDirFCB)\n"));
962 wr_vol:
963         UDFCloseFile__(NewVcb, &RootFileInfo);
964         UDFCleanUpFile__(NewVcb, &RootFileInfo);
965         return STATUS_WRONG_VOLUME;
966     }
967 
968     if(!UDFCompareFileInfo(&RootFileInfo, OldVcb->RootDirFCB->FileInfo)) {
969         UDFPrint(("  !UDFCompareFileInfo\n"));
970         goto wr_vol;
971     }
972     UDFCloseFile__(NewVcb, &RootFileInfo);
973     UDFCleanUpFile__(NewVcb, &RootFileInfo);
974 
975     UDFPrint(("UDFCompareVcb: Ok\n"));
976     return STATUS_SUCCESS;
977 
978 #undef VCB_NE
979 
980 } // end UDFCompareVcb()
981 
982