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