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