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