3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File System Control routines for Cdfs called
12 by the Fsd/Fsp dispatch drivers.
20 // The Bug check file id for this module
23 #define BugCheckFileId (CDFS_BUG_CHECK_FSCTRL)
29 BOOLEAN CdDisable
= FALSE
;
30 BOOLEAN CdNoJoliet
= FALSE
;
33 // Local support routines
36 _Requires_lock_held_(_Global_critical_region_
)
39 _Inout_ PIRP_CONTEXT IrpContext
,
45 _In_ PIRP_CONTEXT IrpContext
,
48 _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo
51 _Requires_lock_held_(_Global_critical_region_
)
54 _Inout_ PIRP_CONTEXT IrpContext
,
58 _Requires_lock_held_(_Global_critical_region_
)
61 _Inout_ PIRP_CONTEXT IrpContext
,
65 _Requires_lock_held_(_Global_critical_region_
)
68 _Inout_ PIRP_CONTEXT IrpContext
,
72 _Requires_lock_held_(_Global_critical_region_
)
75 _Inout_ PIRP_CONTEXT IrpContext
,
79 _Requires_lock_held_(_Global_critical_region_
)
82 _Inout_ PIRP_CONTEXT IrpContext
,
86 _Requires_lock_held_(_Global_critical_region_
)
89 _Inout_ PIRP_CONTEXT IrpContext
,
95 _Inout_ PIRP_CONTEXT IrpContext
,
101 _Inout_ PIRP_CONTEXT IrpContext
,
107 _Inout_ PIRP_CONTEXT IrpContext
,
111 _Requires_lock_held_(_Global_critical_region_
)
113 CdInvalidateVolumes (
114 _Inout_ PIRP_CONTEXT IrpContext
,
119 CdAllowExtendedDasdIo (
120 _Inout_ PIRP_CONTEXT IrpContext
,
124 _Requires_lock_held_(_Global_critical_region_
)
126 CdScanForDismountedVcb (
127 _Inout_ PIRP_CONTEXT IrpContext
130 _Success_(return != FALSE
)
133 _In_ PIRP_CONTEXT IrpContext
,
135 _Out_writes_bytes_(SECTOR_SIZE
) PCHAR RawIsoVd
,
136 _In_ ULONG BlockFactor
,
137 _In_ BOOLEAN ReturnOnError
,
138 _In_ BOOLEAN VerifyVolume
141 _Success_(return != FALSE
) BOOLEAN
143 _In_ PIRP_CONTEXT IrpContext
,
149 CdFindActiveVolDescriptor (
150 _In_ PIRP_CONTEXT IrpContext
,
152 _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE
)) PCHAR RawIsoVd
,
153 _In_ BOOLEAN VerifyVolume
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)
177 // Local support routine
180 _Requires_lock_held_(_Global_critical_region_
)
181 _Requires_lock_held_(Vcb
->VcbResource
)
183 CdLockVolumeInternal (
184 _In_ PIRP_CONTEXT IrpContext
,
186 _In_opt_ PFILE_OBJECT FileObject
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.
197 The volume must be held exclusive by the caller.
201 Vcb - The volume being locked.
203 FileObject - File corresponding to the handle locking the volume. If this
204 is not specified, a system lock is assumed.
208 NTSTATUS - The return status for the operation
215 NTSTATUS FinalStatus
= (FileObject
? STATUS_ACCESS_DENIED
: STATUS_DEVICE_BUSY
);
216 ULONG RemainingUserReferences
= (FileObject
? 1: 0);
219 // The cleanup count for the volume only reflects the fileobject that
220 // will lock the volume. Otherwise, we must fail the request.
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.
227 CdPurgeVolume( IrpContext
, Vcb
, FALSE
);
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.
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.
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.
244 CdReleaseVcb( IrpContext
, Vcb
);
246 Status
= CcWaitForCurrentLazyWriterActivity();
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
254 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
255 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
257 if (!NT_SUCCESS( Status
)) {
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.
269 IoAcquireVpbSpinLock( &SavedIrql
);
271 if (!FlagOn( Vcb
->Vpb
->Flags
, VPB_LOCKED
) &&
272 (Vcb
->VcbCleanup
== RemainingUserReferences
) &&
273 (Vcb
->VcbUserReference
== CDFS_RESIDUAL_USER_REFERENCE
+ RemainingUserReferences
)) {
275 SetFlag( Vcb
->VcbState
, VCB_STATE_LOCKED
);
276 SetFlag( Vcb
->Vpb
->Flags
, VPB_LOCKED
);
277 Vcb
->VolumeLockFileObject
= FileObject
;
278 FinalStatus
= STATUS_SUCCESS
;
281 IoReleaseVpbSpinLock( SavedIrql
);
288 CdUnlockVolumeInternal (
289 _In_ PIRP_CONTEXT IrpContext
,
291 _In_opt_ PFILE_OBJECT FileObject
298 This routine performs the actual unlock volume operation.
300 The volume must be held exclusive by the caller.
304 Vcb - The volume being locked.
306 FileObject - File corresponding to the handle locking the volume. If this
307 is not specified, a system lock is assumed.
311 NTSTATUS - The return status for the operation
313 Attempting to remove a system lock that did not exist is OK.
318 NTSTATUS Status
= STATUS_NOT_LOCKED
;
321 UNREFERENCED_PARAMETER( IrpContext
);
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.
329 IoAcquireVpbSpinLock( &SavedIrql
);
331 if (FlagOn(Vcb
->Vpb
->Flags
, VPB_LOCKED
) &&
332 (FileObject
== Vcb
->VolumeLockFileObject
)) {
334 ClearFlag( Vcb
->VcbState
, VCB_STATE_LOCKED
);
335 ClearFlag( Vcb
->Vpb
->Flags
, VPB_LOCKED
);
336 Vcb
->VolumeLockFileObject
= NULL
;
337 Status
= STATUS_SUCCESS
;
340 IoReleaseVpbSpinLock( SavedIrql
);
347 _Requires_lock_held_(_Global_critical_region_
)
350 _Inout_ PIRP_CONTEXT IrpContext
,
358 This is the common routine for doing FileSystem control operations called
359 by both the fsd and fsp threads
363 Irp - Supplies the Irp to process
367 NTSTATUS - The return status for the operation
373 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
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
383 switch (IrpSp
->MinorFunction
) {
385 case IRP_MN_USER_FS_REQUEST
:
387 Status
= CdUserFsctl( IrpContext
, Irp
);
390 case IRP_MN_MOUNT_VOLUME
:
392 Status
= CdMountVolume( IrpContext
, Irp
);
395 case IRP_MN_VERIFY_VOLUME
:
397 Status
= CdVerifyVolume( IrpContext
, Irp
);
402 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
403 Status
= STATUS_INVALID_DEVICE_REQUEST
;
412 // Local support routine
415 _Requires_lock_held_(_Global_critical_region_
)
418 _Inout_ PIRP_CONTEXT IrpContext
,
425 This is the common routine for implementing the user's requests made
426 through NtFsControlFile.
430 Irp - Supplies the Irp being processed
434 NTSTATUS - The return status for the operation
440 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
445 // Case on the control code.
448 switch ( IrpSp
->Parameters
.FileSystemControl
.FsControlCode
) {
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
:
459 Status
= CdOplockRequest( IrpContext
, Irp
);
462 case FSCTL_LOCK_VOLUME
:
464 Status
= CdLockVolume( IrpContext
, Irp
);
467 case FSCTL_UNLOCK_VOLUME
:
469 Status
= CdUnlockVolume( IrpContext
, Irp
);
472 case FSCTL_DISMOUNT_VOLUME
:
474 Status
= CdDismountVolume( IrpContext
, Irp
);
477 case FSCTL_IS_VOLUME_DIRTY
:
479 Status
= CdIsVolumeDirty( IrpContext
, Irp
);
482 case FSCTL_IS_VOLUME_MOUNTED
:
484 Status
= CdIsVolumeMounted( IrpContext
, Irp
);
487 case FSCTL_IS_PATHNAME_VALID
:
489 Status
= CdIsPathnameValid( IrpContext
, Irp
);
492 case FSCTL_INVALIDATE_VOLUMES
:
494 Status
= CdInvalidateVolumes( IrpContext
, Irp
);
497 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
499 Status
= CdAllowExtendedDasdIo( IrpContext
, Irp
);
503 // We don't support any of the known or unknown requests.
508 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
509 Status
= STATUS_INVALID_DEVICE_REQUEST
;
519 _In_ PIRP_CONTEXT IrpContext
,
522 _In_ PDEVICE_OBJECT DeviceObjectWeTalkTo
529 UNREFERENCED_PARAMETER( IrpContext
);
531 ObDereferenceObject( OldVcb
->TargetDeviceObject
);
533 IoAcquireVpbSpinLock( &SavedIrql
);
535 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
536 NewVcb
->Vpb
->RealDevice
->Vpb
= OldVcb
->Vpb
;
538 OldVcb
->Vpb
->RealDevice
= NewVcb
->Vpb
->RealDevice
;
539 OldVcb
->TargetDeviceObject
= DeviceObjectWeTalkTo
;
541 CdUpdateVcbCondition( OldVcb
, VcbMounted
);
542 CdUpdateMediaChangeCount( OldVcb
, NewVcb
->MediaChangeCount
);
544 ClearFlag( OldVcb
->VcbState
, VCB_STATE_VPB_NOT_ON_DEVICE
);
546 Buffer
= OldVcb
->SectorCacheBuffer
= NewVcb
->SectorCacheBuffer
;
547 NewVcb
->SectorCacheBuffer
= NULL
;
549 if (NULL
!= Buffer
) {
551 for (Index
= 0; Index
< CD_SEC_CACHE_CHUNKS
; Index
++) {
553 OldVcb
->SecCacheChunks
[ Index
].Buffer
= Buffer
;
554 OldVcb
->SecCacheChunks
[ Index
].BaseLbn
= (ULONG
)-1;
556 Buffer
+= CD_SEC_CHUNK_BLOCKS
* SECTOR_SIZE
;
560 IoReleaseVpbSpinLock( SavedIrql
);
565 // Local support routine
568 _Requires_lock_held_(_Global_critical_region_
)
571 _Inout_ PIRP_CONTEXT IrpContext
,
579 This routine performs the mount volume operation. It is responsible for
580 either completing of enqueuing the input Irp.
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:
586 1. Create a new Vcb Structure, and initialize it enough to do I/O
587 through the on-disk volume descriptors.
589 2. Read the disk and check if it is a Cdrom volume.
591 3. If it is not a Cdrom volume then delete the Vcb and
592 complete the IRP back with an appropriate status.
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.
598 5. Otherwise create a Vcb and root DCB for each valid volume descriptor.
602 Irp - Supplies the Irp to process
606 NTSTATUS - The return status for the operation
613 PVOLUME_DEVICE_OBJECT VolDo
= NULL
;
618 BOOLEAN FoundPvd
= FALSE
;
619 BOOLEAN SetDoVerifyOnFail
;
621 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
622 PDEVICE_OBJECT DeviceObjectWeTalkTo
= IrpSp
->Parameters
.MountVolume
.DeviceObject
;
623 PVPB Vpb
= IrpSp
->Parameters
.MountVolume
.Vpb
;
625 PFILE_OBJECT FileObjectToNotify
= NULL
;
628 DISK_GEOMETRY DiskGeometry
;
630 IO_SCSI_CAPABILITIES Capabilities
;
632 IO_STATUS_BLOCK Iosb
;
634 PCHAR RawIsoVd
= NULL
;
636 PCDROM_TOC_LARGE CdromToc
= NULL
;
638 ULONG TocTrackCount
= 0;
639 ULONG TocDiskFlags
= 0;
640 ULONG MediaChangeCount
= 0;
642 #ifdef CDFS_TELEMETRY_DATA
644 GUID VolumeCorrelationId
= { 0 };
650 // Check that we are talking to a Cdrom device. This request should
651 // always be waitable.
654 NT_ASSERT( Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_CD_ROM
);
655 NT_ASSERT( FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
));
657 #ifdef CDFS_TELEMETRY_DATA
659 // We don't want a bogus VolumeGuid to show up in our telemetry
662 RtlZeroMemory( &VolumeGuid
, sizeof(GUID
) );
667 // Update the real device in the IrpContext from the Vpb. There was no available
668 // file object when the IrpContext was created.
671 IrpContext
->RealDevice
= Vpb
->RealDevice
;
673 SetDoVerifyOnFail
= CdRealDevNeedsVerify( IrpContext
->RealDevice
);
676 // Check if we have disabled the mount process.
681 CdCompleteRequest( IrpContext
, Irp
, STATUS_UNRECOGNIZED_VOLUME
);
682 return STATUS_UNRECOGNIZED_VOLUME
;
686 // If we've shutdown disallow further mounts.
689 if (FlagOn( CdData
.Flags
, CD_FLAGS_SHUTDOWN
)) {
691 CdCompleteRequest( IrpContext
, Irp
, STATUS_SYSTEM_SHUTDOWN
);
692 return STATUS_SYSTEM_SHUTDOWN
;
696 // Do a CheckVerify here to lift the MediaChange ticker from the driver
699 Status
= CdPerformDevIoCtrl( IrpContext
,
700 IOCTL_CDROM_CHECK_VERIFY
,
701 DeviceObjectWeTalkTo
,
708 if (!NT_SUCCESS( Status
)) {
710 CdCompleteRequest( IrpContext
, Irp
, Status
);
714 if (Iosb
.Information
!= sizeof(ULONG
)) {
717 // Be safe about the count in case the driver didn't fill it in
720 MediaChangeCount
= 0;
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.
728 Status
= CdPerformDevIoCtrl( IrpContext
,
729 IOCTL_CDROM_GET_DRIVE_GEOMETRY
,
730 DeviceObjectWeTalkTo
,
732 sizeof( DISK_GEOMETRY
),
738 // Return insufficient sources to our caller.
741 if (Status
== STATUS_INSUFFICIENT_RESOURCES
) {
743 CdCompleteRequest( IrpContext
, Irp
, Status
);
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
755 if (NT_SUCCESS( Status
) &&
756 (DiskGeometry
.BytesPerSector
!= 0) &&
757 (DiskGeometry
.BytesPerSector
< SECTOR_SIZE
)) {
759 BlockFactor
= SECTOR_SIZE
/ DiskGeometry
.BytesPerSector
;
763 // Acquire the global resource to do mount operations.
766 CdAcquireCdData( IrpContext
);
769 // Use a try-finally to facilitate cleanup.
775 // Allocate a buffer to query the TOC.
778 CdromToc
= FsRtlAllocatePoolWithTag( CdPagedPool
,
779 sizeof( CDROM_TOC_LARGE
),
782 RtlZeroMemory( CdromToc
, sizeof( CDROM_TOC_LARGE
));
785 // Do a quick check to see if there any Vcb's which can be removed.
788 CdScanForDismountedVcb( IrpContext
);
791 // Get our device object and alignment requirement.
794 Status
= IoCreateDevice( CdData
.DriverObject
,
795 sizeof( VOLUME_DEVICE_OBJECT
) - sizeof( DEVICE_OBJECT
),
797 FILE_DEVICE_CD_ROM_FILE_SYSTEM
,
800 (PDEVICE_OBJECT
*) &VolDo
);
802 if (!NT_SUCCESS( Status
)) { try_leave( Status
); }
805 // Our alignment requirement is the larger of the processor alignment requirement
806 // already in the volume device object and that in the DeviceObjectWeTalkTo
809 if (DeviceObjectWeTalkTo
->AlignmentRequirement
> VolDo
->DeviceObject
.AlignmentRequirement
) {
811 VolDo
->DeviceObject
.AlignmentRequirement
= DeviceObjectWeTalkTo
->AlignmentRequirement
;
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.
819 ((PDEVICE_OBJECT
) VolDo
)->StackSize
= (CCHAR
) (DeviceObjectWeTalkTo
->StackSize
+ 1);
820 StackSize
= ((PDEVICE_OBJECT
) VolDo
)->StackSize
;
822 ClearFlag( VolDo
->DeviceObject
.Flags
, DO_DEVICE_INITIALIZING
);
825 // Initialize the overflow queue for the volume
828 VolDo
->OverflowQueueCount
= 0;
829 InitializeListHead( &VolDo
->OverflowQueue
);
831 VolDo
->PostedRequestCount
= 0;
832 KeInitializeSpinLock( &VolDo
->OverflowQueueSpinLock
);
835 // Let's query for the Toc now and handle any error we get from this operation.
838 Status
= CdProcessToc( IrpContext
,
839 DeviceObjectWeTalkTo
,
846 // If we failed to read the TOC, then bail out. Probably blank media.
849 if (Status
!= STATUS_SUCCESS
) {
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
860 Vpb
->DeviceObject
= (PDEVICE_OBJECT
) VolDo
;
863 // Initialize the Vcb. This routine will raise on an allocation
867 CdInitializeVcb( IrpContext
,
869 DeviceObjectWeTalkTo
,
879 // Show that we initialized the Vcb and can cleanup with the Vcb.
887 #ifdef CDFS_TELEMETRY_DATA
890 // Initialize the volume guid.
893 if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb
->TargetDeviceObject
, &VolumeGuid
))) {
896 // We got a GUID, set it in the Telemetry structure
899 RtlCopyMemory( &CdTelemetryData
.VolumeGuid
, &VolumeGuid
, sizeof(GUID
) );
903 // Initialize the correlation ID.
906 if (NT_SUCCESS( FsRtlVolumeDeviceToCorrelationId( Vcb
->TargetDeviceObject
, &VolumeCorrelationId
) )) {
909 // Stash a copy away in the VCB.
912 RtlCopyMemory( &Vcb
->VolumeCorrelationId
, &VolumeCorrelationId
, sizeof( GUID
) );
915 #endif // CDFS_TELEMETRY_DATA
917 // Lock object is acquired and released using internal state
918 _Analysis_suppress_lock_checking_(Vcb
->VcbResource
);
921 // Store the Vcb in the IrpContext as we didn't have one before.
924 IrpContext
->Vcb
= Vcb
;
926 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
929 // Let's reference the Vpb to make sure we are the one to
930 // have the last dereference.
933 Vcb
->Vpb
->ReferenceCount
+= 1;
936 // Clear the verify bit for the start of mount.
939 CdMarkRealDevVerifyOk( Vcb
->Vpb
->RealDevice
);
941 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
)) {
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.
948 RawIsoVd
= FsRtlAllocatePoolWithTag( CdNonPagedPool
,
949 ROUND_TO_PAGES( SECTOR_SIZE
),
953 // Try to find the primary volume descriptor.
956 FoundPvd
= CdFindPrimaryVd( IrpContext
,
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.
975 if (!(TocDiskFlags
& CDROM_DISK_AUDIO_TRACK
) ||
976 BooleanFlagOn( Vcb
->CdromToc
->TrackData
[0].Control
, TOC_DATA_TRACK
)) {
978 try_leave( Status
= STATUS_UNRECOGNIZED_VOLUME
);
981 SetFlag( Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
| VCB_STATE_CDXA
);
983 CdFreePool( &RawIsoVd
);
989 // Look and see if there is a secondary volume descriptor we want to
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.
1001 RtlCopyMemory( Add2Ptr( RawIsoVd
, SECTOR_SIZE
, PVOID
),
1006 // We have the initial volume descriptor. Locate a secondary
1007 // volume descriptor if present.
1010 CdFindActiveVolDescriptor( IrpContext
,
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.
1027 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
) &&
1028 ((Vcb
->CdromToc
->LastTrack
- Vcb
->CdromToc
->FirstTrack
) == 0)) {
1034 Vcb
->SectorCacheBuffer
= FsRtlAllocatePool( CdPagedPool
,
1035 CD_SEC_CACHE_CHUNKS
*
1036 CD_SEC_CHUNK_BLOCKS
*
1039 for (Index
= 0; Index
< (ULONG
)CD_SEC_CACHE_CHUNKS
; Index
++) {
1041 Vcb
->SecCacheChunks
[ Index
].Buffer
= Buffer
;
1042 Vcb
->SecCacheChunks
[ Index
].BaseLbn
= (ULONG
)-1;
1044 Buffer
+= CD_SEC_CHUNK_BLOCKS
* SECTOR_SIZE
;
1047 Vcb
->SectorCacheIrp
= IoAllocateIrp( StackSize
, FALSE
);
1049 if (Vcb
->SectorCacheIrp
== NULL
) {
1051 try_leave( Status
= STATUS_INSUFFICIENT_RESOURCES
);
1054 IoInitializeIrp( Vcb
->SectorCacheIrp
,
1055 IoSizeOfIrp( StackSize
),
1058 KeInitializeEvent( &Vcb
->SectorCacheEvent
, SynchronizationEvent
, FALSE
);
1059 ExInitializeResourceLite( &Vcb
->SectorCacheResource
);
1063 // Check if this is a remount operation. If so then clean up
1064 // the data structures passed in and created here.
1067 if (CdIsRemount( IrpContext
, Vcb
, &OldVcb
)) {
1069 NT_ASSERT( NULL
!= OldVcb
->SwapVpb
);
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.
1077 CdReMountOldVcb( IrpContext
,
1080 DeviceObjectWeTalkTo
);
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.
1090 if (FlagOn( OldVcb
->VcbState
, VCB_STATE_NOTIFY_REMOUNT
)) {
1092 ClearFlag( OldVcb
->VcbState
, VCB_STATE_NOTIFY_REMOUNT
);
1094 FileObjectToNotify
= OldVcb
->RootIndexFcb
->FileObject
;
1095 ObReferenceObject( FileObjectToNotify
);
1098 try_leave( Status
= STATUS_SUCCESS
);
1102 // This is a new mount. Go ahead and initialize the
1103 // Vcb from the volume descriptor.
1106 CdUpdateVcbFromVolDescriptor( IrpContext
,
1111 // Drop an extra reference on the root dir file so we'll be able to send
1115 if (Vcb
->RootIndexFcb
) {
1117 FileObjectToNotify
= Vcb
->RootIndexFcb
->FileObject
;
1118 ObReferenceObject( FileObjectToNotify
);
1122 // Now check the maximum transfer limits on the device in case we
1123 // get raw reads on this volume.
1126 Status
= CdPerformDevIoCtrl( IrpContext
,
1127 IOCTL_SCSI_GET_CAPABILITIES
,
1128 DeviceObjectWeTalkTo
,
1130 sizeof( IO_SCSI_CAPABILITIES
),
1135 if (NT_SUCCESS(Status
)) {
1137 Vcb
->MaximumTransferRawSectors
= Capabilities
.MaximumTransferLength
/ RAW_SECTOR_SIZE
;
1138 Vcb
->MaximumPhysicalPages
= Capabilities
.MaximumPhysicalPages
;
1143 // This should never happen, but we can safely assume 64k and 16 pages.
1146 Vcb
->MaximumTransferRawSectors
= (64 * 1024) / RAW_SECTOR_SIZE
;
1147 Vcb
->MaximumPhysicalPages
= 16;
1151 // The new mount is complete. Remove the additional references on this
1152 // Vcb and the device we are mounted on top of.
1155 Vcb
->VcbReference
-= CDFS_RESIDUAL_REFERENCE
;
1156 NT_ASSERT( Vcb
->VcbReference
== CDFS_RESIDUAL_REFERENCE
);
1158 ObDereferenceObject( Vcb
->TargetDeviceObject
);
1160 CdUpdateVcbCondition( Vcb
, VcbMounted
);
1162 CdReleaseVcb( IrpContext
, Vcb
);
1165 Status
= STATUS_SUCCESS
;
1170 // Free the TOC buffer if not in the Vcb.
1173 if (CdromToc
!= NULL
) {
1175 CdFreePool( &CdromToc
);
1179 // Free the sector buffer if allocated.
1182 if (RawIsoVd
!= NULL
) {
1184 CdFreePool( &RawIsoVd
);
1188 // If we are not mounting the device, then set the verify bit again.
1191 if ((AbnormalTermination() || (Status
!= STATUS_SUCCESS
)) &&
1192 SetDoVerifyOnFail
) {
1194 CdMarkRealDevForVerify( IrpContext
->RealDevice
);
1198 // If we didn't complete the mount then cleanup any remaining structures.
1201 if (Vpb
!= NULL
) { Vpb
->DeviceObject
= NULL
; }
1206 // Make sure there is no Vcb in the IrpContext since it could go away
1209 IrpContext
->Vcb
= NULL
;
1211 Vcb
->VcbReference
-= CDFS_RESIDUAL_REFERENCE
;
1213 if (CdDismountVcb( IrpContext
, Vcb
)) {
1215 CdReleaseVcb( IrpContext
, Vcb
);
1218 } else if (VolDo
!= NULL
) {
1220 IoDeleteDevice( (PDEVICE_OBJECT
) VolDo
);
1224 // Release the global resource.
1227 CdReleaseCdData( IrpContext
);
1231 // Now send mount notification.
1234 if (FileObjectToNotify
) {
1236 FsRtlNotifyVolumeEvent( FileObjectToNotify
, FSRTL_VOLUME_MOUNT
);
1237 ObDereferenceObject( FileObjectToNotify
);
1240 #ifdef CDFS_TELEMETRY_DATA
1246 CdTelemetryMountSafe( &VolumeCorrelationId
, STATUS_SUCCESS
, Vcb
);
1251 // Complete the request if no exception.
1254 CdCompleteRequest( IrpContext
, Irp
, Status
);
1260 // Local support routine
1263 _Requires_lock_held_(_Global_critical_region_
)
1266 _Inout_ PIRP_CONTEXT IrpContext
,
1272 Routine Description:
1274 This routine performs the verify volume operation. It is responsible for
1275 either completing of enqueuing the input Irp.
1279 Irp - Supplies the Irp to process
1283 NTSTATUS - The return status for the operation
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
;
1292 PCHAR RawIsoVd
= NULL
;
1294 PCDROM_TOC_LARGE CdromToc
= NULL
;
1295 ULONG TocLength
= 0;
1296 ULONG TocTrackCount
= 0;
1297 ULONG TocDiskFlags
= 0;
1299 ULONG MediaChangeCount
= Vcb
->MediaChangeCount
;
1301 PFILE_OBJECT FileObjectToNotify
= NULL
;
1303 BOOLEAN ReturnError
;
1304 BOOLEAN ReleaseVcb
= FALSE
;
1306 IO_STATUS_BLOCK Iosb
;
1309 UNICODE_STRING UnicodeLabel
;
1311 WCHAR VolumeLabel
[ VOLUME_ID_LENGTH
];
1312 ULONG VolumeLabelLength
;
1316 NTSTATUS Status
= STATUS_SUCCESS
;
1321 // We check that we are talking to a Cdrom device.
1324 NT_ASSERT( Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_CD_ROM
);
1325 NT_ASSERT( FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
));
1328 // Update the real device in the IrpContext from the Vpb. There was no available
1329 // file object when the IrpContext was created.
1332 IrpContext
->RealDevice
= Vpb
->RealDevice
;
1335 // Acquire the global resource to synchronise against mounts and teardown,
1336 // finally clause releases.
1339 CdAcquireCdData( IrpContext
);
1343 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
1347 // Check to see if the volume is eligible for verification.
1350 if ((Vcb
->VcbCondition
== VcbInvalid
) ||
1351 (Vcb
->VcbCondition
== VcbDismountInProgress
)) {
1353 try_return( Status
= STATUS_WRONG_VOLUME
);
1357 // Verify that there is a disk here.
1360 Status
= CdPerformDevIoCtrl( IrpContext
,
1361 IOCTL_CDROM_CHECK_VERIFY
,
1362 Vcb
->TargetDeviceObject
,
1369 if (!NT_SUCCESS( Status
)) {
1372 // If we will allow a raw mount then return WRONG_VOLUME to
1373 // allow the volume to be mounted by raw.
1376 if (FlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
)) {
1378 Status
= STATUS_WRONG_VOLUME
;
1381 try_return( Status
);
1384 if (Iosb
.Information
!= sizeof(ULONG
)) {
1387 // Be safe about the count in case the driver didn't fill it in
1390 MediaChangeCount
= 0;
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.
1398 if (MediaChangeCount
== 0 ||
1399 (Vcb
->MediaChangeCount
!= MediaChangeCount
)) {
1402 // Allocate a buffer to query the TOC.
1405 CdromToc
= FsRtlAllocatePoolWithTag( CdPagedPool
,
1406 sizeof( CDROM_TOC_LARGE
),
1409 RtlZeroMemory( CdromToc
, sizeof( CDROM_TOC_LARGE
));
1412 // Let's query for the Toc now and handle any error we get from this operation.
1415 Status
= CdProcessToc( IrpContext
,
1416 Vcb
->TargetDeviceObject
,
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.
1427 if (Status
!= STATUS_SUCCESS
) {
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.
1436 if (!CdIsRawDevice( IrpContext
, Status
)) {
1438 Status
= STATUS_WRONG_VOLUME
;
1441 try_return( Status
);
1444 // We got a TOC. Verify that it matches the previous Toc.
1447 } else if ((Vcb
->TocLength
!= TocLength
) ||
1448 (Vcb
->TrackCount
!= TocTrackCount
) ||
1449 (Vcb
->DiskFlags
!= TocDiskFlags
) ||
1450 !RtlEqualMemory( CdromToc
,
1454 try_return( Status
= STATUS_WRONG_VOLUME
);
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.
1462 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
)) {
1465 // Allocate a buffer for the sector buffer.
1468 RawIsoVd
= FsRtlAllocatePoolWithTag( CdNonPagedPool
,
1469 ROUND_TO_PAGES( 2 * SECTOR_SIZE
),
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.
1479 ReturnError
= FALSE
;
1481 if (FlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
)) {
1486 if (!CdFindPrimaryVd( IrpContext
,
1494 // If the previous Vcb did not represent a raw disk
1495 // then show this volume was dismounted.
1498 try_return( Status
= STATUS_WRONG_VOLUME
);
1504 // Look for a supplementary VD.
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.
1511 RtlCopyMemory( Add2Ptr( RawIsoVd
, SECTOR_SIZE
, PVOID
),
1516 // We have the initial volume descriptor. Locate a secondary
1517 // volume descriptor if present.
1520 CdFindActiveVolDescriptor( IrpContext
,
1525 // Compare the serial numbers. If they don't match, set the
1526 // status to wrong volume.
1529 if (Vpb
->SerialNumber
!= CdSerial32( RawIsoVd
, SECTOR_SIZE
)) {
1531 try_return( Status
= STATUS_WRONG_VOLUME
);
1535 // Verify the volume labels.
1538 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
1541 // Compute the length of the volume name
1544 AnsiLabel
.Buffer
= (PCHAR
)CdRvdVolId( RawIsoVd
, Vcb
->VcbState
);
1545 AnsiLabel
.MaximumLength
= AnsiLabel
.Length
= (ULONG
)VOLUME_ID_LENGTH
;
1547 UnicodeLabel
.MaximumLength
= VOLUME_ID_LENGTH
* sizeof( WCHAR
);
1548 UnicodeLabel
.Buffer
= VolumeLabel
;
1551 // Convert this to unicode. If we get any error then use a name
1555 VolumeLabelLength
= 0;
1557 if (NT_SUCCESS( RtlOemStringToCountedUnicodeString( &UnicodeLabel
,
1561 VolumeLabelLength
= UnicodeLabel
.Length
;
1565 // We need to convert from big-endian to little endian.
1570 CdConvertBigToLittleEndian( IrpContext
,
1571 (PCHAR
) CdRvdVolId( RawIsoVd
, Vcb
->VcbState
),
1573 (PCHAR
) VolumeLabel
);
1575 VolumeLabelLength
= VOLUME_ID_LENGTH
;
1579 // Strip the trailing spaces or zeroes from the name.
1582 Index
= VolumeLabelLength
/ sizeof( WCHAR
);
1586 if ((VolumeLabel
[ Index
- 1 ] != L
'\0') &&
1587 (VolumeLabel
[ Index
- 1 ] != L
' ')) {
1596 // Now set the final length for the name.
1599 VolumeLabelLength
= (USHORT
) (Index
* sizeof( WCHAR
));
1602 // Now check that the label matches.
1604 if ((Vpb
->VolumeLabelLength
!= VolumeLabelLength
) ||
1605 !RtlEqualMemory( Vpb
->VolumeLabel
,
1607 VolumeLabelLength
)) {
1609 try_return( Status
= STATUS_WRONG_VOLUME
);
1616 // The volume is OK, clear the verify bit.
1619 CdUpdateVcbCondition( Vcb
, VcbMounted
);
1621 CdMarkRealDevVerifyOk( Vpb
->RealDevice
);
1624 // See if we will need to provide notification of the remount. This is the readonly
1625 // filesystem's form of dismount/mount notification.
1628 if (FlagOn( Vcb
->VcbState
, VCB_STATE_NOTIFY_REMOUNT
)) {
1630 ClearFlag( Vcb
->VcbState
, VCB_STATE_NOTIFY_REMOUNT
);
1632 FileObjectToNotify
= Vcb
->RootIndexFcb
->FileObject
;
1633 ObReferenceObject( FileObjectToNotify
);
1639 // Update the media change count to note that we have verified the volume
1640 // at this value - regardless of the outcome.
1643 CdUpdateMediaChangeCount( Vcb
, MediaChangeCount
);
1646 // If the volume was already unmounted, nothing more to do.
1649 if (Vcb
->VcbCondition
== VcbNotMounted
) {
1651 Status
= STATUS_WRONG_VOLUME
;
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.
1658 } else if ((Vcb
->VcbCondition
== VcbMounted
) && (Status
== STATUS_WRONG_VOLUME
)) {
1660 CdUpdateVcbCondition( Vcb
, VcbNotMounted
);
1662 if (Vcb
->XASector
!= NULL
) {
1664 CdFreePool( &Vcb
->XASector
);
1666 Vcb
->XADiskOffset
= 0;
1669 CdFreeDirCache( IrpContext
);
1672 // Now, if there are no user handles to the volume, try to spark
1673 // teardown by purging the volume.
1676 if (Vcb
->VcbCleanup
== 0) {
1678 if (NT_SUCCESS( CdPurgeVolume( IrpContext
, Vcb
, FALSE
))) {
1680 ReleaseVcb
= CdCheckForDismount( IrpContext
, Vcb
, FALSE
);
1689 // Free the TOC buffer if allocated.
1692 if (CdromToc
!= NULL
) {
1694 CdFreePool( &CdromToc
);
1697 if (RawIsoVd
!= NULL
) {
1699 CdFreePool( &RawIsoVd
);
1704 CdReleaseVcb( IrpContext
, Vcb
);
1707 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
1710 CdReleaseCdData( IrpContext
);
1714 // Now send mount notification.
1717 if (FileObjectToNotify
) {
1719 FsRtlNotifyVolumeEvent( FileObjectToNotify
, FSRTL_VOLUME_MOUNT
);
1720 ObDereferenceObject( FileObjectToNotify
);
1724 // Complete the request if no exception.
1727 CdCompleteRequest( IrpContext
, Irp
, Status
);
1733 // Local support routine
1736 _Requires_lock_held_(_Global_critical_region_
)
1739 _Inout_ PIRP_CONTEXT IrpContext
,
1745 Routine Description:
1747 This is the common routine to handle oplock requests made via the
1748 NtFsControlFile call.
1752 Irp - Supplies the Irp being processed
1756 NTSTATUS - The return status for the operation
1761 NTSTATUS Status
= STATUS_SUCCESS
;
1765 ULONG OplockCount
= 0;
1766 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
1771 // We only permit oplock requests on files.
1774 if (CdDecodeFileObject( IrpContext
,
1777 &Ccb
) != UserFileOpen
) {
1779 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
1780 return STATUS_INVALID_PARAMETER
;
1784 // Make this a waitable Irpcontext so we don't fail to acquire
1788 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
1789 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_FORCE_POST
);
1792 // Switch on the function control code. We grab the Fcb exclusively
1793 // for oplock requests, shared for oplock break acknowledgement.
1796 switch (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
) {
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
:
1803 CdAcquireFcbExclusive( IrpContext
, Fcb
, FALSE
);
1805 if (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_2
) {
1807 if (Fcb
->FileLock
!= NULL
) {
1809 #if (NTDDI_VERSION >= NTDDI_WIN7)
1810 OplockCount
= (ULONG
) FsRtlAreThereCurrentOrInProgressFileLocks( Fcb
->FileLock
);
1812 OplockCount
= (ULONG
) FsRtlAreThereCurrentFileLocks( Fcb
->FileLock
);
1818 OplockCount
= Fcb
->FcbCleanup
;
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
:
1828 CdAcquireFcbShared( IrpContext
, Fcb
, FALSE
);
1833 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
1834 return STATUS_INVALID_PARAMETER
;
1838 // Use a try finally to free the Fcb.
1847 CdVerifyFcbOperation( IrpContext
, Fcb
);
1850 // Call the FsRtl routine to grant/acknowledge oplock.
1853 Status
= FsRtlOplockFsctrl( CdGetFcbOplock(Fcb
),
1858 // Set the flag indicating if Fast I/O is possible
1861 CdLockFcb( IrpContext
, Fcb
);
1862 Fcb
->IsFastIoPossible
= CdIsFastIoPossible( Fcb
);
1863 CdUnlockFcb( IrpContext
, Fcb
);
1866 // The oplock package will complete the Irp.
1874 // Release all of our resources
1877 CdReleaseFcb( IrpContext
, Fcb
);
1881 // Complete the request if there was no exception.
1884 CdCompleteRequest( IrpContext
, Irp
, Status
);
1890 // Local support routine
1893 _Requires_lock_held_(_Global_critical_region_
)
1896 _Inout_ PIRP_CONTEXT IrpContext
,
1902 Routine Description:
1904 This routine performs the lock volume operation. It is responsible for
1905 either completing of enqueuing the input Irp.
1909 Irp - Supplies the Irp to process
1913 NTSTATUS - The return status for the operation
1918 NTSTATUS Status
= STATUS_SUCCESS
;
1920 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
1929 // Decode the file object, the only type of opens we accept are
1930 // user volume opens.
1933 if (CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
1935 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
1937 return STATUS_INVALID_PARAMETER
;
1941 // Send our notification so that folks that like to hold handles on
1942 // volumes can get out of the way.
1945 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
1948 // Acquire exclusive access to the Vcb.
1952 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
1960 CdVerifyVcb( IrpContext
, Vcb
);
1962 Status
= CdLockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
1970 CdReleaseVcb( IrpContext
, Vcb
);
1972 if (AbnormalTermination() || !NT_SUCCESS( Status
)) {
1974 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
1979 // Complete the request if there haven't been any exceptions.
1982 CdCompleteRequest( IrpContext
, Irp
, Status
);
1988 // Local support routine
1991 _Requires_lock_held_(_Global_critical_region_
)
1994 _Inout_ PIRP_CONTEXT IrpContext
,
2000 Routine Description:
2002 This routine performs the unlock volume operation. It is responsible for
2003 either completing of enqueuing the input Irp.
2007 Irp - Supplies the Irp to process
2011 NTSTATUS - The return status for the operation
2018 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2027 // Decode the file object, the only type of opens we accept are
2028 // user volume opens.
2031 if (CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
2033 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2034 return STATUS_INVALID_PARAMETER
;
2038 // Acquire exclusive access to the Vcb.
2043 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
2046 // We won't check for a valid Vcb for this request. An unlock will always
2047 // succeed on a locked volume.
2050 Status
= CdUnlockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
2053 // Release all of our resources
2056 CdReleaseVcb( IrpContext
, Vcb
);
2059 // Send notification that the volume is avaliable.
2062 if (NT_SUCCESS( Status
)) {
2064 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
2068 // Complete the request if there haven't been any exceptions.
2071 CdCompleteRequest( IrpContext
, Irp
, Status
);
2078 // Local support routine
2081 _Requires_lock_held_(_Global_critical_region_
)
2084 _Inout_ PIRP_CONTEXT IrpContext
,
2090 Routine Description:
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.
2100 Irp - Supplies the Irp to process
2104 NTSTATUS - The return status for the operation
2110 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2118 if (CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
2120 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2121 return STATUS_INVALID_PARAMETER
;
2127 // Send dismount notification.
2130 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_DISMOUNT
);
2133 // Make this request waitable.
2136 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
2139 // Acquire exclusive access to the Vcb, and take the global resource to
2140 // sync. against mounts, verifies etc.
2143 CdAcquireCdData( IrpContext
);
2144 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
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.
2151 if (Vcb
->VcbCondition
!= VcbMounted
) {
2153 Status
= STATUS_VOLUME_DISMOUNTED
;
2158 // Invalidate the volume right now.
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.
2165 CdLockVcb( IrpContext
, Vcb
);
2167 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
2169 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
2172 SetFlag( Vcb
->VcbState
, VCB_STATE_DISMOUNTED
);
2174 CdUnlockVcb( IrpContext
, Vcb
);
2178 // Set flag to tell the close path that we want to force dismount
2179 // the volume when this handle is closed.
2182 SetFlag( Ccb
->Flags
, CCB_FLAG_DISMOUNT_ON_CLOSE
);
2184 Status
= STATUS_SUCCESS
;
2188 // Release all of our resources
2191 CdReleaseVcb( IrpContext
, Vcb
);
2192 CdReleaseCdData( IrpContext
);
2194 #if (NTDDI_VERSION >= NTDDI_WIN8)
2196 FsRtlDismountComplete( Vcb
->TargetDeviceObject
, Status
);
2201 // Complete the request if there haven't been any exceptions.
2204 CdCompleteRequest( IrpContext
, Irp
, Status
);
2210 // Local support routine
2215 _Inout_ PIRP_CONTEXT IrpContext
,
2221 Routine Description:
2223 This routine determines if a volume is currently dirty.
2227 Irp - Supplies the Irp to process
2231 NTSTATUS - The return status for the operation
2236 PIO_STACK_LOCATION IrpSp
;
2238 TYPE_OF_OPEN TypeOfOpen
;
2247 // Get the current stack location and extract the output
2248 // buffer information.
2251 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2254 // Get a pointer to the output buffer.
2257 if (Irp
->AssociatedIrp
.SystemBuffer
!= NULL
) {
2259 VolumeState
= Irp
->AssociatedIrp
.SystemBuffer
;
2263 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_USER_BUFFER
);
2264 return STATUS_INVALID_USER_BUFFER
;
2268 // Make sure the output buffer is large enough and then initialize
2269 // the answer to be that the volume isn't dirty.
2272 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
)) {
2274 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2275 return STATUS_INVALID_PARAMETER
;
2281 // Decode the file object
2284 TypeOfOpen
= CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
);
2286 if (TypeOfOpen
!= UserVolumeOpen
) {
2288 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2289 return STATUS_INVALID_PARAMETER
;
2292 if (Fcb
->Vcb
->VcbCondition
!= VcbMounted
) {
2294 CdCompleteRequest( IrpContext
, Irp
, STATUS_VOLUME_DISMOUNTED
);
2295 return STATUS_VOLUME_DISMOUNTED
;
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.
2303 Irp
->IoStatus
.Information
= sizeof( ULONG
);
2305 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
2306 return STATUS_SUCCESS
;
2311 // Local support routine
2316 _Inout_ PIRP_CONTEXT IrpContext
,
2322 Routine Description:
2324 This routine determines if a volume is currently mounted.
2328 Irp - Supplies the Irp to process
2332 NTSTATUS - The return status for the operation
2337 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2345 // Decode the file object.
2348 CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
);
2353 // Disable PopUps, we want to return any error.
2356 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
2359 // Verify the Vcb. This will raise in the error condition.
2362 CdVerifyVcb( IrpContext
, Fcb
->Vcb
);
2365 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
2367 return STATUS_SUCCESS
;
2372 // Local support routine
2377 _Inout_ PIRP_CONTEXT IrpContext
,
2383 Routine Description:
2385 This routine determines if pathname is a valid CDFS pathname.
2386 We always succeed this request.
2390 Irp - Supplies the Irp to process.
2401 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
2402 return STATUS_SUCCESS
;
2407 // Local support routine
2410 _Requires_lock_held_(_Global_critical_region_
)
2412 CdInvalidateVolumes (
2413 _Inout_ PIRP_CONTEXT IrpContext
,
2419 Routine Description:
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.
2427 Irp - Supplies the Irp to process
2431 NTSTATUS - The return status for the operation
2437 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2440 BOOLEAN UnlockVcb
= FALSE
;
2442 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
2450 PFILE_OBJECT FileToMarkBad
;
2451 PDEVICE_OBJECT DeviceToMarkBad
;
2454 // We only allow the invalidate call to come in on our file system devices.
2457 if (IrpSp
->DeviceObject
!= CdData
.FileSystemDeviceObject
) {
2459 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
2461 return STATUS_INVALID_DEVICE_REQUEST
;
2465 // Check for the correct security access.
2466 // The caller must have the SeTcbPrivilege.
2469 if (!SeSinglePrivilegeCheck( TcbPrivilege
, Irp
->RequestorMode
)) {
2471 CdCompleteRequest( IrpContext
, Irp
, STATUS_PRIVILEGE_NOT_HELD
);
2473 return STATUS_PRIVILEGE_NOT_HELD
;
2477 // Try to get a pointer to the device object from the handle passed in.
2480 #if defined(_WIN64) && BUILD_WOW64_ENABLED
2482 if (IoIs32bitProcess( Irp
)) {
2484 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof( UINT32
)) {
2486 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2487 return STATUS_INVALID_PARAMETER
;
2490 Handle
= (HANDLE
) LongToHandle( *((PUINT32
) Irp
->AssociatedIrp
.SystemBuffer
) );
2497 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof( HANDLE
)) {
2499 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
2500 return STATUS_INVALID_PARAMETER
;
2503 Handle
= *((PHANDLE
) Irp
->AssociatedIrp
.SystemBuffer
);
2506 Status
= ObReferenceObjectByHandle( Handle
,
2513 if (!NT_SUCCESS(Status
)) {
2515 CdCompleteRequest( IrpContext
, Irp
, Status
);
2520 // Grab the DeviceObject from the FileObject.
2523 DeviceToMarkBad
= FileToMarkBad
->DeviceObject
;
2526 // We only needed the device object involved, not a reference to the file.
2529 ObDereferenceObject( FileToMarkBad
);
2532 // Make sure this request can wait.
2535 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
2536 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_FORCE_POST
);
2539 // Synchronise with pnp/mount/verify paths.
2542 CdAcquireCdData( IrpContext
);
2545 // Nothing can go wrong now.
2549 // Now walk through all the mounted Vcb's looking for candidates to
2552 // On volumes we mark invalid, check for dismount possibility (which is
2553 // why we have to get the next link so early).
2556 Links
= CdData
.VcbQueue
.Flink
;
2558 while (Links
!= &CdData
.VcbQueue
) {
2560 Vcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
2562 Links
= Links
->Flink
;
2565 // If we get a match, mark the volume Bad, and also check to
2566 // see if the volume should go away.
2569 CdLockVcb( IrpContext
, Vcb
);
2571 if (Vcb
->Vpb
->RealDevice
== DeviceToMarkBad
) {
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
2579 IoAcquireVpbSpinLock( &SavedIrql
);
2581 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the vpb is allowed")
2582 if (DeviceToMarkBad
->Vpb
== Vcb
->Vpb
) {
2584 PVPB NewVpb
= Vcb
->SwapVpb
;
2586 NT_ASSERT( FlagOn( Vcb
->Vpb
->Flags
, VPB_MOUNTED
));
2587 NT_ASSERT( NULL
!= NewVpb
);
2589 RtlZeroMemory( NewVpb
, sizeof( VPB
) );
2591 NewVpb
->Type
= IO_TYPE_VPB
;
2592 NewVpb
->Size
= sizeof( VPB
);
2593 NewVpb
->RealDevice
= DeviceToMarkBad
;
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)
2601 Vcb
->SwapVpb
= NULL
;
2604 IoReleaseVpbSpinLock( SavedIrql
);
2606 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
2608 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
2611 CdUnlockVcb( IrpContext
, Vcb
);
2613 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
2615 CdPurgeVolume( IrpContext
, Vcb
, FALSE
);
2617 UnlockVcb
= CdCheckForDismount( IrpContext
, Vcb
, FALSE
);
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.
2624 __analysis_assert( UnlockVcb
== TRUE
);
2628 CdReleaseVcb( IrpContext
, Vcb
);
2633 CdUnlockVcb( IrpContext
, Vcb
);
2637 CdReleaseCdData( IrpContext
);
2639 CdCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
2640 return STATUS_SUCCESS
;
2645 // Local support routine
2649 CdAllowExtendedDasdIo (
2650 _Inout_ PIRP_CONTEXT IrpContext
,
2656 Routine Description:
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.
2664 Irp - Supplies the Irp to process
2668 NTSTATUS - The return status for the operation
2674 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2682 // Decode the file object, the only type of opens we accept are
2683 // user volume opens.
2686 if (CdDecodeFileObject( IrpContext
, IrpSp
->FileObject
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
2688 Status
= STATUS_INVALID_PARAMETER
;
2692 SetFlag( Ccb
->Flags
, CCB_FLAG_ALLOW_EXTENDED_DASD_IO
);
2693 Status
= STATUS_SUCCESS
;
2696 CdCompleteRequest( IrpContext
, Irp
, Status
);
2702 // Local support routine
2705 _Requires_lock_held_(_Global_critical_region_
)
2707 CdScanForDismountedVcb (
2708 _Inout_ PIRP_CONTEXT IrpContext
2713 Routine Description:
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.
2734 // Walk through all of the Vcb's attached to the global data.
2737 Links
= CdData
.VcbQueue
.Flink
;
2739 while (Links
!= &CdData
.VcbQueue
) {
2741 Vcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
2744 // Move to the next link now since the current Vcb may be deleted.
2747 Links
= Links
->Flink
;
2750 // If dismount is already underway then check if this Vcb can
2754 if ((Vcb
->VcbCondition
== VcbDismountInProgress
) ||
2755 (Vcb
->VcbCondition
== VcbInvalid
) ||
2756 ((Vcb
->VcbCondition
== VcbNotMounted
) && (Vcb
->VcbReference
<= CDFS_RESIDUAL_REFERENCE
))) {
2758 CdCheckForDismount( IrpContext
, Vcb
, FALSE
);
2767 // Local support routine
2769 _Success_(return != FALSE
)
2772 _In_ PIRP_CONTEXT IrpContext
,
2774 _Out_writes_bytes_(SECTOR_SIZE
) PCHAR RawIsoVd
,
2775 _In_ ULONG BlockFactor
,
2776 _In_ BOOLEAN ReturnOnError
,
2777 _In_ BOOLEAN VerifyVolume
2782 Routine Description:
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.
2791 Vcb - Pointer to the VCB for the volume.
2793 RawIsoVd - Pointer to a sector buffer which will contain the primary
2794 volume descriptor on exit, if successful.
2796 BlockFactor - Block factor used by the current device for the TableOfContents.
2798 ReturnOnError - Indicates that we should raise on I/O errors rather than
2799 returning a FALSE value.
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
2807 BOOLEAN - TRUE if a valid primary volume descriptor found, FALSE
2815 BOOLEAN FoundVd
= FALSE
;
2820 PCDROM_TOC_LARGE CdromToc
;
2827 // If there are no data tracks, don't even bother hunting for descriptors.
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.
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.
2841 if (FlagOn(Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
)) {
2847 // We will make at most two passes through the volume descriptor sequence.
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
2854 // On the second pass we will start offset from sector zero.
2857 while (!FoundVd
&& (ThisPass
<= 2)) {
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.
2866 if (ThisPass
== 1) {
2871 // Check for whether this device supports XA and multi-session.
2877 // Allocate a buffer for the last session information.
2880 CdromToc
= FsRtlAllocatePoolWithTag( CdPagedPool
,
2881 sizeof( CDROM_TOC_LARGE
),
2884 RtlZeroMemory( CdromToc
, sizeof( CDROM_TOC_LARGE
));
2887 // Query the last session information from the driver.
2890 Status
= CdPerformDevIoCtrl( IrpContext
,
2891 IOCTL_CDROM_GET_LAST_SESSION
,
2892 Vcb
->TargetDeviceObject
,
2894 sizeof( CDROM_TOC_LARGE
),
2900 // Raise an exception if there was an allocation failure.
2903 if (Status
== STATUS_INSUFFICIENT_RESOURCES
) {
2905 CdRaiseStatus( IrpContext
, Status
);
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.
2914 if (NT_SUCCESS( Status
) &&
2915 (CdromToc
->FirstTrack
!= CdromToc
->LastTrack
)) {
2923 // The track address is BigEndian, we need to flip the bytes.
2926 Source
= (PCHAR
) &CdromToc
->TrackData
[0].Address
[3];
2927 Dest
= (PCHAR
) &BaseSector
;
2931 *Dest
++ = *Source
--;
2936 // Now adjust the base sector by the block factor of the
2940 BaseSector
/= BlockFactor
;
2943 // Make this look like the second pass since we are only using the
2944 // first session. No reason to retry on error.
2954 if (CdromToc
!= NULL
) { CdFreePool( &CdromToc
); }
2959 // Compute the starting sector offset from the start of the session.
2962 SectorOffset
= FIRST_VD_SECTOR
;
2965 // Start by assuming we have neither Hsg or Iso volumes.
2971 // Loop until either error encountered, primary volume descriptor is
2972 // found or a terminal volume descriptor is found.
2978 // Attempt to read the desired sector. Exit directly if operation
2981 // If this is pass 1 we will ignore errors in read sectors and just
2982 // go to the next pass.
2985 if (!CdReadSectors( IrpContext
,
2986 LlBytesFromSectors( BaseSector
+ SectorOffset
),
2988 (BOOLEAN
) ((ThisPass
== 1) || ReturnOnError
),
2990 Vcb
->TargetDeviceObject
)) {
2996 // Check if either an ISO or HSG volume.
2999 if (RtlEqualMemory( CdIsoId
,
3000 CdRvdId( RawIsoVd
, VCB_STATE_ISO
),
3003 SetFlag( VolumeFlags
, VCB_STATE_ISO
);
3005 } else if (RtlEqualMemory( CdHsgId
,
3006 CdRvdId( RawIsoVd
, VCB_STATE_HSG
),
3009 SetFlag( VolumeFlags
, VCB_STATE_HSG
);
3012 // We have neither so break out of the loop.
3021 // Break out if the version number is incorrect or this is
3025 if ((CdRvdVersion( RawIsoVd
, VolumeFlags
) != VERSION_1
) ||
3026 (CdRvdDescType( RawIsoVd
, VolumeFlags
) == VD_TERMINATOR
)) {
3032 // If this is a primary volume descriptor then our search is over.
3035 if (CdRvdDescType( RawIsoVd
, VolumeFlags
) == VD_PRIMARY
) {
3038 // If we are not in the verify path then initialize the
3039 // fields in the Vcb with basic information from this
3043 if (!VerifyVolume
) {
3046 // Set the flag for the volume type.
3049 SetFlag( Vcb
->VcbState
, VolumeFlags
);
3052 // Store the base sector and sector offset for the
3053 // primary volume descriptor.
3056 Vcb
->BaseSector
= BaseSector
;
3057 Vcb
->VdSectorOffset
= SectorOffset
;
3058 Vcb
->PrimaryVdSectorOffset
= SectorOffset
;
3066 // Indicate that we're at the next sector.
3080 // Local support routine
3083 _Success_(return != FALSE
) BOOLEAN
3085 _In_ PIRP_CONTEXT IrpContext
,
3091 Routine Description:
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:
3097 If the new Vcb is a device only Mvcb and there is a previous
3100 Otherwise following conditions must be matched.
3102 1 - The 32 serial in the current VPB matches that in a previous
3105 2 - The volume label in the Vpb matches that in the previous
3108 3 - The system pointer to the real device object in the current
3109 VPB matches that in the same previous VPB.
3111 4 - Finally the previous Vcb cannot be invalid or have a dismount
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.
3117 Skip over the current Vcb.
3121 Vcb - This is the Vcb we are checking for a remount.
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
3129 BOOLEAN - TRUE if this is in fact a remount, FALSE otherwise.
3136 PVPB Vpb
= Vcb
->Vpb
;
3139 BOOLEAN Remount
= FALSE
;
3143 UNREFERENCED_PARAMETER( IrpContext
);
3146 // Check whether we are looking for a device only Mvcb.
3149 for (Link
= CdData
.VcbQueue
.Flink
;
3150 Link
!= &CdData
.VcbQueue
;
3151 Link
= Link
->Flink
) {
3153 *OldVcb
= CONTAINING_RECORD( Link
, VCB
, VcbLinks
);
3159 if (Vcb
== *OldVcb
) { continue; }
3162 // Look at the Vpb and state of the previous Vcb.
3165 OldVpb
= (*OldVcb
)->Vpb
;
3167 if ((OldVpb
!= Vpb
) &&
3168 (OldVpb
->RealDevice
== Vpb
->RealDevice
) &&
3169 ((*OldVcb
)->VcbCondition
== VcbNotMounted
)) {
3172 // If the current disk is a raw disk then it can match a previous music or
3176 if (FlagOn( Vcb
->VcbState
, VCB_STATE_AUDIO_DISK
)) {
3178 if (FlagOn( (*OldVcb
)->VcbState
, VCB_STATE_AUDIO_DISK
)) {
3181 // If we have both TOC then fail the remount if the lengths
3182 // are different or they don't match.
3185 if ((Vcb
->TocLength
!= (*OldVcb
)->TocLength
) ||
3186 ((Vcb
->TocLength
!= 0) &&
3187 !RtlEqualMemory( Vcb
->CdromToc
,
3188 (*OldVcb
)->CdromToc
,
3189 Vcb
->TocLength
))) {
3199 // The current disk is not a raw disk. Go ahead and compare
3200 // serial numbers, volume label and TOC.
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
,
3212 Vpb
->VolumeLabelLength
))) {
3214 // Remember the old Vcb. Then set the return value to
3229 // Local support routine
3233 CdFindActiveVolDescriptor (
3234 _In_ PIRP_CONTEXT IrpContext
,
3236 _Inout_updates_bytes_(ROUND_TO_PAGES( SECTOR_SIZE
)) PCHAR RawIsoVd
,
3237 _In_ BOOLEAN VerifyVolume
3242 Routine Description:
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.
3248 If we don't find the secondary descriptor then we will reread the primary.
3250 This routine will update the serial number and volume label in the Vpb.
3254 Vcb - This is the Vcb for the volume being mounted.
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
3260 VerifyVolume - indicates we are being called by the verify path, and should
3261 not modify the Vcb fields.
3270 BOOLEAN FoundSecondaryVd
= FALSE
;
3271 ULONG SectorOffset
= FIRST_VD_SECTOR
;
3280 // We only look for secondary volume descriptors on an Iso disk.
3283 if ((FlagOn( Vcb
->VcbState
, VCB_STATE_ISO
) || VerifyVolume
) && !CdNoJoliet
) {
3286 // Scan the volume descriptors from the beginning looking for a valid
3287 // secondary or a terminator.
3290 SectorOffset
= FIRST_VD_SECTOR
;
3295 // Read the next sector. We should never have an error in this
3299 CdReadSectors( IrpContext
,
3300 LlBytesFromSectors( Vcb
->BaseSector
+ SectorOffset
),
3304 Vcb
->TargetDeviceObject
);
3307 // Break out if the version number or standard Id is incorrect.
3308 // Also break out if this is a terminator.
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
)) {
3319 // We have a match if this is a secondary descriptor with a matching
3323 if ((CdRvdDescType( RawIsoVd
, VCB_STATE_JOLIET
) == VD_SECONDARY
) &&
3324 (RtlEqualMemory( CdRvdEsc( RawIsoVd
, VCB_STATE_JOLIET
),
3327 RtlEqualMemory( CdRvdEsc( RawIsoVd
, VCB_STATE_JOLIET
),
3330 RtlEqualMemory( CdRvdEsc( RawIsoVd
, VCB_STATE_JOLIET
),
3334 if (!VerifyVolume
) {
3337 // Update the Vcb with the new volume descriptor.
3340 ClearFlag( Vcb
->VcbState
, VCB_STATE_ISO
);
3341 SetFlag( Vcb
->VcbState
, VCB_STATE_JOLIET
);
3343 Vcb
->VdSectorOffset
= SectorOffset
;
3346 FoundSecondaryVd
= TRUE
;
3351 // Otherwise move on to the next sector.
3358 // If we didn't find the secondary then recover the original volume
3359 // descriptor stored in the second half of the RawIsoVd.
3362 if (!FoundSecondaryVd
) {
3364 RtlCopyMemory( RawIsoVd
,
3365 Add2Ptr( RawIsoVd
, SECTOR_SIZE
, PVOID
),
3371 // If we're in the verify path, our work is done, since we don't want
3372 // to update any Vcb/Vpb values.
3381 // Compute the serial number and volume label from the volume descriptor.
3384 Vcb
->Vpb
->SerialNumber
= CdSerial32( RawIsoVd
, SECTOR_SIZE
);
3387 // Make sure the CD label will fit in the Vpb.
3390 NT_ASSERT( VOLUME_ID_LENGTH
* sizeof( WCHAR
) <= MAXIMUM_VOLUME_LABEL_LENGTH
);
3393 // If this is not a Unicode label we must convert it to unicode.
3396 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
3399 // Convert the label to unicode. If we get any error then use a name
3403 Vcb
->Vpb
->VolumeLabelLength
= 0;
3405 if (NT_SUCCESS( RtlOemToUnicodeN( &Vcb
->Vpb
->VolumeLabel
[0],
3406 MAXIMUM_VOLUME_LABEL_LENGTH
,
3408 (PCH
)CdRvdVolId( RawIsoVd
, Vcb
->VcbState
),
3409 VOLUME_ID_LENGTH
))) {
3411 Vcb
->Vpb
->VolumeLabelLength
= (USHORT
) Length
;
3415 // We need to convert from big-endian to little endian.
3420 CdConvertBigToLittleEndian( IrpContext
,
3421 (PCHAR
) CdRvdVolId( RawIsoVd
, Vcb
->VcbState
),
3423 (PCHAR
) Vcb
->Vpb
->VolumeLabel
);
3425 Vcb
->Vpb
->VolumeLabelLength
= VOLUME_ID_LENGTH
* sizeof( WCHAR
);
3429 // Strip the trailing spaces or zeroes from the name.
3432 Index
= Vcb
->Vpb
->VolumeLabelLength
/ sizeof( WCHAR
);
3436 if ((Vcb
->Vpb
->VolumeLabel
[ Index
- 1 ] != L
'\0') &&
3437 (Vcb
->Vpb
->VolumeLabel
[ Index
- 1 ] != L
' ')) {
3446 // Now set the final length for the name.
3449 Vcb
->Vpb
->VolumeLabelLength
= (USHORT
) (Index
* sizeof( WCHAR
));