[FASTFAT] Implement delayed close
[reactos.git] / drivers / filesystems / cdfs_new / fsctrl.c
1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 FsCtrl.c
8
9 Abstract:
10
11 This module implements the File System Control routines for Cdfs called
12 by the Fsd/Fsp dispatch drivers.
13
14
15 --*/
16
17 #include "cdprocs.h"
18
19 //
20 // The Bug check file id for this module
21 //
22
23 #define BugCheckFileId (CDFS_BUG_CHECK_FSCTRL)
24
25 //
26 // Local constants
27 //
28
29 BOOLEAN CdDisable = FALSE;
30 BOOLEAN CdNoJoliet = FALSE;
31
32 //
33 // Local support routines
34 //
35
36 _Requires_lock_held_(_Global_critical_region_)
37 NTSTATUS
38 CdUserFsctl (
39 _Inout_ PIRP_CONTEXT IrpContext,
40 _Inout_ PIRP Irp
41 );
42
43 VOID
44 CdReMountOldVcb (
45 _In_ PIRP_CONTEXT IrpContext,
46 _Inout_ PVCB OldVcb,
47 _Inout_ PVCB NewVcb,
48 _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo
49 );
50
51 _Requires_lock_held_(_Global_critical_region_)
52 NTSTATUS
53 CdMountVolume (
54 _Inout_ PIRP_CONTEXT IrpContext,
55 _Inout_ PIRP Irp
56 );
57
58 _Requires_lock_held_(_Global_critical_region_)
59 NTSTATUS
60 CdVerifyVolume (
61 _Inout_ PIRP_CONTEXT IrpContext,
62 _Inout_ PIRP Irp
63 );
64
65 _Requires_lock_held_(_Global_critical_region_)
66 NTSTATUS
67 CdOplockRequest (
68 _Inout_ PIRP_CONTEXT IrpContext,
69 _Inout_ PIRP Irp
70 );
71
72 _Requires_lock_held_(_Global_critical_region_)
73 NTSTATUS
74 CdLockVolume (
75 _Inout_ PIRP_CONTEXT IrpContext,
76 _Inout_ PIRP Irp
77 );
78
79 _Requires_lock_held_(_Global_critical_region_)
80 NTSTATUS
81 CdUnlockVolume (
82 _Inout_ PIRP_CONTEXT IrpContext,
83 _Inout_ PIRP Irp
84 );
85
86 _Requires_lock_held_(_Global_critical_region_)
87 NTSTATUS
88 CdDismountVolume (
89 _Inout_ PIRP_CONTEXT IrpContext,
90 _Inout_ PIRP Irp
91 );
92
93 NTSTATUS
94 CdIsVolumeDirty (
95 _Inout_ PIRP_CONTEXT IrpContext,
96 _Inout_ PIRP Irp
97 );
98
99 NTSTATUS
100 CdIsVolumeMounted (
101 _Inout_ PIRP_CONTEXT IrpContext,
102 _Inout_ PIRP Irp
103 );
104
105 NTSTATUS
106 CdIsPathnameValid (
107 _Inout_ PIRP_CONTEXT IrpContext,
108 _Inout_ PIRP Irp
109 );
110
111 _Requires_lock_held_(_Global_critical_region_)
112 NTSTATUS
113 CdInvalidateVolumes (
114 _Inout_ PIRP_CONTEXT IrpContext,
115 _Inout_ PIRP Irp
116 );
117
118 NTSTATUS
119 CdAllowExtendedDasdIo (
120 _Inout_ PIRP_CONTEXT IrpContext,
121 _Inout_ PIRP Irp
122 );
123
124 _Requires_lock_held_(_Global_critical_region_)
125 VOID
126 CdScanForDismountedVcb (
127 _Inout_ PIRP_CONTEXT IrpContext
128 );
129
130 _Success_(return != FALSE)
131 BOOLEAN
132 CdFindPrimaryVd (
133 _In_ PIRP_CONTEXT IrpContext,
134 _Inout_ PVCB Vcb,
135 _Out_writes_bytes_(SECTOR_SIZE) PCHAR RawIsoVd,
136 _In_ ULONG BlockFactor,
137 _In_ BOOLEAN ReturnOnError,
138 _In_ BOOLEAN VerifyVolume
139 );
140
141 _Success_(return != FALSE) BOOLEAN
142 CdIsRemount (
143 _In_ PIRP_CONTEXT IrpContext,
144 _In_ PVCB Vcb,
145 _Out_ PVCB *OldVcb
146 );
147
148 VOID
149 CdFindActiveVolDescriptor (
150 _In_ PIRP_CONTEXT IrpContext,
151 _In_ PVCB Vcb,
152 _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE )) PCHAR RawIsoVd,
153 _In_ BOOLEAN VerifyVolume
154 );
155
156 #ifdef ALLOC_PRAGMA
157 #pragma alloc_text(PAGE, CdCommonFsControl)
158 #pragma alloc_text(PAGE, CdDismountVolume)
159 #pragma alloc_text(PAGE, CdFindActiveVolDescriptor)
160 #pragma alloc_text(PAGE, CdFindPrimaryVd)
161 #pragma alloc_text(PAGE, CdIsPathnameValid)
162 #pragma alloc_text(PAGE, CdIsRemount)
163 #pragma alloc_text(PAGE, CdIsVolumeDirty)
164 #pragma alloc_text(PAGE, CdIsVolumeMounted)
165 #pragma alloc_text(PAGE, CdLockVolume)
166 #pragma alloc_text(PAGE, CdMountVolume)
167 #pragma alloc_text(PAGE, CdOplockRequest)
168 #pragma alloc_text(PAGE, CdAllowExtendedDasdIo)
169 #pragma alloc_text(PAGE, CdScanForDismountedVcb)
170 #pragma alloc_text(PAGE, CdUnlockVolume)
171 #pragma alloc_text(PAGE, CdUserFsctl)
172 #pragma alloc_text(PAGE, CdVerifyVolume)
173 #endif
174
175 \f
176 //
177 // Local support routine
178 //
179
180 _Requires_lock_held_(_Global_critical_region_)
181 _Requires_lock_held_(Vcb->VcbResource)
182 NTSTATUS
183 CdLockVolumeInternal (
184 _In_ PIRP_CONTEXT IrpContext,
185 _Inout_ PVCB Vcb,
186 _In_opt_ PFILE_OBJECT FileObject
187 )
188
189 /*++
190
191 Routine Description:
192
193 This routine performs the actual lock volume operation. It will be called
194 by anyone wishing to try to protect the volume for a long duration. PNP
195 operations are such a user.
196
197 The volume must be held exclusive by the caller.
198
199 Arguments:
200
201 Vcb - The volume being locked.
202
203 FileObject - File corresponding to the handle locking the volume. If this
204 is not specified, a system lock is assumed.
205
206 Return Value:
207
208 NTSTATUS - The return status for the operation
209
210 --*/
211
212 {
213 NTSTATUS Status;
214 KIRQL SavedIrql;
215 NTSTATUS FinalStatus = (FileObject? STATUS_ACCESS_DENIED: STATUS_DEVICE_BUSY);
216 ULONG RemainingUserReferences = (FileObject? 1: 0);
217
218 //
219 // The cleanup count for the volume only reflects the fileobject that
220 // will lock the volume. Otherwise, we must fail the request.
221 //
222 // Since the only cleanup is for the provided fileobject, we will try
223 // to get rid of all of the other user references. If there is only one
224 // remaining after the purge then we can allow the volume to be locked.
225 //
226
227 CdPurgeVolume( IrpContext, Vcb, FALSE );
228
229 //
230 // Now back out of our synchronization and wait for the lazy writer
231 // to finish off any lazy closes that could have been outstanding.
232 //
233 // Since we purged, we know that the lazy writer will issue all
234 // possible lazy closes in the next tick - if we hadn't, an otherwise
235 // unopened file with a large amount of dirty data could have hung
236 // around for a while as the data trickled out to the disk.
237 //
238 // This is even more important now since we send notification to
239 // alert other folks that this style of check is about to happen so
240 // that they can close their handles. We don't want to enter a fast
241 // race with the lazy writer tearing down his references to the file.
242 //
243
244 CdReleaseVcb( IrpContext, Vcb );
245
246 Status = CcWaitForCurrentLazyWriterActivity();
247
248 //
249 // This is intentional. If we were able to get the Vcb before, just
250 // wait for it and take advantage of knowing that it is OK to leave
251 // the flag up.
252 //
253
254 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
255 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
256
257 if (!NT_SUCCESS( Status )) {
258
259 return Status;
260 }
261
262 CdFspClose( Vcb );
263
264 //
265 // If the volume is already explicitly locked then fail. We use the
266 // Vpb locked flag as an 'explicit lock' flag in the same way as Fat.
267 //
268
269 IoAcquireVpbSpinLock( &SavedIrql );
270
271 if (!FlagOn( Vcb->Vpb->Flags, VPB_LOCKED ) &&
272 (Vcb->VcbCleanup == RemainingUserReferences) &&
273 (Vcb->VcbUserReference == CDFS_RESIDUAL_USER_REFERENCE + RemainingUserReferences)) {
274
275 SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
276 SetFlag( Vcb->Vpb->Flags, VPB_LOCKED);
277 Vcb->VolumeLockFileObject = FileObject;
278 FinalStatus = STATUS_SUCCESS;
279 }
280
281 IoReleaseVpbSpinLock( SavedIrql );
282
283 return FinalStatus;
284 }
285
286 \f
287 NTSTATUS
288 CdUnlockVolumeInternal (
289 _In_ PIRP_CONTEXT IrpContext,
290 _Inout_ PVCB Vcb,
291 _In_opt_ PFILE_OBJECT FileObject
292 )
293
294 /*++
295
296 Routine Description:
297
298 This routine performs the actual unlock volume operation.
299
300 The volume must be held exclusive by the caller.
301
302 Arguments:
303
304 Vcb - The volume being locked.
305
306 FileObject - File corresponding to the handle locking the volume. If this
307 is not specified, a system lock is assumed.
308
309 Return Value:
310
311 NTSTATUS - The return status for the operation
312
313 Attempting to remove a system lock that did not exist is OK.
314
315 --*/
316
317 {
318 NTSTATUS Status = STATUS_NOT_LOCKED;
319 KIRQL SavedIrql;
320
321 UNREFERENCED_PARAMETER( IrpContext );
322
323 //
324 // Note that we check the VPB_LOCKED flag here rather than the Vcb
325 // lock flag. The Vpb flag is only set for an explicit lock request, not
326 // for the implicit lock obtained on a volume open with zero share mode.
327 //
328
329 IoAcquireVpbSpinLock( &SavedIrql );
330
331 if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
332 (FileObject == Vcb->VolumeLockFileObject)) {
333
334 ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED );
335 ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED);
336 Vcb->VolumeLockFileObject = NULL;
337 Status = STATUS_SUCCESS;
338 }
339
340 IoReleaseVpbSpinLock( SavedIrql );
341
342 return Status;
343 }
344
345 \f
346
347 _Requires_lock_held_(_Global_critical_region_)
348 NTSTATUS
349 CdCommonFsControl (
350 _Inout_ PIRP_CONTEXT IrpContext,
351 _Inout_ PIRP Irp
352 )
353
354 /*++
355
356 Routine Description:
357
358 This is the common routine for doing FileSystem control operations called
359 by both the fsd and fsp threads
360
361 Arguments:
362
363 Irp - Supplies the Irp to process
364
365 Return Value:
366
367 NTSTATUS - The return status for the operation
368
369 --*/
370
371 {
372 NTSTATUS Status;
373 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
374
375 PAGED_CODE();
376
377 //
378 // We know this is a file system control so we'll case on the
379 // minor function, and call a internal worker routine to complete
380 // the irp.
381 //
382
383 switch (IrpSp->MinorFunction) {
384
385 case IRP_MN_USER_FS_REQUEST:
386
387 Status = CdUserFsctl( IrpContext, Irp );
388 break;
389
390 case IRP_MN_MOUNT_VOLUME:
391
392 Status = CdMountVolume( IrpContext, Irp );
393 break;
394
395 case IRP_MN_VERIFY_VOLUME:
396
397 Status = CdVerifyVolume( IrpContext, Irp );
398 break;
399
400 default:
401
402 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
403 Status = STATUS_INVALID_DEVICE_REQUEST;
404 break;
405 }
406
407 return Status;
408 }
409
410 \f
411 //
412 // Local support routine
413 //
414
415 _Requires_lock_held_(_Global_critical_region_)
416 NTSTATUS
417 CdUserFsctl (
418 _Inout_ PIRP_CONTEXT IrpContext,
419 _Inout_ PIRP Irp
420 )
421 /*++
422
423 Routine Description:
424
425 This is the common routine for implementing the user's requests made
426 through NtFsControlFile.
427
428 Arguments:
429
430 Irp - Supplies the Irp being processed
431
432 Return Value:
433
434 NTSTATUS - The return status for the operation
435
436 --*/
437
438 {
439 NTSTATUS Status;
440 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
441
442 PAGED_CODE();
443
444 //
445 // Case on the control code.
446 //
447
448 switch ( IrpSp->Parameters.FileSystemControl.FsControlCode ) {
449
450 case FSCTL_REQUEST_OPLOCK_LEVEL_1 :
451 case FSCTL_REQUEST_OPLOCK_LEVEL_2 :
452 case FSCTL_REQUEST_BATCH_OPLOCK :
453 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE :
454 case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
455 case FSCTL_OPLOCK_BREAK_NOTIFY :
456 case FSCTL_OPLOCK_BREAK_ACK_NO_2 :
457 case FSCTL_REQUEST_FILTER_OPLOCK :
458
459 Status = CdOplockRequest( IrpContext, Irp );
460 break;
461
462 case FSCTL_LOCK_VOLUME :
463
464 Status = CdLockVolume( IrpContext, Irp );
465 break;
466
467 case FSCTL_UNLOCK_VOLUME :
468
469 Status = CdUnlockVolume( IrpContext, Irp );
470 break;
471
472 case FSCTL_DISMOUNT_VOLUME :
473
474 Status = CdDismountVolume( IrpContext, Irp );
475 break;
476
477 case FSCTL_IS_VOLUME_DIRTY :
478
479 Status = CdIsVolumeDirty( IrpContext, Irp );
480 break;
481
482 case FSCTL_IS_VOLUME_MOUNTED :
483
484 Status = CdIsVolumeMounted( IrpContext, Irp );
485 break;
486
487 case FSCTL_IS_PATHNAME_VALID :
488
489 Status = CdIsPathnameValid( IrpContext, Irp );
490 break;
491
492 case FSCTL_INVALIDATE_VOLUMES :
493
494 Status = CdInvalidateVolumes( IrpContext, Irp );
495 break;
496
497 case FSCTL_ALLOW_EXTENDED_DASD_IO:
498
499 Status = CdAllowExtendedDasdIo( IrpContext, Irp );
500 break;
501
502 //
503 // We don't support any of the known or unknown requests.
504 //
505
506 default:
507
508 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
509 Status = STATUS_INVALID_DEVICE_REQUEST;
510 break;
511 }
512
513 return Status;
514 }
515
516
517 VOID
518 CdReMountOldVcb (
519 _In_ PIRP_CONTEXT IrpContext,
520 _Inout_ PVCB OldVcb,
521 _Inout_ PVCB NewVcb,
522 _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo
523 )
524 {
525 KIRQL SavedIrql;
526 ULONG Index;
527 PUCHAR Buffer;
528
529 UNREFERENCED_PARAMETER( IrpContext );
530
531 ObDereferenceObject( OldVcb->TargetDeviceObject );
532
533 IoAcquireVpbSpinLock( &SavedIrql );
534
535 #ifdef _MSC_VER
536 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
537 #endif
538 NewVcb->Vpb->RealDevice->Vpb = OldVcb->Vpb;
539
540 OldVcb->Vpb->RealDevice = NewVcb->Vpb->RealDevice;
541 OldVcb->TargetDeviceObject = DeviceObjectWeTalkTo;
542
543 CdUpdateVcbCondition( OldVcb, VcbMounted);
544 CdUpdateMediaChangeCount( OldVcb, NewVcb->MediaChangeCount);
545
546 ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
547
548 Buffer = OldVcb->SectorCacheBuffer = NewVcb->SectorCacheBuffer;
549 NewVcb->SectorCacheBuffer = NULL;
550
551 if (NULL != Buffer) {
552
553 for (Index = 0; Index < CD_SEC_CACHE_CHUNKS; Index++) {
554
555 OldVcb->SecCacheChunks[ Index].Buffer = Buffer;
556 OldVcb->SecCacheChunks[ Index].BaseLbn = (ULONG)-1;
557
558 Buffer += CD_SEC_CHUNK_BLOCKS * SECTOR_SIZE;
559 }
560 }
561
562 IoReleaseVpbSpinLock( SavedIrql );
563 }
564
565
566 //
567 // Local support routine
568 //
569
570 _Requires_lock_held_(_Global_critical_region_)
571 NTSTATUS
572 CdMountVolume (
573 _Inout_ PIRP_CONTEXT IrpContext,
574 _Inout_ PIRP Irp
575 )
576
577 /*++
578
579 Routine Description:
580
581 This routine performs the mount volume operation. It is responsible for
582 either completing of enqueuing the input Irp.
583
584 Its job is to verify that the volume denoted in the IRP is a Cdrom volume,
585 and create the VCB and root DCB structures. The algorithm it
586 uses is essentially as follows:
587
588 1. Create a new Vcb Structure, and initialize it enough to do I/O
589 through the on-disk volume descriptors.
590
591 2. Read the disk and check if it is a Cdrom volume.
592
593 3. If it is not a Cdrom volume then delete the Vcb and
594 complete the IRP back with an appropriate status.
595
596 4. Check if the volume was previously mounted and if it was then do a
597 remount operation. This involves deleting the VCB, hook in the
598 old VCB, and complete the IRP.
599
600 5. Otherwise create a Vcb and root DCB for each valid volume descriptor.
601
602 Arguments:
603
604 Irp - Supplies the Irp to process
605
606 Return Value:
607
608 NTSTATUS - The return status for the operation
609
610 --*/
611
612 {
613 NTSTATUS Status;
614
615 PVOLUME_DEVICE_OBJECT VolDo = NULL;
616 PVCB Vcb = NULL;
617 PVCB OldVcb;
618 UCHAR StackSize;
619
620 BOOLEAN FoundPvd = FALSE;
621 BOOLEAN SetDoVerifyOnFail;
622
623 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
624 PDEVICE_OBJECT DeviceObjectWeTalkTo = IrpSp->Parameters.MountVolume.DeviceObject;
625 PVPB Vpb = IrpSp->Parameters.MountVolume.Vpb;
626
627 PFILE_OBJECT FileObjectToNotify = NULL;
628
629 ULONG BlockFactor;
630 DISK_GEOMETRY DiskGeometry;
631
632 IO_SCSI_CAPABILITIES Capabilities;
633
634 IO_STATUS_BLOCK Iosb;
635
636 PCHAR RawIsoVd = NULL;
637
638 PCDROM_TOC_LARGE CdromToc = NULL;
639 ULONG TocLength = 0;
640 ULONG TocTrackCount = 0;
641 ULONG TocDiskFlags = 0;
642 ULONG MediaChangeCount = 0;
643
644 #ifdef __REACTOS__
645 DEVICE_TYPE FilesystemDeviceType;
646 #endif
647
648 #ifdef CDFS_TELEMETRY_DATA
649 GUID VolumeGuid;
650 GUID VolumeCorrelationId = { 0 };
651 #endif
652
653 PAGED_CODE();
654
655 //
656 // Check that we are talking to a Cdrom device. This request should
657 // always be waitable.
658 //
659
660 #ifdef __REACTOS__
661 if (IrpSp->DeviceObject == CdData.HddFileSystemDeviceObject) {
662 FilesystemDeviceType = FILE_DEVICE_DISK_FILE_SYSTEM;
663 } else {
664 #endif
665 NT_ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM );
666 #ifdef __REACTOS__
667 FilesystemDeviceType = FILE_DEVICE_CD_ROM_FILE_SYSTEM;
668 }
669 #endif
670 NT_ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ));
671
672 #ifdef CDFS_TELEMETRY_DATA
673 //
674 // We don't want a bogus VolumeGuid to show up in our telemetry
675 //
676
677 RtlZeroMemory( &VolumeGuid, sizeof(GUID) );
678
679 #endif
680
681 //
682 // Update the real device in the IrpContext from the Vpb. There was no available
683 // file object when the IrpContext was created.
684 //
685
686 IrpContext->RealDevice = Vpb->RealDevice;
687
688 SetDoVerifyOnFail = CdRealDevNeedsVerify( IrpContext->RealDevice);
689
690 //
691 // Check if we have disabled the mount process.
692 //
693
694 if (CdDisable) {
695
696 CdCompleteRequest( IrpContext, Irp, STATUS_UNRECOGNIZED_VOLUME );
697 return STATUS_UNRECOGNIZED_VOLUME;
698 }
699
700 //
701 // If we've shutdown disallow further mounts.
702 //
703
704 if (FlagOn( CdData.Flags, CD_FLAGS_SHUTDOWN )) {
705
706 CdCompleteRequest( IrpContext, Irp, STATUS_SYSTEM_SHUTDOWN );
707 return STATUS_SYSTEM_SHUTDOWN;
708 }
709
710 //
711 // Do a CheckVerify here to lift the MediaChange ticker from the driver
712 //
713
714 Status = CdPerformDevIoCtrl( IrpContext,
715 #ifndef __REACTOS__
716 IOCTL_CDROM_CHECK_VERIFY,
717 #else
718 (FilesystemDeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ? IOCTL_DISK_CHECK_VERIFY : IOCTL_CDROM_CHECK_VERIFY),
719 #endif
720 DeviceObjectWeTalkTo,
721 &MediaChangeCount,
722 sizeof(ULONG),
723 FALSE,
724 TRUE,
725 &Iosb );
726
727 if (!NT_SUCCESS( Status )) {
728
729 CdCompleteRequest( IrpContext, Irp, Status );
730 return Status;
731 }
732
733 if (Iosb.Information != sizeof(ULONG)) {
734
735 //
736 // Be safe about the count in case the driver didn't fill it in
737 //
738
739 MediaChangeCount = 0;
740 }
741
742 //
743 // Now let's make Jeff delirious and call to get the disk geometry. This
744 // will fix the case where the first change line is swallowed.
745 //
746
747 Status = CdPerformDevIoCtrl( IrpContext,
748 #ifndef __REACTOS__
749 IOCTL_CDROM_GET_DRIVE_GEOMETRY,
750 #else
751 (FilesystemDeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ? IOCTL_DISK_GET_DRIVE_GEOMETRY : IOCTL_CDROM_GET_DRIVE_GEOMETRY),
752 #endif
753 DeviceObjectWeTalkTo,
754 &DiskGeometry,
755 sizeof( DISK_GEOMETRY ),
756 FALSE,
757 TRUE,
758 NULL );
759
760 //
761 // Return insufficient sources to our caller.
762 //
763
764 if (Status == STATUS_INSUFFICIENT_RESOURCES) {
765
766 CdCompleteRequest( IrpContext, Irp, Status );
767 return Status;
768 }
769
770 //
771 // Now check the block factor for addressing the volume descriptors.
772 // If the call for the disk geometry failed then assume there is one
773 // block per sector.
774 //
775
776 BlockFactor = 1;
777
778 if (NT_SUCCESS( Status ) &&
779 (DiskGeometry.BytesPerSector != 0) &&
780 (DiskGeometry.BytesPerSector < SECTOR_SIZE)) {
781
782 BlockFactor = SECTOR_SIZE / DiskGeometry.BytesPerSector;
783 }
784
785 //
786 // Acquire the global resource to do mount operations.
787 //
788
789 CdAcquireCdData( IrpContext );
790
791 //
792 // Use a try-finally to facilitate cleanup.
793 //
794
795 _SEH2_TRY {
796
797 //
798 // Allocate a buffer to query the TOC.
799 //
800
801 CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool,
802 sizeof( CDROM_TOC_LARGE ),
803 TAG_CDROM_TOC );
804
805 RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE ));
806
807 //
808 // Do a quick check to see if there any Vcb's which can be removed.
809 //
810
811 CdScanForDismountedVcb( IrpContext );
812
813 //
814 // Get our device object and alignment requirement.
815 //
816
817 Status = IoCreateDevice( CdData.DriverObject,
818 sizeof( VOLUME_DEVICE_OBJECT ) - sizeof( DEVICE_OBJECT ),
819 NULL,
820 #ifndef __REACTOS__
821 FILE_DEVICE_CD_ROM_FILE_SYSTEM,
822 #else
823 FilesystemDeviceType,
824 #endif
825 0,
826 FALSE,
827 (PDEVICE_OBJECT *) &VolDo );
828
829 if (!NT_SUCCESS( Status )) { try_leave( Status ); }
830
831 //
832 // Our alignment requirement is the larger of the processor alignment requirement
833 // already in the volume device object and that in the DeviceObjectWeTalkTo
834 //
835
836 if (DeviceObjectWeTalkTo->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
837
838 VolDo->DeviceObject.AlignmentRequirement = DeviceObjectWeTalkTo->AlignmentRequirement;
839 }
840
841 //
842 // We must initialize the stack size in our device object before
843 // the following reads, because the I/O system has not done it yet.
844 //
845
846 ((PDEVICE_OBJECT) VolDo)->StackSize = (CCHAR) (DeviceObjectWeTalkTo->StackSize + 1);
847 StackSize = ((PDEVICE_OBJECT) VolDo)->StackSize;
848
849 ClearFlag( VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING );
850
851 //
852 // Initialize the overflow queue for the volume
853 //
854
855 VolDo->OverflowQueueCount = 0;
856 InitializeListHead( &VolDo->OverflowQueue );
857
858 VolDo->PostedRequestCount = 0;
859 KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock );
860
861 //
862 // Let's query for the Toc now and handle any error we get from this operation.
863 //
864
865 Status = CdProcessToc( IrpContext,
866 DeviceObjectWeTalkTo,
867 CdromToc,
868 &TocLength,
869 &TocTrackCount,
870 &TocDiskFlags );
871
872 //
873 // If we failed to read the TOC, then bail out. Probably blank media.
874 //
875
876 if (Status != STATUS_SUCCESS) {
877
878 #ifdef __REACTOS__
879
880 //
881 // Don't bail out if that was a disk based ISO image, it is legit
882 //
883
884 if (FilesystemDeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) {
885 CdFreePool( &CdromToc );
886 Status = STATUS_SUCCESS;
887 } else {
888 #endif
889 try_leave( Status );
890 #ifdef __REACTOS__
891 }
892 #endif
893 }
894
895 //
896 // Now before we can initialize the Vcb we need to set up the
897 // device object field in the VPB to point to our new volume device
898 // object.
899 //
900
901 Vpb->DeviceObject = (PDEVICE_OBJECT) VolDo;
902
903 //
904 // Initialize the Vcb. This routine will raise on an allocation
905 // failure.
906 //
907
908 CdInitializeVcb( IrpContext,
909 &VolDo->Vcb,
910 DeviceObjectWeTalkTo,
911 Vpb,
912 CdromToc,
913 TocLength,
914 TocTrackCount,
915 TocDiskFlags,
916 BlockFactor,
917 MediaChangeCount );
918
919 //
920 // Show that we initialized the Vcb and can cleanup with the Vcb.
921 //
922
923 Vcb = &VolDo->Vcb;
924 VolDo = NULL;
925 Vpb = NULL;
926 CdromToc = NULL;
927
928 #ifdef CDFS_TELEMETRY_DATA
929
930 //
931 // Initialize the volume guid.
932 //
933
934 if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb->TargetDeviceObject, &VolumeGuid ))) {
935
936 //
937 // We got a GUID, set it in the Telemetry structure
938 //
939
940 RtlCopyMemory( &CdTelemetryData.VolumeGuid, &VolumeGuid, sizeof(GUID) );
941 }
942
943 //
944 // Initialize the correlation ID.
945 //
946
947 if (NT_SUCCESS( FsRtlVolumeDeviceToCorrelationId( Vcb->TargetDeviceObject, &VolumeCorrelationId ) )) {
948
949 //
950 // Stash a copy away in the VCB.
951 //
952
953 RtlCopyMemory( &Vcb->VolumeCorrelationId, &VolumeCorrelationId, sizeof( GUID ) );
954 }
955
956 #endif // CDFS_TELEMETRY_DATA
957
958 // Lock object is acquired and released using internal state
959 _Analysis_suppress_lock_checking_(Vcb->VcbResource);
960
961 //
962 // Store the Vcb in the IrpContext as we didn't have one before.
963 //
964
965 IrpContext->Vcb = Vcb;
966
967 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
968
969 //
970 // Let's reference the Vpb to make sure we are the one to
971 // have the last dereference.
972 //
973
974 Vcb->Vpb->ReferenceCount += 1;
975
976 //
977 // Clear the verify bit for the start of mount.
978 //
979
980 CdMarkRealDevVerifyOk( Vcb->Vpb->RealDevice);
981
982 if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) {
983
984 //
985 // Allocate a buffer to read in the volume descriptors. We allocate a full
986 // page to make sure we don't hit any alignment problems.
987 //
988
989 RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool,
990 ROUND_TO_PAGES( SECTOR_SIZE ),
991 TAG_VOL_DESC );
992
993 //
994 // Try to find the primary volume descriptor.
995 //
996
997 FoundPvd = CdFindPrimaryVd( IrpContext,
998 Vcb,
999 RawIsoVd,
1000 BlockFactor,
1001 TRUE,
1002 FALSE );
1003
1004 if (!FoundPvd) {
1005
1006 //
1007 // We failed to find a valid VD in the data track, but there were also
1008 // audio tracks on this disc, so we'll try to mount it as an audio CD.
1009 // Since we're always last in the mount order, we won't be preventing
1010 // any other FS from trying to mount the data track. However if the
1011 // data track was at the start of the disc, then we abort, to avoid
1012 // having to filter it from our synthesised directory listing later. We
1013 // already filtered off any data track at the end.
1014 //
1015
1016 if (!(TocDiskFlags & CDROM_DISK_AUDIO_TRACK) ||
1017 BooleanFlagOn( Vcb->CdromToc->TrackData[0].Control, TOC_DATA_TRACK)) {
1018
1019 try_leave( Status = STATUS_UNRECOGNIZED_VOLUME);
1020 }
1021
1022 SetFlag( Vcb->VcbState, VCB_STATE_AUDIO_DISK | VCB_STATE_CDXA );
1023
1024 CdFreePool( &RawIsoVd );
1025 RawIsoVd = NULL;
1026 }
1027 }
1028
1029 //
1030 // Look and see if there is a secondary volume descriptor we want to
1031 // use.
1032 //
1033
1034 if (FoundPvd) {
1035
1036 //
1037 // Store the primary volume descriptor in the second half of
1038 // RawIsoVd. Then if our search for a secondary fails we can
1039 // recover this immediately.
1040 //
1041
1042 RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ),
1043 RawIsoVd,
1044 SECTOR_SIZE );
1045
1046 //
1047 // We have the initial volume descriptor. Locate a secondary
1048 // volume descriptor if present.
1049 //
1050
1051 CdFindActiveVolDescriptor( IrpContext,
1052 Vcb,
1053 RawIsoVd,
1054 FALSE);
1055 }
1056
1057 //
1058 // Allocate a block cache to speed directory operations. We can't
1059 // use the cache if there is any chance the volume has link blocks
1060 // in the data area (i.e. was packet written and then finalized to
1061 // Joliet/9660). So we simply only allow the cache to operate on
1062 // media with a single track - since we're really targetting pressed
1063 // installation media here. We can't be more precise, since D/CD-ROM
1064 // drives don't support READ_TRACK_INFO, which is the only way for
1065 // certain to know whether or not a track was packet written.
1066 //
1067
1068 if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK) &&
1069 #ifndef __REACTOS__
1070 ((Vcb->CdromToc->LastTrack - Vcb->CdromToc->FirstTrack) == 0)) {
1071 #else
1072 ((FilesystemDeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) ||
1073 ((Vcb->CdromToc->LastTrack - Vcb->CdromToc->FirstTrack) == 0))) {
1074 #endif
1075
1076 ULONG Index;
1077 PUCHAR Buffer;
1078
1079 Buffer =
1080 Vcb->SectorCacheBuffer = FsRtlAllocatePool( CdPagedPool,
1081 CD_SEC_CACHE_CHUNKS *
1082 CD_SEC_CHUNK_BLOCKS *
1083 SECTOR_SIZE);
1084
1085 for (Index = 0; Index < (ULONG)CD_SEC_CACHE_CHUNKS; Index++) {
1086
1087 Vcb->SecCacheChunks[ Index].Buffer = Buffer;
1088 Vcb->SecCacheChunks[ Index].BaseLbn = (ULONG)-1;
1089
1090 Buffer += CD_SEC_CHUNK_BLOCKS * SECTOR_SIZE;
1091 }
1092
1093 Vcb->SectorCacheIrp = IoAllocateIrp( StackSize, FALSE);
1094
1095 if (Vcb->SectorCacheIrp == NULL) {
1096
1097 try_leave( Status = STATUS_INSUFFICIENT_RESOURCES );
1098 }
1099
1100 IoInitializeIrp( Vcb->SectorCacheIrp,
1101 IoSizeOfIrp( StackSize),
1102 (CCHAR)StackSize);
1103
1104 KeInitializeEvent( &Vcb->SectorCacheEvent, SynchronizationEvent, FALSE);
1105 ExInitializeResourceLite( &Vcb->SectorCacheResource);
1106 }
1107
1108 //
1109 // Check if this is a remount operation. If so then clean up
1110 // the data structures passed in and created here.
1111 //
1112
1113 if (CdIsRemount( IrpContext, Vcb, &OldVcb )) {
1114
1115 NT_ASSERT( NULL != OldVcb->SwapVpb );
1116
1117 //
1118 // Link the old Vcb to point to the new device object that we
1119 // should be talking to, dereferencing the previous. Call a
1120 // nonpaged routine to do this since we take the Vpb spinlock.
1121 //
1122
1123 CdReMountOldVcb( IrpContext,
1124 OldVcb,
1125 Vcb,
1126 DeviceObjectWeTalkTo);
1127
1128 //
1129 // See if we will need to provide notification of the remount. This is the readonly
1130 // filesystem's form of dismount/mount notification - we promise that whenever a
1131 // volume is "dismounted", that a mount notification will occur when it is revalidated.
1132 // Note that we do not send mount on normal remounts - that would duplicate the media
1133 // arrival notification of the device driver.
1134 //
1135
1136 if (FlagOn( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) {
1137
1138 ClearFlag( OldVcb->VcbState, VCB_STATE_NOTIFY_REMOUNT );
1139
1140 FileObjectToNotify = OldVcb->RootIndexFcb->FileObject;
1141 ObReferenceObject( FileObjectToNotify );
1142 }
1143
1144 try_leave( Status = STATUS_SUCCESS );
1145 }
1146
1147 //
1148 // This is a new mount. Go ahead and initialize the
1149 // Vcb from the volume descriptor.
1150 //
1151
1152 CdUpdateVcbFromVolDescriptor( IrpContext,
1153 Vcb,
1154 RawIsoVd );
1155
1156 //
1157 // Drop an extra reference on the root dir file so we'll be able to send
1158 // notification.
1159 //
1160
1161 if (Vcb->RootIndexFcb) {
1162
1163 FileObjectToNotify = Vcb->RootIndexFcb->FileObject;
1164 ObReferenceObject( FileObjectToNotify );
1165 }
1166
1167 //
1168 // Now check the maximum transfer limits on the device in case we
1169 // get raw reads on this volume.
1170 //
1171
1172 Status = CdPerformDevIoCtrl( IrpContext,
1173 IOCTL_SCSI_GET_CAPABILITIES,
1174 DeviceObjectWeTalkTo,
1175 &Capabilities,
1176 sizeof( IO_SCSI_CAPABILITIES ),
1177 FALSE,
1178 TRUE,
1179 NULL );
1180
1181 if (NT_SUCCESS(Status)) {
1182
1183 Vcb->MaximumTransferRawSectors = Capabilities.MaximumTransferLength / RAW_SECTOR_SIZE;
1184 Vcb->MaximumPhysicalPages = Capabilities.MaximumPhysicalPages;
1185
1186 } else {
1187
1188 //
1189 // This should never happen, but we can safely assume 64k and 16 pages.
1190 //
1191
1192 Vcb->MaximumTransferRawSectors = (64 * 1024) / RAW_SECTOR_SIZE;
1193 Vcb->MaximumPhysicalPages = 16;
1194 }
1195
1196 //
1197 // The new mount is complete. Remove the additional references on this
1198 // Vcb and the device we are mounted on top of.
1199 //
1200
1201 Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE;
1202 NT_ASSERT( Vcb->VcbReference == CDFS_RESIDUAL_REFERENCE );
1203
1204 ObDereferenceObject( Vcb->TargetDeviceObject );
1205
1206 CdUpdateVcbCondition( Vcb, VcbMounted);
1207
1208 CdReleaseVcb( IrpContext, Vcb );
1209 Vcb = NULL;
1210
1211 Status = STATUS_SUCCESS;
1212
1213 } _SEH2_FINALLY {
1214
1215 //
1216 // Free the TOC buffer if not in the Vcb.
1217 //
1218
1219 if (CdromToc != NULL) {
1220
1221 CdFreePool( &CdromToc );
1222 }
1223
1224 //
1225 // Free the sector buffer if allocated.
1226 //
1227
1228 if (RawIsoVd != NULL) {
1229
1230 CdFreePool( &RawIsoVd );
1231 }
1232
1233 //
1234 // If we are not mounting the device, then set the verify bit again.
1235 //
1236
1237 if ((_SEH2_AbnormalTermination() || (Status != STATUS_SUCCESS)) &&
1238 SetDoVerifyOnFail) {
1239
1240 CdMarkRealDevForVerify( IrpContext->RealDevice);
1241 }
1242
1243 //
1244 // If we didn't complete the mount then cleanup any remaining structures.
1245 //
1246
1247 if (Vpb != NULL) { Vpb->DeviceObject = NULL; }
1248
1249 if (Vcb != NULL) {
1250
1251 //
1252 // Make sure there is no Vcb in the IrpContext since it could go away
1253 //
1254
1255 IrpContext->Vcb = NULL;
1256
1257 Vcb->VcbReference -= CDFS_RESIDUAL_REFERENCE;
1258
1259 if (CdDismountVcb( IrpContext, Vcb )) {
1260
1261 CdReleaseVcb( IrpContext, Vcb );
1262 }
1263
1264 } else if (VolDo != NULL) {
1265
1266 IoDeleteDevice( (PDEVICE_OBJECT) VolDo );
1267 }
1268
1269 //
1270 // Release the global resource.
1271 //
1272
1273 CdReleaseCdData( IrpContext );
1274 } _SEH2_END;
1275
1276 //
1277 // Now send mount notification.
1278 //
1279
1280 if (FileObjectToNotify) {
1281
1282 FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT );
1283 ObDereferenceObject( FileObjectToNotify );
1284 }
1285
1286 #ifdef CDFS_TELEMETRY_DATA
1287
1288 //
1289 // Send Telemetry
1290 //
1291
1292 CdTelemetryMountSafe( &VolumeCorrelationId, STATUS_SUCCESS, Vcb );
1293
1294 #endif
1295
1296 //
1297 // Complete the request if no exception.
1298 //
1299
1300 CdCompleteRequest( IrpContext, Irp, Status );
1301 return Status;
1302 }
1303
1304 \f
1305 //
1306 // Local support routine
1307 //
1308
1309 _Requires_lock_held_(_Global_critical_region_)
1310 NTSTATUS
1311 CdVerifyVolume (
1312 _Inout_ PIRP_CONTEXT IrpContext,
1313 _Inout_ PIRP Irp
1314 )
1315
1316 /*++
1317
1318 Routine Description:
1319
1320 This routine performs the verify volume operation. It is responsible for
1321 either completing of enqueuing the input Irp.
1322
1323 Arguments:
1324
1325 Irp - Supplies the Irp to process
1326
1327 Return Value:
1328
1329 NTSTATUS - The return status for the operation
1330
1331 --*/
1332
1333 {
1334 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
1335 PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb;
1336 PVCB Vcb = &((PVOLUME_DEVICE_OBJECT) IrpSp->Parameters.VerifyVolume.DeviceObject)->Vcb;
1337
1338 PCHAR RawIsoVd = NULL;
1339
1340 PCDROM_TOC_LARGE CdromToc = NULL;
1341 ULONG TocLength = 0;
1342 ULONG TocTrackCount = 0;
1343 ULONG TocDiskFlags = 0;
1344
1345 ULONG MediaChangeCount = Vcb->MediaChangeCount;
1346
1347 PFILE_OBJECT FileObjectToNotify = NULL;
1348
1349 BOOLEAN ReturnError;
1350 BOOLEAN ReleaseVcb = FALSE;
1351
1352 IO_STATUS_BLOCK Iosb;
1353
1354 STRING AnsiLabel;
1355 UNICODE_STRING UnicodeLabel;
1356
1357 WCHAR VolumeLabel[ VOLUME_ID_LENGTH ];
1358 ULONG VolumeLabelLength;
1359
1360 ULONG Index;
1361
1362 NTSTATUS Status = STATUS_SUCCESS;
1363
1364 PAGED_CODE();
1365
1366 //
1367 // We check that we are talking to a Cdrom device.
1368 //
1369
1370 NT_ASSERT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM );
1371 NT_ASSERT( FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ));
1372
1373 //
1374 // Update the real device in the IrpContext from the Vpb. There was no available
1375 // file object when the IrpContext was created.
1376 //
1377
1378 IrpContext->RealDevice = Vpb->RealDevice;
1379
1380 //
1381 // Acquire the global resource to synchronise against mounts and teardown,
1382 // finally clause releases.
1383 //
1384
1385 CdAcquireCdData( IrpContext );
1386
1387 _SEH2_TRY {
1388
1389 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
1390 ReleaseVcb = TRUE;
1391
1392 //
1393 // Check to see if the volume is eligible for verification.
1394 //
1395
1396 if ((Vcb->VcbCondition == VcbInvalid) ||
1397 (Vcb->VcbCondition == VcbDismountInProgress)) {
1398
1399 try_return( Status = STATUS_WRONG_VOLUME );
1400 }
1401
1402 //
1403 // Verify that there is a disk here.
1404 //
1405
1406 Status = CdPerformDevIoCtrl( IrpContext,
1407 IOCTL_CDROM_CHECK_VERIFY,
1408 Vcb->TargetDeviceObject,
1409 &MediaChangeCount,
1410 sizeof(ULONG),
1411 FALSE,
1412 TRUE,
1413 &Iosb );
1414
1415 if (!NT_SUCCESS( Status )) {
1416
1417 //
1418 // If we will allow a raw mount then return WRONG_VOLUME to
1419 // allow the volume to be mounted by raw.
1420 //
1421
1422 if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) {
1423
1424 Status = STATUS_WRONG_VOLUME;
1425 }
1426
1427 try_return( Status );
1428 }
1429
1430 if (Iosb.Information != sizeof(ULONG)) {
1431
1432 //
1433 // Be safe about the count in case the driver didn't fill it in
1434 //
1435
1436 MediaChangeCount = 0;
1437 }
1438
1439 //
1440 // Verify that the device actually saw a change. If the driver does not
1441 // support the MCC, then we must verify the volume in any case.
1442 //
1443
1444 if (MediaChangeCount == 0 ||
1445 (Vcb->MediaChangeCount != MediaChangeCount)) {
1446
1447 //
1448 // Allocate a buffer to query the TOC.
1449 //
1450
1451 CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool,
1452 sizeof( CDROM_TOC_LARGE ),
1453 TAG_CDROM_TOC );
1454
1455 RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE ));
1456
1457 //
1458 // Let's query for the Toc now and handle any error we get from this operation.
1459 //
1460
1461 Status = CdProcessToc( IrpContext,
1462 Vcb->TargetDeviceObject,
1463 CdromToc,
1464 &TocLength,
1465 &TocTrackCount,
1466 &TocDiskFlags );
1467
1468 //
1469 // If we failed to read the TOC, then give up now. Drives will fail
1470 // a TOC read on, for example, erased CD-RW media.
1471 //
1472
1473 if (Status != STATUS_SUCCESS) {
1474
1475 //
1476 // For any errors other than no media and not ready, commute the
1477 // status to ensure that the current VPB is kicked off the device
1478 // below - there is probably blank media in the drive, since we got
1479 // further than the check verify.
1480 //
1481
1482 if (!CdIsRawDevice( IrpContext, Status )) {
1483
1484 Status = STATUS_WRONG_VOLUME;
1485 }
1486
1487 try_return( Status );
1488
1489 //
1490 // We got a TOC. Verify that it matches the previous Toc.
1491 //
1492
1493 } else if ((Vcb->TocLength != TocLength) ||
1494 (Vcb->TrackCount != TocTrackCount) ||
1495 (Vcb->DiskFlags != TocDiskFlags) ||
1496 !RtlEqualMemory( CdromToc,
1497 Vcb->CdromToc,
1498 TocLength )) {
1499
1500 try_return( Status = STATUS_WRONG_VOLUME );
1501 }
1502
1503 //
1504 // If the disk to verify is an audio disk then we already have a
1505 // match. Otherwise we need to check the volume descriptor.
1506 //
1507
1508 if (!FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) {
1509
1510 //
1511 // Allocate a buffer for the sector buffer.
1512 //
1513
1514 RawIsoVd = FsRtlAllocatePoolWithTag( CdNonPagedPool,
1515 ROUND_TO_PAGES( 2 * SECTOR_SIZE ),
1516 TAG_VOL_DESC );
1517
1518 //
1519 // Read the primary volume descriptor for this volume. If we
1520 // get an io error and this verify was a the result of DASD open,
1521 // commute the Io error to STATUS_WRONG_VOLUME. Note that if we currently
1522 // expect a music disk then this request should fail.
1523 //
1524
1525 ReturnError = FALSE;
1526
1527 if (FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) {
1528
1529 ReturnError = TRUE;
1530 }
1531
1532 if (!CdFindPrimaryVd( IrpContext,
1533 Vcb,
1534 RawIsoVd,
1535 Vcb->BlockFactor,
1536 ReturnError,
1537 TRUE )) {
1538
1539 //
1540 // If the previous Vcb did not represent a raw disk
1541 // then show this volume was dismounted.
1542 //
1543
1544 try_return( Status = STATUS_WRONG_VOLUME );
1545
1546 }
1547 else {
1548
1549 //
1550 // Look for a supplementary VD.
1551 //
1552 // Store the primary volume descriptor in the second half of
1553 // RawIsoVd. Then if our search for a secondary fails we can
1554 // recover this immediately.
1555 //
1556
1557 RtlCopyMemory( Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ),
1558 RawIsoVd,
1559 SECTOR_SIZE );
1560
1561 //
1562 // We have the initial volume descriptor. Locate a secondary
1563 // volume descriptor if present.
1564 //
1565
1566 CdFindActiveVolDescriptor( IrpContext,
1567 Vcb,
1568 RawIsoVd,
1569 TRUE);
1570 //
1571 // Compare the serial numbers. If they don't match, set the
1572 // status to wrong volume.
1573 //
1574
1575 if (Vpb->SerialNumber != CdSerial32( RawIsoVd, SECTOR_SIZE )) {
1576
1577 try_return( Status = STATUS_WRONG_VOLUME );
1578 }
1579
1580 //
1581 // Verify the volume labels.
1582 //
1583
1584 if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) {
1585
1586 //
1587 // Compute the length of the volume name
1588 //
1589
1590 AnsiLabel.Buffer = (PCHAR)CdRvdVolId( RawIsoVd, Vcb->VcbState );
1591 AnsiLabel.MaximumLength = AnsiLabel.Length = (ULONG)VOLUME_ID_LENGTH;
1592
1593 UnicodeLabel.MaximumLength = VOLUME_ID_LENGTH * sizeof( WCHAR );
1594 UnicodeLabel.Buffer = VolumeLabel;
1595
1596 //
1597 // Convert this to unicode. If we get any error then use a name
1598 // length of zero.
1599 //
1600
1601 VolumeLabelLength = 0;
1602
1603 if (NT_SUCCESS( RtlOemStringToCountedUnicodeString( &UnicodeLabel,
1604 &AnsiLabel,
1605 FALSE ))) {
1606
1607 VolumeLabelLength = UnicodeLabel.Length;
1608 }
1609
1610 //
1611 // We need to convert from big-endian to little endian.
1612 //
1613
1614 } else {
1615
1616 CdConvertBigToLittleEndian( IrpContext,
1617 (PCHAR) CdRvdVolId( RawIsoVd, Vcb->VcbState ),
1618 VOLUME_ID_LENGTH,
1619 (PCHAR) VolumeLabel );
1620
1621 VolumeLabelLength = VOLUME_ID_LENGTH;
1622 }
1623
1624 //
1625 // Strip the trailing spaces or zeroes from the name.
1626 //
1627
1628 Index = VolumeLabelLength / sizeof( WCHAR );
1629
1630 while (Index > 0) {
1631
1632 if ((VolumeLabel[ Index - 1 ] != L'\0') &&
1633 (VolumeLabel[ Index - 1 ] != L' ')) {
1634
1635 break;
1636 }
1637
1638 Index -= 1;
1639 }
1640
1641 //
1642 // Now set the final length for the name.
1643 //
1644
1645 VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR ));
1646
1647 //
1648 // Now check that the label matches.
1649 //
1650 if ((Vpb->VolumeLabelLength != VolumeLabelLength) ||
1651 !RtlEqualMemory( Vpb->VolumeLabel,
1652 VolumeLabel,
1653 VolumeLabelLength )) {
1654
1655 try_return( Status = STATUS_WRONG_VOLUME );
1656 }
1657 }
1658 }
1659 }
1660
1661 //
1662 // The volume is OK, clear the verify bit.
1663 //
1664
1665 CdUpdateVcbCondition( Vcb, VcbMounted);
1666
1667 CdMarkRealDevVerifyOk( Vpb->RealDevice);
1668
1669 //
1670 // See if we will need to provide notification of the remount. This is the readonly
1671 // filesystem's form of dismount/mount notification.
1672 //
1673
1674 if (FlagOn( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT )) {
1675
1676 ClearFlag( Vcb->VcbState, VCB_STATE_NOTIFY_REMOUNT );
1677
1678 FileObjectToNotify = Vcb->RootIndexFcb->FileObject;
1679 ObReferenceObject( FileObjectToNotify );
1680 }
1681
1682 try_exit: NOTHING;
1683
1684 //
1685 // Update the media change count to note that we have verified the volume
1686 // at this value - regardless of the outcome.
1687 //
1688
1689 CdUpdateMediaChangeCount( Vcb, MediaChangeCount);
1690
1691 //
1692 // If the volume was already unmounted, nothing more to do.
1693 //
1694
1695 if (Vcb->VcbCondition == VcbNotMounted) {
1696
1697 Status = STATUS_WRONG_VOLUME;
1698
1699 //
1700 // If we got the wrong volume then free any remaining XA sector in
1701 // the current Vcb. Also mark the Vcb as not mounted.
1702 //
1703
1704 } else if ((Vcb->VcbCondition == VcbMounted) && (Status == STATUS_WRONG_VOLUME)) {
1705
1706 CdUpdateVcbCondition( Vcb, VcbNotMounted);
1707
1708 if (Vcb->XASector != NULL) {
1709
1710 CdFreePool( &Vcb->XASector );
1711 Vcb->XASector = 0;
1712 Vcb->XADiskOffset = 0;
1713 }
1714
1715 CdFreeDirCache( IrpContext);
1716
1717 //
1718 // Now, if there are no user handles to the volume, try to spark
1719 // teardown by purging the volume.
1720 //
1721
1722 if (Vcb->VcbCleanup == 0) {
1723
1724 if (NT_SUCCESS( CdPurgeVolume( IrpContext, Vcb, FALSE ))) {
1725
1726 ReleaseVcb = CdCheckForDismount( IrpContext, Vcb, FALSE );
1727 }
1728 }
1729 }
1730
1731 } _SEH2_FINALLY {
1732
1733 //
1734 // Free the TOC buffer if allocated.
1735 //
1736
1737 if (CdromToc != NULL) {
1738
1739 CdFreePool( &CdromToc );
1740 }
1741
1742 if (RawIsoVd != NULL) {
1743
1744 CdFreePool( &RawIsoVd );
1745 }
1746
1747 if (ReleaseVcb) {
1748
1749 CdReleaseVcb( IrpContext, Vcb );
1750 }
1751 else {
1752 _Analysis_assume_lock_not_held_(Vcb->VcbResource);
1753 }
1754
1755 CdReleaseCdData( IrpContext );
1756 } _SEH2_END;
1757
1758 //
1759 // Now send mount notification.
1760 //
1761
1762 if (FileObjectToNotify) {
1763
1764 FsRtlNotifyVolumeEvent( FileObjectToNotify, FSRTL_VOLUME_MOUNT );
1765 ObDereferenceObject( FileObjectToNotify );
1766 }
1767
1768 //
1769 // Complete the request if no exception.
1770 //
1771
1772 CdCompleteRequest( IrpContext, Irp, Status );
1773 return Status;
1774 }
1775
1776 \f
1777 //
1778 // Local support routine
1779 //
1780
1781 _Requires_lock_held_(_Global_critical_region_)
1782 NTSTATUS
1783 CdOplockRequest (
1784 _Inout_ PIRP_CONTEXT IrpContext,
1785 _Inout_ PIRP Irp
1786 )
1787
1788 /*++
1789
1790 Routine Description:
1791
1792 This is the common routine to handle oplock requests made via the
1793 NtFsControlFile call.
1794
1795 Arguments:
1796
1797 Irp - Supplies the Irp being processed
1798
1799 Return Value:
1800
1801 NTSTATUS - The return status for the operation
1802
1803 --*/
1804
1805 {
1806 NTSTATUS Status = STATUS_SUCCESS;
1807 PFCB Fcb;
1808 PCCB Ccb;
1809
1810 ULONG OplockCount = 0;
1811 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
1812
1813 PAGED_CODE();
1814
1815 //
1816 // We only permit oplock requests on files.
1817 //
1818
1819 if (CdDecodeFileObject( IrpContext,
1820 IrpSp->FileObject,
1821 &Fcb,
1822 &Ccb ) != UserFileOpen ) {
1823
1824 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
1825 return STATUS_INVALID_PARAMETER;
1826 }
1827
1828 //
1829 // Make this a waitable Irpcontext so we don't fail to acquire
1830 // the resources.
1831 //
1832
1833 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
1834 ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
1835
1836 //
1837 // Switch on the function control code. We grab the Fcb exclusively
1838 // for oplock requests, shared for oplock break acknowledgement.
1839 //
1840
1841 switch (IrpSp->Parameters.FileSystemControl.FsControlCode) {
1842
1843 case FSCTL_REQUEST_OPLOCK_LEVEL_1 :
1844 case FSCTL_REQUEST_OPLOCK_LEVEL_2 :
1845 case FSCTL_REQUEST_BATCH_OPLOCK :
1846 case FSCTL_REQUEST_FILTER_OPLOCK :
1847
1848 CdAcquireFcbExclusive( IrpContext, Fcb, FALSE );
1849
1850 if (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
1851
1852 if (Fcb->FileLock != NULL) {
1853
1854 #if (NTDDI_VERSION >= NTDDI_WIN7)
1855 OplockCount = (ULONG) FsRtlAreThereCurrentOrInProgressFileLocks( Fcb->FileLock );
1856 #else
1857 OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( Fcb->FileLock );
1858 #endif
1859 }
1860
1861 } else {
1862
1863 OplockCount = Fcb->FcbCleanup;
1864 }
1865
1866 break;
1867
1868 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
1869 case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
1870 case FSCTL_OPLOCK_BREAK_NOTIFY:
1871 case FSCTL_OPLOCK_BREAK_ACK_NO_2:
1872
1873 CdAcquireFcbShared( IrpContext, Fcb, FALSE );
1874 break;
1875
1876 default:
1877
1878 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
1879 return STATUS_INVALID_PARAMETER;
1880 }
1881
1882 //
1883 // Use a try finally to free the Fcb.
1884 //
1885
1886 _SEH2_TRY {
1887
1888 //
1889 // Verify the Fcb.
1890 //
1891
1892 CdVerifyFcbOperation( IrpContext, Fcb );
1893
1894 //
1895 // Call the FsRtl routine to grant/acknowledge oplock.
1896 //
1897
1898 Status = FsRtlOplockFsctrl( CdGetFcbOplock(Fcb),
1899 Irp,
1900 OplockCount );
1901
1902 //
1903 // Set the flag indicating if Fast I/O is possible
1904 //
1905
1906 CdLockFcb( IrpContext, Fcb );
1907 Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb );
1908 CdUnlockFcb( IrpContext, Fcb );
1909
1910 //
1911 // The oplock package will complete the Irp.
1912 //
1913
1914 Irp = NULL;
1915
1916 } _SEH2_FINALLY {
1917
1918 //
1919 // Release all of our resources
1920 //
1921
1922 CdReleaseFcb( IrpContext, Fcb );
1923 } _SEH2_END;
1924
1925 //
1926 // Complete the request if there was no exception.
1927 //
1928
1929 CdCompleteRequest( IrpContext, Irp, Status );
1930 return Status;
1931 }
1932
1933 \f
1934 //
1935 // Local support routine
1936 //
1937
1938 _Requires_lock_held_(_Global_critical_region_)
1939 NTSTATUS
1940 CdLockVolume (
1941 _Inout_ PIRP_CONTEXT IrpContext,
1942 _Inout_ PIRP Irp
1943 )
1944
1945 /*++
1946
1947 Routine Description:
1948
1949 This routine performs the lock volume operation. It is responsible for
1950 either completing of enqueuing the input Irp.
1951
1952 Arguments:
1953
1954 Irp - Supplies the Irp to process
1955
1956 Return Value:
1957
1958 NTSTATUS - The return status for the operation
1959
1960 --*/
1961
1962 {
1963 NTSTATUS Status = STATUS_SUCCESS;
1964
1965 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
1966
1967 PVCB Vcb;
1968 PFCB Fcb;
1969 PCCB Ccb;
1970
1971 PAGED_CODE();
1972
1973 //
1974 // Decode the file object, the only type of opens we accept are
1975 // user volume opens.
1976 //
1977
1978 if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen) {
1979
1980 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
1981
1982 return STATUS_INVALID_PARAMETER;
1983 }
1984
1985 //
1986 // Send our notification so that folks that like to hold handles on
1987 // volumes can get out of the way.
1988 //
1989
1990 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK );
1991
1992 //
1993 // Acquire exclusive access to the Vcb.
1994 //
1995
1996 Vcb = Fcb->Vcb;
1997 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
1998
1999 _SEH2_TRY {
2000
2001 //
2002 // Verify the Vcb.
2003 //
2004
2005 CdVerifyVcb( IrpContext, Vcb );
2006
2007 Status = CdLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
2008
2009 } _SEH2_FINALLY {
2010
2011 //
2012 // Release the Vcb.
2013 //
2014
2015 CdReleaseVcb( IrpContext, Vcb );
2016
2017 if (_SEH2_AbnormalTermination() || !NT_SUCCESS( Status )) {
2018
2019 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED );
2020 }
2021 } _SEH2_END;
2022
2023 //
2024 // Complete the request if there haven't been any exceptions.
2025 //
2026
2027 CdCompleteRequest( IrpContext, Irp, Status );
2028 return Status;
2029 }
2030
2031 \f
2032 //
2033 // Local support routine
2034 //
2035
2036 _Requires_lock_held_(_Global_critical_region_)
2037 NTSTATUS
2038 CdUnlockVolume (
2039 _Inout_ PIRP_CONTEXT IrpContext,
2040 _Inout_ PIRP Irp
2041 )
2042
2043 /*++
2044
2045 Routine Description:
2046
2047 This routine performs the unlock volume operation. It is responsible for
2048 either completing of enqueuing the input Irp.
2049
2050 Arguments:
2051
2052 Irp - Supplies the Irp to process
2053
2054 Return Value:
2055
2056 NTSTATUS - The return status for the operation
2057
2058 --*/
2059
2060 {
2061 NTSTATUS Status;
2062
2063 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2064
2065 PVCB Vcb;
2066 PFCB Fcb;
2067 PCCB Ccb;
2068
2069 PAGED_CODE();
2070
2071 //
2072 // Decode the file object, the only type of opens we accept are
2073 // user volume opens.
2074 //
2075
2076 if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) {
2077
2078 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2079 return STATUS_INVALID_PARAMETER;
2080 }
2081
2082 //
2083 // Acquire exclusive access to the Vcb.
2084 //
2085
2086 Vcb = Fcb->Vcb;
2087
2088 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
2089
2090 //
2091 // We won't check for a valid Vcb for this request. An unlock will always
2092 // succeed on a locked volume.
2093 //
2094
2095 Status = CdUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
2096
2097 //
2098 // Release all of our resources
2099 //
2100
2101 CdReleaseVcb( IrpContext, Vcb );
2102
2103 //
2104 // Send notification that the volume is avaliable.
2105 //
2106
2107 if (NT_SUCCESS( Status )) {
2108
2109 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK );
2110 }
2111
2112 //
2113 // Complete the request if there haven't been any exceptions.
2114 //
2115
2116 CdCompleteRequest( IrpContext, Irp, Status );
2117 return Status;
2118 }
2119
2120
2121 \f
2122 //
2123 // Local support routine
2124 //
2125
2126 _Requires_lock_held_(_Global_critical_region_)
2127 NTSTATUS
2128 CdDismountVolume (
2129 _Inout_ PIRP_CONTEXT IrpContext,
2130 _Inout_ PIRP Irp
2131 )
2132
2133 /*++
2134
2135 Routine Description:
2136
2137 This routine performs the dismount volume operation. It is responsible for
2138 either completing of enqueuing the input Irp. We only dismount a volume which
2139 has been locked. The intent here is that someone has locked the volume (they are the
2140 only remaining handle). We set the verify bit here and the user will close his handle.
2141 We will dismount a volume with no user's handles in the verify path.
2142
2143 Arguments:
2144
2145 Irp - Supplies the Irp to process
2146
2147 Return Value:
2148
2149 NTSTATUS - The return status for the operation
2150
2151 --*/
2152
2153 {
2154 NTSTATUS Status;
2155 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2156
2157 PVCB Vcb;
2158 PFCB Fcb;
2159 PCCB Ccb;
2160
2161 PAGED_CODE();
2162
2163 if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) {
2164
2165 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2166 return STATUS_INVALID_PARAMETER;
2167 }
2168
2169 Vcb = Fcb->Vcb;
2170
2171 //
2172 // Send dismount notification.
2173 //
2174
2175 FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT );
2176
2177 //
2178 // Make this request waitable.
2179 //
2180
2181 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
2182
2183 //
2184 // Acquire exclusive access to the Vcb, and take the global resource to
2185 // sync. against mounts, verifies etc.
2186 //
2187
2188 CdAcquireCdData( IrpContext );
2189 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
2190
2191 //
2192 // Mark the volume as needs to be verified, but only do it if
2193 // the vcb is locked by this handle and the volume is currently mounted.
2194 //
2195
2196 if (Vcb->VcbCondition != VcbMounted) {
2197
2198 Status = STATUS_VOLUME_DISMOUNTED;
2199
2200 } else {
2201
2202 //
2203 // Invalidate the volume right now.
2204 //
2205 // The intent here is to make every subsequent operation
2206 // on the volume fail and grease the rails toward dismount.
2207 // By definition there is no going back from a SURPRISE.
2208 //
2209
2210 CdLockVcb( IrpContext, Vcb );
2211
2212 if (Vcb->VcbCondition != VcbDismountInProgress) {
2213
2214 CdUpdateVcbCondition( Vcb, VcbInvalid );
2215 }
2216
2217 SetFlag( Vcb->VcbState, VCB_STATE_DISMOUNTED );
2218
2219 CdUnlockVcb( IrpContext, Vcb );
2220
2221
2222 //
2223 // Set flag to tell the close path that we want to force dismount
2224 // the volume when this handle is closed.
2225 //
2226
2227 SetFlag( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE);
2228
2229 Status = STATUS_SUCCESS;
2230 }
2231
2232 //
2233 // Release all of our resources
2234 //
2235
2236 CdReleaseVcb( IrpContext, Vcb );
2237 CdReleaseCdData( IrpContext );
2238
2239 #if (NTDDI_VERSION >= NTDDI_WIN8)
2240
2241 FsRtlDismountComplete( Vcb->TargetDeviceObject, Status );
2242
2243 #endif
2244
2245 //
2246 // Complete the request if there haven't been any exceptions.
2247 //
2248
2249 CdCompleteRequest( IrpContext, Irp, Status );
2250 return Status;
2251 }
2252
2253 \f
2254 //
2255 // Local support routine
2256 //
2257
2258 NTSTATUS
2259 CdIsVolumeDirty (
2260 _Inout_ PIRP_CONTEXT IrpContext,
2261 _Inout_ PIRP Irp
2262 )
2263
2264 /*++
2265
2266 Routine Description:
2267
2268 This routine determines if a volume is currently dirty.
2269
2270 Arguments:
2271
2272 Irp - Supplies the Irp to process
2273
2274 Return Value:
2275
2276 NTSTATUS - The return status for the operation
2277
2278 --*/
2279
2280 {
2281 PIO_STACK_LOCATION IrpSp;
2282
2283 TYPE_OF_OPEN TypeOfOpen;
2284 PFCB Fcb;
2285 PCCB Ccb;
2286
2287 PULONG VolumeState;
2288
2289 PAGED_CODE();
2290
2291 //
2292 // Get the current stack location and extract the output
2293 // buffer information.
2294 //
2295
2296 IrpSp = IoGetCurrentIrpStackLocation( Irp );
2297
2298 //
2299 // Get a pointer to the output buffer.
2300 //
2301
2302 if (Irp->AssociatedIrp.SystemBuffer != NULL) {
2303
2304 VolumeState = Irp->AssociatedIrp.SystemBuffer;
2305
2306 } else {
2307
2308 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
2309 return STATUS_INVALID_USER_BUFFER;
2310 }
2311
2312 //
2313 // Make sure the output buffer is large enough and then initialize
2314 // the answer to be that the volume isn't dirty.
2315 //
2316
2317 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
2318
2319 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2320 return STATUS_INVALID_PARAMETER;
2321 }
2322
2323 *VolumeState = 0;
2324
2325 //
2326 // Decode the file object
2327 //
2328
2329 TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb );
2330
2331 if (TypeOfOpen != UserVolumeOpen) {
2332
2333 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2334 return STATUS_INVALID_PARAMETER;
2335 }
2336
2337 if (Fcb->Vcb->VcbCondition != VcbMounted) {
2338
2339 CdCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED );
2340 return STATUS_VOLUME_DISMOUNTED;
2341 }
2342
2343 //
2344 // Now set up to return the clean state. CDs obviously can never be dirty
2345 // but we want to make sure we have enforced the full semantics of this call.
2346 //
2347
2348 Irp->IoStatus.Information = sizeof( ULONG );
2349
2350 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
2351 return STATUS_SUCCESS;
2352 }
2353
2354 \f
2355 //
2356 // Local support routine
2357 //
2358
2359 NTSTATUS
2360 CdIsVolumeMounted (
2361 _Inout_ PIRP_CONTEXT IrpContext,
2362 _Inout_ PIRP Irp
2363 )
2364
2365 /*++
2366
2367 Routine Description:
2368
2369 This routine determines if a volume is currently mounted.
2370
2371 Arguments:
2372
2373 Irp - Supplies the Irp to process
2374
2375 Return Value:
2376
2377 NTSTATUS - The return status for the operation
2378
2379 --*/
2380
2381 {
2382 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2383
2384 PFCB Fcb;
2385 PCCB Ccb;
2386
2387 PAGED_CODE();
2388
2389 //
2390 // Decode the file object.
2391 //
2392
2393 CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb );
2394
2395 if (Fcb != NULL) {
2396
2397 //
2398 // Disable PopUps, we want to return any error.
2399 //
2400
2401 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS );
2402
2403 //
2404 // Verify the Vcb. This will raise in the error condition.
2405 //
2406
2407 CdVerifyVcb( IrpContext, Fcb->Vcb );
2408 }
2409
2410 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
2411
2412 return STATUS_SUCCESS;
2413 }
2414
2415 \f
2416 //
2417 // Local support routine
2418 //
2419
2420 NTSTATUS
2421 CdIsPathnameValid (
2422 _Inout_ PIRP_CONTEXT IrpContext,
2423 _Inout_ PIRP Irp
2424 )
2425
2426 /*++
2427
2428 Routine Description:
2429
2430 This routine determines if pathname is a valid CDFS pathname.
2431 We always succeed this request.
2432
2433 Arguments:
2434
2435 Irp - Supplies the Irp to process.
2436
2437 Return Value:
2438
2439 None
2440
2441 --*/
2442
2443 {
2444 PAGED_CODE();
2445
2446 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
2447 return STATUS_SUCCESS;
2448 }
2449
2450 \f
2451 //
2452 // Local support routine
2453 //
2454
2455 _Requires_lock_held_(_Global_critical_region_)
2456 NTSTATUS
2457 CdInvalidateVolumes (
2458 _Inout_ PIRP_CONTEXT IrpContext,
2459 _Inout_ PIRP Irp
2460 )
2461
2462 /*++
2463
2464 Routine Description:
2465
2466 This routine searches for all the volumes mounted on the same real device
2467 of the current DASD handle, and marks them all bad. The only operation
2468 that can be done on such handles is cleanup and close.
2469
2470 Arguments:
2471
2472 Irp - Supplies the Irp to process
2473
2474 Return Value:
2475
2476 NTSTATUS - The return status for the operation
2477
2478 --*/
2479
2480 {
2481 NTSTATUS Status;
2482 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2483 KIRQL SavedIrql;
2484
2485 BOOLEAN UnlockVcb = FALSE;
2486
2487 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
2488
2489 HANDLE Handle;
2490
2491 PVCB Vcb;
2492
2493 PLIST_ENTRY Links;
2494
2495 PFILE_OBJECT FileToMarkBad;
2496 PDEVICE_OBJECT DeviceToMarkBad;
2497
2498 //
2499 // We only allow the invalidate call to come in on our file system devices.
2500 //
2501
2502 #ifndef __REACTOS__
2503 if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject) {
2504 #else
2505 if (IrpSp->DeviceObject != CdData.FileSystemDeviceObject &&
2506 IrpSp->DeviceObject != CdData.HddFileSystemDeviceObject) {
2507 #endif
2508
2509 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
2510
2511 return STATUS_INVALID_DEVICE_REQUEST;
2512 }
2513
2514 //
2515 // Check for the correct security access.
2516 // The caller must have the SeTcbPrivilege.
2517 //
2518
2519 if (!SeSinglePrivilegeCheck( TcbPrivilege, Irp->RequestorMode )) {
2520
2521 CdCompleteRequest( IrpContext, Irp, STATUS_PRIVILEGE_NOT_HELD );
2522
2523 return STATUS_PRIVILEGE_NOT_HELD;
2524 }
2525
2526 //
2527 // Try to get a pointer to the device object from the handle passed in.
2528 //
2529
2530 #if defined(_WIN64) && BUILD_WOW64_ENABLED
2531
2532 if (IoIs32bitProcess( Irp )) {
2533
2534 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( UINT32 )) {
2535
2536 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2537 return STATUS_INVALID_PARAMETER;
2538 }
2539
2540 Handle = (HANDLE) LongToHandle( *((PUINT32) Irp->AssociatedIrp.SystemBuffer) );
2541
2542 } else
2543
2544 #endif
2545
2546 {
2547 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof( HANDLE )) {
2548
2549 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
2550 return STATUS_INVALID_PARAMETER;
2551 }
2552
2553 Handle = *((PHANDLE) Irp->AssociatedIrp.SystemBuffer);
2554 }
2555
2556 Status = ObReferenceObjectByHandle( Handle,
2557 0,
2558 *IoFileObjectType,
2559 KernelMode,
2560 (PVOID*)&FileToMarkBad, /* ReactOS Change: GCC "passing argument 5 of 'ObReferenceObjectByHandle' from incompatible pointer type" */
2561 NULL );
2562
2563 if (!NT_SUCCESS(Status)) {
2564
2565 CdCompleteRequest( IrpContext, Irp, Status );
2566 return Status;
2567 }
2568
2569 //
2570 // Grab the DeviceObject from the FileObject.
2571 //
2572
2573 DeviceToMarkBad = FileToMarkBad->DeviceObject;
2574
2575 //
2576 // We only needed the device object involved, not a reference to the file.
2577 //
2578
2579 ObDereferenceObject( FileToMarkBad );
2580
2581 //
2582 // Make sure this request can wait.
2583 //
2584
2585 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
2586 ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
2587
2588 //
2589 // Synchronise with pnp/mount/verify paths.
2590 //
2591
2592 CdAcquireCdData( IrpContext );
2593
2594 //
2595 // Nothing can go wrong now.
2596 //
2597
2598 //
2599 // Now walk through all the mounted Vcb's looking for candidates to
2600 // mark invalid.
2601 //
2602 // On volumes we mark invalid, check for dismount possibility (which is
2603 // why we have to get the next link so early).
2604 //
2605
2606 Links = CdData.VcbQueue.Flink;
2607
2608 while (Links != &CdData.VcbQueue) {
2609
2610 Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks);
2611
2612 Links = Links->Flink;
2613
2614 //
2615 // If we get a match, mark the volume Bad, and also check to
2616 // see if the volume should go away.
2617 //
2618
2619 CdLockVcb( IrpContext, Vcb );
2620
2621 if (Vcb->Vpb->RealDevice == DeviceToMarkBad) {
2622
2623 //
2624 // Take the VPB spinlock, and look to see if this volume is the
2625 // one currently mounted on the actual device. If it is, pull it
2626 // off immediately.
2627 //
2628
2629 IoAcquireVpbSpinLock( &SavedIrql );
2630
2631 #ifdef _MSC_VER
2632 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
2633 #endif
2634 if (DeviceToMarkBad->Vpb == Vcb->Vpb) {
2635
2636 PVPB NewVpb = Vcb->SwapVpb;
2637
2638 NT_ASSERT( FlagOn( Vcb->Vpb->Flags, VPB_MOUNTED));
2639 NT_ASSERT( NULL != NewVpb);
2640
2641 RtlZeroMemory( NewVpb, sizeof( VPB ) );
2642
2643 NewVpb->Type = IO_TYPE_VPB;
2644 NewVpb->Size = sizeof( VPB );
2645 NewVpb->RealDevice = DeviceToMarkBad;
2646
2647 #ifdef _MSC_VER
2648 #pragma prefast(push)
2649 #pragma prefast(disable: 28175, "this is a filesystem driver, touching the vpb is allowed")
2650 #endif
2651 NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING );
2652 DeviceToMarkBad->Vpb = NewVpb;
2653 #ifdef _MSC_VER
2654 #pragma prefast(pop)
2655 #endif
2656
2657 Vcb->SwapVpb = NULL;
2658 }
2659
2660 IoReleaseVpbSpinLock( SavedIrql );
2661
2662 if (Vcb->VcbCondition != VcbDismountInProgress) {
2663
2664 CdUpdateVcbCondition( Vcb, VcbInvalid);
2665 }
2666
2667 CdUnlockVcb( IrpContext, Vcb );
2668
2669 CdAcquireVcbExclusive( IrpContext, Vcb, FALSE);
2670
2671 CdPurgeVolume( IrpContext, Vcb, FALSE );
2672
2673 UnlockVcb = CdCheckForDismount( IrpContext, Vcb, FALSE );
2674
2675 //
2676 // prefast: if UnlockVcb is false, then the VCB was already deleted, so we better not touch the Vcb.
2677 // tell Prefast something nice so it stops complaining about us leaking it.
2678 //
2679
2680 __analysis_assert( UnlockVcb == TRUE );
2681
2682 if (UnlockVcb) {
2683
2684 CdReleaseVcb( IrpContext, Vcb);
2685 }
2686
2687 } else {
2688
2689 CdUnlockVcb( IrpContext, Vcb );
2690 }
2691 }
2692
2693 CdReleaseCdData( IrpContext );
2694
2695 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
2696 return STATUS_SUCCESS;
2697 }
2698
2699
2700 //
2701 // Local support routine
2702 //
2703
2704 NTSTATUS
2705 CdAllowExtendedDasdIo (
2706 _Inout_ PIRP_CONTEXT IrpContext,
2707 _Inout_ PIRP Irp
2708 )
2709
2710 /*++
2711
2712 Routine Description:
2713
2714 This routine marks the CCB to indicate that the handle
2715 may be used to read past the end of the volume file. The
2716 handle must be a dasd handle.
2717
2718 Arguments:
2719
2720 Irp - Supplies the Irp to process
2721
2722 Return Value:
2723
2724 NTSTATUS - The return status for the operation
2725
2726 --*/
2727
2728 {
2729 NTSTATUS Status;
2730 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2731
2732 PFCB Fcb;
2733 PCCB Ccb;
2734
2735 PAGED_CODE();
2736
2737 //
2738 // Decode the file object, the only type of opens we accept are
2739 // user volume opens.
2740 //
2741
2742 if (CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb ) != UserVolumeOpen ) {
2743
2744 Status = STATUS_INVALID_PARAMETER;
2745 }
2746 else {
2747
2748 SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO );
2749 Status = STATUS_SUCCESS;
2750 }
2751
2752 CdCompleteRequest( IrpContext, Irp, Status );
2753 return Status;
2754 }
2755
2756
2757 //
2758 // Local support routine
2759 //
2760
2761 _Requires_lock_held_(_Global_critical_region_)
2762 VOID
2763 CdScanForDismountedVcb (
2764 _Inout_ PIRP_CONTEXT IrpContext
2765 )
2766
2767 /*++
2768
2769 Routine Description:
2770
2771 This routine walks through the list of Vcb's looking for any which may
2772 now be deleted. They may have been left on the list because there were
2773 outstanding references.
2774
2775 Arguments:
2776
2777 Return Value:
2778
2779 None
2780
2781 --*/
2782
2783 {
2784 PVCB Vcb;
2785 PLIST_ENTRY Links;
2786
2787 PAGED_CODE();
2788
2789 //
2790 // Walk through all of the Vcb's attached to the global data.
2791 //
2792
2793 Links = CdData.VcbQueue.Flink;
2794
2795 while (Links != &CdData.VcbQueue) {
2796
2797 Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
2798
2799 //
2800 // Move to the next link now since the current Vcb may be deleted.
2801 //
2802
2803 Links = Links->Flink;
2804
2805 //
2806 // If dismount is already underway then check if this Vcb can
2807 // go away.
2808 //
2809
2810 if ((Vcb->VcbCondition == VcbDismountInProgress) ||
2811 (Vcb->VcbCondition == VcbInvalid) ||
2812 ((Vcb->VcbCondition == VcbNotMounted) && (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE))) {
2813
2814 CdCheckForDismount( IrpContext, Vcb, FALSE );
2815 }
2816 }
2817
2818 return;
2819 }
2820
2821 \f
2822 //
2823 // Local support routine
2824 //
2825 _Success_(return != FALSE)
2826 BOOLEAN
2827 CdFindPrimaryVd (
2828 _In_ PIRP_CONTEXT IrpContext,
2829 _Inout_ PVCB Vcb,
2830 _Out_writes_bytes_(SECTOR_SIZE) PCHAR RawIsoVd,
2831 _In_ ULONG BlockFactor,
2832 _In_ BOOLEAN ReturnOnError,
2833 _In_ BOOLEAN VerifyVolume
2834 )
2835
2836 /*++
2837
2838 Routine Description:
2839
2840 This routine is called to walk through the volume descriptors looking
2841 for a primary volume descriptor. When/if a primary is found a 32-bit
2842 serial number is generated and stored into the Vpb. We also store the
2843 location of the primary volume descriptor in the Vcb.
2844
2845 Arguments:
2846
2847 Vcb - Pointer to the VCB for the volume.
2848
2849 RawIsoVd - Pointer to a sector buffer which will contain the primary
2850 volume descriptor on exit, if successful.
2851
2852 BlockFactor - Block factor used by the current device for the TableOfContents.
2853
2854 ReturnOnError - Indicates that we should raise on I/O errors rather than
2855 returning a FALSE value.
2856
2857 VerifyVolume - Indicates if we were called from the verify path. We
2858 do a few things different in this path. We don't update the Vcb in
2859 the verify path.
2860
2861 Return Value:
2862
2863 BOOLEAN - TRUE if a valid primary volume descriptor found, FALSE
2864 otherwise.
2865
2866 --*/
2867
2868 {
2869 NTSTATUS Status;
2870 ULONG ThisPass = 1;
2871 BOOLEAN FoundVd = FALSE;
2872
2873 ULONG BaseSector;
2874 ULONG SectorOffset;
2875
2876 PCDROM_TOC_LARGE CdromToc;
2877
2878 ULONG VolumeFlags;
2879
2880 PAGED_CODE();
2881
2882 //
2883 // If there are no data tracks, don't even bother hunting for descriptors.
2884 //
2885 // This explicitly breaks various non-BlueBook compliant CDs that scribble
2886 // an ISO filesystem on media claiming only audio tracks. Since these
2887 // disks can cause serious problems in some CDROM units, fail fast. I admit
2888 // that it is possible that someone can still record the descriptors in the
2889 // audio track, record a data track (but fail to record descriptors there)
2890 // and still have the disk work. As this form of error worked in NT 4.0, and
2891 // since these disks really do exist, I don't want to change them.
2892 //
2893 // If we wished to support all such media (we don't), it would be neccesary
2894 // to clear this flag on finding ISO or HSG descriptors below.
2895 //
2896
2897 if (FlagOn(Vcb->VcbState, VCB_STATE_AUDIO_DISK)) {
2898
2899 return FALSE;
2900 }
2901
2902 //
2903 // We will make at most two passes through the volume descriptor sequence.
2904 //
2905 // On the first pass we will query for the last session. Using this
2906 // as a starting offset we will attempt to mount the volume. On any failure
2907 // we will go to the second pass and try without using any multi-session
2908 // information.
2909 //
2910 // On the second pass we will start offset from sector zero.
2911 //
2912
2913 while (!FoundVd && (ThisPass <= 2)) {
2914
2915 //
2916 // If we aren't at pass 1 then we start at sector 0. Otherwise we
2917 // try to look up the multi-session information.
2918 //
2919
2920 BaseSector = 0;
2921
2922 if (ThisPass == 1) {
2923
2924 CdromToc = NULL;
2925
2926 //
2927 // Check for whether this device supports XA and multi-session.
2928 //
2929
2930 _SEH2_TRY {
2931
2932 //
2933 // Allocate a buffer for the last session information.
2934 //
2935
2936 CdromToc = FsRtlAllocatePoolWithTag( CdPagedPool,
2937 sizeof( CDROM_TOC_LARGE ),
2938 TAG_CDROM_TOC );
2939
2940 RtlZeroMemory( CdromToc, sizeof( CDROM_TOC_LARGE ));
2941
2942 //
2943 // Query the last session information from the driver.
2944 //
2945
2946 Status = CdPerformDevIoCtrl( IrpContext,
2947 IOCTL_CDROM_GET_LAST_SESSION,
2948 Vcb->TargetDeviceObject,
2949 CdromToc,
2950 sizeof( CDROM_TOC_LARGE ),
2951 FALSE,
2952 TRUE,
2953 NULL );
2954
2955 //
2956 // Raise an exception if there was an allocation failure.
2957 //
2958
2959 if (Status == STATUS_INSUFFICIENT_RESOURCES) {
2960
2961 CdRaiseStatus( IrpContext, Status );
2962 }
2963
2964 //
2965 // We don't handle any errors yet. We will hit that below
2966 // as we try to scan the disk. If we have last session information
2967 // then modify the base sector.
2968 //
2969
2970 if (NT_SUCCESS( Status ) &&
2971 (CdromToc->FirstTrack != CdromToc->LastTrack)) {
2972
2973 PCHAR Source, Dest;
2974 ULONG Count;
2975
2976 Count = 4;
2977
2978 //
2979 // The track address is BigEndian, we need to flip the bytes.
2980 //
2981
2982 Source = (PCHAR) &CdromToc->TrackData[0].Address[3];
2983 Dest = (PCHAR) &BaseSector;
2984
2985 do {
2986
2987 *Dest++ = *Source--;
2988
2989 } while (--Count);
2990
2991 //
2992 // Now adjust the base sector by the block factor of the
2993 // device.
2994 //
2995
2996 BaseSector /= BlockFactor;
2997
2998 //
2999 // Make this look like the second pass since we are only using the
3000 // first session. No reason to retry on error.
3001 //
3002
3003 } else {
3004
3005 ThisPass += 1;
3006 }
3007
3008 } _SEH2_FINALLY {
3009
3010 if (CdromToc != NULL) { CdFreePool( &CdromToc ); }
3011 } _SEH2_END;
3012 }
3013
3014 //
3015 // Compute the starting sector offset from the start of the session.
3016 //
3017
3018 SectorOffset = FIRST_VD_SECTOR;
3019
3020 //
3021 // Start by assuming we have neither Hsg or Iso volumes.
3022 //
3023
3024 VolumeFlags = 0;
3025
3026 //
3027 // Loop until either error encountered, primary volume descriptor is
3028 // found or a terminal volume descriptor is found.
3029 //
3030
3031 while (TRUE) {
3032
3033 //
3034 // Attempt to read the desired sector. Exit directly if operation
3035 // not completed.
3036 //
3037 // If this is pass 1 we will ignore errors in read sectors and just
3038 // go to the next pass.
3039 //
3040
3041 if (!CdReadSectors( IrpContext,
3042 LlBytesFromSectors( BaseSector + SectorOffset ),
3043 SECTOR_SIZE,
3044 (BOOLEAN) ((ThisPass == 1) || ReturnOnError),
3045 RawIsoVd,
3046 Vcb->TargetDeviceObject )) {
3047
3048 break;
3049 }
3050
3051 //
3052 // Check if either an ISO or HSG volume.
3053 //
3054
3055 if (RtlEqualMemory( CdIsoId,
3056 CdRvdId( RawIsoVd, VCB_STATE_ISO ),
3057 VOL_ID_LEN )) {
3058
3059 SetFlag( VolumeFlags, VCB_STATE_ISO );
3060
3061 } else if (RtlEqualMemory( CdHsgId,
3062 CdRvdId( RawIsoVd, VCB_STATE_HSG ),
3063 VOL_ID_LEN )) {
3064
3065 SetFlag( VolumeFlags, VCB_STATE_HSG );
3066
3067 //
3068 // We have neither so break out of the loop.
3069 //
3070
3071 } else {
3072
3073 break;
3074 }
3075
3076 //
3077 // Break out if the version number is incorrect or this is
3078 // a terminator.
3079 //
3080
3081 if ((CdRvdVersion( RawIsoVd, VolumeFlags ) != VERSION_1) ||
3082 (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_TERMINATOR)) {
3083
3084 break;
3085 }
3086
3087 //
3088 // If this is a primary volume descriptor then our search is over.
3089 //
3090
3091 if (CdRvdDescType( RawIsoVd, VolumeFlags ) == VD_PRIMARY) {
3092
3093 //
3094 // If we are not in the verify path then initialize the
3095 // fields in the Vcb with basic information from this
3096 // descriptor.
3097 //
3098
3099 if (!VerifyVolume) {
3100
3101 //
3102 // Set the flag for the volume type.
3103 //
3104
3105 SetFlag( Vcb->VcbState, VolumeFlags );
3106
3107 //
3108 // Store the base sector and sector offset for the
3109 // primary volume descriptor.
3110 //
3111
3112 Vcb->BaseSector = BaseSector;
3113 Vcb->VdSectorOffset = SectorOffset;
3114 Vcb->PrimaryVdSectorOffset = SectorOffset;
3115 }
3116
3117 FoundVd = TRUE;
3118 break;
3119 }
3120
3121 //
3122 // Indicate that we're at the next sector.
3123 //
3124
3125 SectorOffset += 1;
3126 }
3127
3128 ThisPass += 1;
3129 }
3130
3131 return FoundVd;
3132 }
3133
3134 \f
3135 //
3136 // Local support routine
3137 //
3138
3139 _Success_(return != FALSE) BOOLEAN
3140 CdIsRemount (
3141 _In_ PIRP_CONTEXT IrpContext,
3142 _In_ PVCB Vcb,
3143 _Out_ PVCB *OldVcb
3144 )
3145 /*++
3146
3147 Routine Description:
3148
3149 This routine walks through the links of the Vcb chain in the global
3150 data structure. The remount condition is met when the following
3151 conditions are all met:
3152
3153 If the new Vcb is a device only Mvcb and there is a previous
3154 device only Mvcb.
3155
3156 Otherwise following conditions must be matched.
3157
3158 1 - The 32 serial in the current VPB matches that in a previous
3159 VPB.
3160
3161 2 - The volume label in the Vpb matches that in the previous
3162 Vpb.
3163
3164 3 - The system pointer to the real device object in the current
3165 VPB matches that in the same previous VPB.
3166
3167 4 - Finally the previous Vcb cannot be invalid or have a dismount
3168 underway.
3169
3170 If a VPB is found which matches these conditions, then the address of
3171 the VCB for that VPB is returned via the pointer Vcb.
3172
3173 Skip over the current Vcb.
3174
3175 Arguments:
3176
3177 Vcb - This is the Vcb we are checking for a remount.
3178
3179 OldVcb - A pointer to the address to store the address for the Vcb
3180 for the volume if this is a remount. (This is a pointer to
3181 a pointer)
3182
3183 Return Value:
3184
3185 BOOLEAN - TRUE if this is in fact a remount, FALSE otherwise.
3186
3187 --*/
3188
3189 {
3190 PLIST_ENTRY Link;
3191
3192 PVPB Vpb = Vcb->Vpb;
3193 PVPB OldVpb;
3194
3195 BOOLEAN Remount = FALSE;
3196
3197 PAGED_CODE();
3198
3199 UNREFERENCED_PARAMETER( IrpContext );
3200
3201 //
3202 // Check whether we are looking for a device only Mvcb.
3203 //
3204
3205 for (Link = CdData.VcbQueue.Flink;
3206 Link != &CdData.VcbQueue;
3207 Link = Link->Flink) {
3208
3209 *OldVcb = CONTAINING_RECORD( Link, VCB, VcbLinks );
3210
3211 //
3212 // Skip ourselves.
3213 //
3214
3215 if (Vcb == *OldVcb) { continue; }
3216
3217 //
3218 // Look at the Vpb and state of the previous Vcb.
3219 //
3220
3221 OldVpb = (*OldVcb)->Vpb;
3222
3223 if ((OldVpb != Vpb) &&
3224 (OldVpb->RealDevice == Vpb->RealDevice) &&
3225 ((*OldVcb)->VcbCondition == VcbNotMounted)) {
3226
3227 //
3228 // If the current disk is a raw disk then it can match a previous music or
3229 // raw disk.
3230 //
3231
3232 if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK)) {
3233
3234 if (FlagOn( (*OldVcb)->VcbState, VCB_STATE_AUDIO_DISK )) {
3235
3236 //
3237 // If we have both TOC then fail the remount if the lengths
3238 // are different or they don't match.
3239 //
3240
3241 if ((Vcb->TocLength != (*OldVcb)->TocLength) ||
3242 ((Vcb->TocLength != 0) &&
3243 !RtlEqualMemory( Vcb->CdromToc,
3244 (*OldVcb)->CdromToc,
3245 Vcb->TocLength ))) {
3246
3247 continue;
3248 }
3249
3250 Remount = TRUE;
3251 break;
3252 }
3253
3254 //
3255 // The current disk is not a raw disk. Go ahead and compare
3256 // serial numbers, volume label and TOC.
3257 //
3258
3259 }
3260 else if ((OldVpb->SerialNumber == Vpb->SerialNumber) &&
3261 (Vcb->TocLength == (*OldVcb)->TocLength) &&
3262 ((Vcb->TocLength == 0) || RtlEqualMemory( Vcb->CdromToc,
3263 (*OldVcb)->CdromToc,
3264 Vcb->TocLength )) &&
3265 (Vpb->VolumeLabelLength == OldVpb->VolumeLabelLength) &&
3266 (RtlEqualMemory( OldVpb->VolumeLabel,
3267 Vpb->VolumeLabel,
3268 Vpb->VolumeLabelLength ))) {
3269 //
3270 // Remember the old Vcb. Then set the return value to
3271 // TRUE and break.
3272 //
3273
3274 Remount = TRUE;
3275 break;
3276 }
3277 }
3278 }
3279
3280 return Remount;
3281 }
3282
3283 \f
3284 //
3285 // Local support routine
3286 //
3287
3288 VOID
3289 CdFindActiveVolDescriptor (
3290 _In_ PIRP_CONTEXT IrpContext,
3291 _In_ PVCB Vcb,
3292 _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE )) PCHAR RawIsoVd,
3293 _In_ BOOLEAN VerifyVolume
3294 )
3295
3296 /*++
3297
3298 Routine Description:
3299
3300 This routine is called to search for a valid secondary volume descriptor that
3301 we will support. Right now we only support Joliet escape sequences for
3302 the secondary descriptor.
3303
3304 If we don't find the secondary descriptor then we will reread the primary.
3305
3306 This routine will update the serial number and volume label in the Vpb.
3307
3308 Arguments:
3309
3310 Vcb - This is the Vcb for the volume being mounted.
3311
3312 RawIsoVd - Sector buffer used to read the volume descriptors from the disks, but
3313 on input should contain the PVD (ISO) in the SECOND 'sector' of the
3314 buffer.
3315
3316 VerifyVolume - indicates we are being called by the verify path, and should
3317 not modify the Vcb fields.
3318
3319 Return Value:
3320
3321 None
3322
3323 --*/
3324
3325 {
3326 BOOLEAN FoundSecondaryVd = FALSE;
3327 ULONG SectorOffset = FIRST_VD_SECTOR;
3328
3329 ULONG Length;
3330
3331 ULONG Index;
3332
3333 PAGED_CODE();
3334
3335 //
3336 // We only look for secondary volume descriptors on an Iso disk.
3337 //
3338
3339 if ((FlagOn( Vcb->VcbState, VCB_STATE_ISO) || VerifyVolume) && !CdNoJoliet) {
3340
3341 //
3342 // Scan the volume descriptors from the beginning looking for a valid
3343 // secondary or a terminator.
3344 //
3345
3346 SectorOffset = FIRST_VD_SECTOR;
3347
3348 while (TRUE) {
3349
3350 //
3351 // Read the next sector. We should never have an error in this
3352 // path.
3353 //
3354
3355 CdReadSectors( IrpContext,
3356 LlBytesFromSectors( Vcb->BaseSector + SectorOffset ),
3357 SECTOR_SIZE,
3358 FALSE,
3359 RawIsoVd,
3360 Vcb->TargetDeviceObject );
3361
3362 //
3363 // Break out if the version number or standard Id is incorrect.
3364 // Also break out if this is a terminator.
3365 //
3366
3367 if (!RtlEqualMemory( CdIsoId, CdRvdId( RawIsoVd, VCB_STATE_JOLIET ), VOL_ID_LEN ) ||
3368 (CdRvdVersion( RawIsoVd, VCB_STATE_JOLIET ) != VERSION_1) ||
3369 (CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_TERMINATOR)) {
3370
3371 break;
3372 }
3373
3374 //
3375 // We have a match if this is a secondary descriptor with a matching
3376 // escape sequence.
3377 //
3378
3379 if ((CdRvdDescType( RawIsoVd, VCB_STATE_JOLIET ) == VD_SECONDARY) &&
3380 (RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ),
3381 CdJolietEscape[0],
3382 ESC_SEQ_LEN ) ||
3383 RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ),
3384 CdJolietEscape[1],
3385 ESC_SEQ_LEN ) ||
3386 RtlEqualMemory( CdRvdEsc( RawIsoVd, VCB_STATE_JOLIET ),
3387 CdJolietEscape[2],
3388 ESC_SEQ_LEN ))) {
3389
3390 if (!VerifyVolume) {
3391
3392 //
3393 // Update the Vcb with the new volume descriptor.
3394 //
3395
3396 ClearFlag( Vcb->VcbState, VCB_STATE_ISO );
3397 SetFlag( Vcb->VcbState, VCB_STATE_JOLIET );
3398
3399 Vcb->VdSectorOffset = SectorOffset;
3400 }
3401
3402 FoundSecondaryVd = TRUE;
3403 break;
3404 }
3405
3406 //
3407 // Otherwise move on to the next sector.
3408 //
3409
3410 SectorOffset += 1;
3411 }
3412
3413 //
3414 // If we didn't find the secondary then recover the original volume
3415 // descriptor stored in the second half of the RawIsoVd.
3416 //
3417
3418 if (!FoundSecondaryVd) {
3419
3420 RtlCopyMemory( RawIsoVd,
3421 Add2Ptr( RawIsoVd, SECTOR_SIZE, PVOID ),
3422 SECTOR_SIZE );
3423 }
3424 }
3425
3426 //
3427 // If we're in the verify path, our work is done, since we don't want
3428 // to update any Vcb/Vpb values.
3429 //
3430
3431 if (VerifyVolume) {
3432
3433 return;
3434 }
3435
3436 //
3437 // Compute the serial number and volume label from the volume descriptor.
3438 //
3439
3440 Vcb->Vpb->SerialNumber = CdSerial32( RawIsoVd, SECTOR_SIZE );
3441
3442 //
3443 // Make sure the CD label will fit in the Vpb.
3444 //
3445
3446 NT_ASSERT( VOLUME_ID_LENGTH * sizeof( WCHAR ) <= MAXIMUM_VOLUME_LABEL_LENGTH );
3447
3448 //
3449 // If this is not a Unicode label we must convert it to unicode.
3450 //
3451
3452 if (!FlagOn( Vcb->VcbState, VCB_STATE_JOLIET )) {
3453
3454 //
3455 // Convert the label to unicode. If we get any error then use a name
3456 // length of zero.
3457 //
3458
3459 Vcb->Vpb->VolumeLabelLength = 0;
3460
3461 if (NT_SUCCESS( RtlOemToUnicodeN( &Vcb->Vpb->VolumeLabel[0],
3462 MAXIMUM_VOLUME_LABEL_LENGTH,
3463 &Length,
3464 (PCH)CdRvdVolId( RawIsoVd, Vcb->VcbState ),
3465 VOLUME_ID_LENGTH ))) {
3466
3467 Vcb->Vpb->VolumeLabelLength = (USHORT) Length;
3468 }
3469
3470 //
3471 // We need to convert from big-endian to little endian.
3472 //
3473
3474 } else {
3475
3476 CdConvertBigToLittleEndian( IrpContext,
3477 (PCHAR) CdRvdVolId( RawIsoVd, Vcb->VcbState ),
3478 VOLUME_ID_LENGTH,
3479 (PCHAR) Vcb->Vpb->VolumeLabel );
3480
3481 Vcb->Vpb->VolumeLabelLength = VOLUME_ID_LENGTH * sizeof( WCHAR );
3482 }
3483
3484 //
3485 // Strip the trailing spaces or zeroes from the name.
3486 //
3487
3488 Index = Vcb->Vpb->VolumeLabelLength / sizeof( WCHAR );
3489
3490 while (Index > 0) {
3491
3492 if ((Vcb->Vpb->VolumeLabel[ Index - 1 ] != L'\0') &&
3493 (Vcb->Vpb->VolumeLabel[ Index - 1 ] != L' ')) {
3494
3495 break;
3496 }
3497
3498 Index -= 1;
3499 }
3500
3501 //
3502 // Now set the final length for the name.
3503 //
3504
3505 Vcb->Vpb->VolumeLabelLength = (USHORT) (Index * sizeof( WCHAR ));
3506 }
3507
3508
3509