[FASTFAT] Implement delayed close
[reactos.git] / drivers / filesystems / udfs / verfysup.cpp
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
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
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
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
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
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
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