ReactOS 0.4.15-dev-8116-gf69e256
fsctrl.c
Go to the documentation of this file.
1/*++
2
3
4Copyright (c) 1989-2000 Microsoft Corporation
5
6Module Name:
7
8 FsCtrl.c
9
10Abstract:
11
12 This module implements the File System Control routines for Fat called
13 by the dispatch driver.
14
15
16--*/
17
18#include "fatprocs.h"
19
20//
21// The Bug check file id for this module
22//
23
24#define BugCheckFileId (FAT_BUG_CHECK_FSCTRL)
25
26//
27// The local debug trace level
28//
29
30#define Dbg (DEBUG_TRACE_FSCTRL)
31
32//
33// Local procedure prototypes
34//
35
36_Requires_lock_held_(_Global_critical_region_)
38FatMountVolume (
39 IN PIRP_CONTEXT IrpContext,
41 IN PVPB Vpb,
43 );
44
45_Requires_lock_held_(_Global_critical_region_)
47FatVerifyVolume (
48 IN PIRP_CONTEXT IrpContext,
49 IN PIRP Irp
50 );
51
54 IN PIRP_CONTEXT IrpContext,
56 );
57
58_Requires_lock_held_(_Global_critical_region_)
60FatUserFsCtrl (
61 IN PIRP_CONTEXT IrpContext,
62 IN PIRP Irp
63 );
64
65_Requires_lock_held_(_Global_critical_region_)
67FatOplockRequest (
68 _In_ PIRP_CONTEXT IrpContext,
70 );
71
72_Requires_lock_held_(_Global_critical_region_)
74FatLockVolume (
75 IN PIRP_CONTEXT IrpContext,
76 IN PIRP Irp
77 );
78
81 IN PIRP_CONTEXT IrpContext,
82 IN PIRP Irp
83 );
84
85_Requires_lock_held_(_Global_critical_region_)
87FatDismountVolume (
88 IN PIRP_CONTEXT IrpContext,
89 IN PIRP Irp
90 );
91
92_Requires_lock_held_(_Global_critical_region_)
94FatDirtyVolume (
95 IN PIRP_CONTEXT IrpContext,
96 IN PIRP Irp
97 );
98
101 IN PIRP_CONTEXT IrpContext,
102 IN PIRP Irp
103 );
104
107 IN PIRP_CONTEXT IrpContext,
108 IN PIRP Irp
109 );
110
113 IN PIRP_CONTEXT IrpContext,
114 IN PIRP Irp
115 );
116
117_Requires_lock_held_(_Global_critical_region_)
119FatInvalidateVolumes (
120 IN PIRP Irp
121 );
122
123_Requires_lock_held_(_Global_critical_region_)
124VOID
125FatScanForDismountedVcb (
126 IN PIRP_CONTEXT IrpContext
127 );
128
131 IN PIRP_CONTEXT IrpContext,
132 IN PVCB Vcb,
134 IN LBO Lbo,
135 IN ULONG NumberOfBytesToRead,
136 IN BOOLEAN ReturnOnError
137 );
138
139_Requires_lock_held_(_Global_critical_region_)
141FatQueryRetrievalPointers (
142 IN PIRP_CONTEXT IrpContext,
143 IN PIRP Irp
144 );
145
148 IN PIRP_CONTEXT IrpContext,
149 IN PIRP Irp
150 );
151
154 IN PIRP_CONTEXT IrpContext,
155 IN PIRP Irp
156 );
157
160 IN PIRP_CONTEXT IrpContext,
161 IN PIRP Irp
162 );
163
164_Requires_lock_held_(_Global_critical_region_)
166FatGetBootAreaInfo (
167 _In_ PIRP_CONTEXT IrpContext,
169 );
170
171_Requires_lock_held_(_Global_critical_region_)
173FatGetRetrievalPointerBase (
174 _In_ PIRP_CONTEXT IrpContext,
176 );
177
178_Requires_lock_held_(_Global_critical_region_)
180FatMarkHandle(
181 _In_ PIRP_CONTEXT IrpContext,
183 );
184
187 __in PIRP_CONTEXT IrpContext,
189 );
190
191_Requires_lock_held_(_Global_critical_region_)
193FatSetPurgeFailureMode (
194 _In_ PIRP_CONTEXT IrpContext,
196 );
197
198//
199// Local support routine prototypes
200//
201
202_Requires_lock_held_(_Global_critical_region_)
204FatGetVolumeBitmap (
205 IN PIRP_CONTEXT IrpContext,
206 IN PIRP Irp
207 );
208
209_Requires_lock_held_(_Global_critical_region_)
211FatGetRetrievalPointers (
212 IN PIRP_CONTEXT IrpContext,
213 IN PIRP Irp
214 );
215
216_Requires_lock_held_(_Global_critical_region_)
217VOID
218FatMoveFileNeedsWriteThrough (
219 _In_ PIRP_CONTEXT IrpContext,
221 _In_ ULONG OldWriteThroughFlags
222 );
223
224_Requires_lock_held_(_Global_critical_region_)
226FatMoveFile (
227 IN PIRP_CONTEXT IrpContext,
228 IN PIRP Irp
229 );
230
231VOID
233 PIRP_CONTEXT IrpContext,
236 ULONG TargetCluster,
237 ULONG BytesToReallocate,
238 PULONG FirstSpliceSourceCluster,
239 PULONG FirstSpliceTargetCluster,
240 PULONG SecondSpliceSourceCluster,
241 PULONG SecondSpliceTargetCluster,
242 PLARGE_MCB SourceMcb
243);
244
245_Requires_lock_held_(_Global_critical_region_)
246VOID
247FatComputeMoveFileParameter (
248 IN PIRP_CONTEXT IrpContext,
253 OUT PULONG BytesToReallocate,
254 OUT PULONG BytesToWrite,
255 OUT PLARGE_INTEGER SourceLbo
256);
257
260 IN PIRP_CONTEXT IrpContext,
261 IN PVPB Vpb,
263 IN ULONG Size,
264 OUT PBOOLEAN LabelFound
265);
266
267VOID
269 IN PIRP_CONTEXT IrpContext,
270 IN PVCB Vcb,
273 );
274
275#ifdef ALLOC_PRAGMA
276#pragma alloc_text(PAGE, FatAddMcbEntry)
277#pragma alloc_text(PAGE, FatAllowExtendedDasdIo)
278#pragma alloc_text(PAGE, FatCommonFileSystemControl)
279#pragma alloc_text(PAGE, FatComputeMoveFileParameter)
280#pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints)
281#pragma alloc_text(PAGE, FatDirtyVolume)
282#pragma alloc_text(PAGE, FatFsdFileSystemControl)
283#pragma alloc_text(PAGE, FatGetRetrievalPointerBase)
284#pragma alloc_text(PAGE, FatGetBootAreaInfo)
285#pragma alloc_text(PAGE, FatMarkHandle)
286#pragma alloc_text(PAGE, FatGetRetrievalPointers)
287#pragma alloc_text(PAGE, FatGetStatistics)
288#pragma alloc_text(PAGE, FatGetVolumeBitmap)
289#pragma alloc_text(PAGE, FatIsMediaWriteProtected)
290#pragma alloc_text(PAGE, FatIsPathnameValid)
291#pragma alloc_text(PAGE, FatIsVolumeDirty)
292#pragma alloc_text(PAGE, FatIsVolumeMounted)
293#pragma alloc_text(PAGE, FatLockVolume)
294#pragma alloc_text(PAGE, FatLookupLastMcbEntry)
295#pragma alloc_text(PAGE, FatGetNextMcbEntry)
296#pragma alloc_text(PAGE, FatMountVolume)
297#pragma alloc_text(PAGE, FatMoveFileNeedsWriteThrough)
298#pragma alloc_text(PAGE, FatMoveFile)
299#pragma alloc_text(PAGE, FatOplockRequest)
300#pragma alloc_text(PAGE, FatPerformVerifyDiskRead)
301#pragma alloc_text(PAGE, FatQueryBpb)
302#pragma alloc_text(PAGE, FatQueryRetrievalPointers)
303#pragma alloc_text(PAGE, FatRemoveMcbEntry)
304#pragma alloc_text(PAGE, FatScanForDismountedVcb)
305#pragma alloc_text(PAGE, FatFlushAndCleanVolume)
306#pragma alloc_text(PAGE, FatSearchBufferForLabel)
307#pragma alloc_text(PAGE, FatSetPurgeFailureMode)
308#pragma alloc_text(PAGE, FatUnlockVolume)
309#pragma alloc_text(PAGE, FatUserFsCtrl)
310#pragma alloc_text(PAGE, FatVerifyLookupFatEntry)
311#pragma alloc_text(PAGE, FatVerifyVolume)
312#endif
313
314#if DBG
315
316BOOLEAN FatMoveFileDebug = 0;
317
318#endif
319
320//
321// These wrappers go around the MCB package; we scale the LBO's passed
322// in (which can be bigger than 32 bits on fat32) by the volume's sector
323// size.
324//
325// Note we now use the real large mcb package. This means these shims
326// now also convert the -1 unused LBN number to the 0 of the original
327// mcb package.
328//
329
330#define MCB_SCALE_LOG2 (Vcb->AllocationSupport.LogOfBytesPerSector)
331#define MCB_SCALE (1 << MCB_SCALE_LOG2)
332#define MCB_SCALE_MODULO (MCB_SCALE - 1)
333
335FatNonSparseMcb(
336 _In_ PVCB Vcb,
338 _Out_ PVBO Vbo,
340 )
341{
342 LBO Lbo;
343 ULONG Index = 0;
344 LONGLONG llVbo = 0;
345
347
348 while (FsRtlGetNextLargeMcbEntry(Mcb, Index, &llVbo, &Lbo, ByteCount)) {
349 *Vbo = (VBO)llVbo;
350 if (((ULONG)Lbo) == -1) {
351 return FALSE;
352 }
353
354 Index++;
355 }
356
357 *Vbo = (VBO)llVbo;
358
359 return TRUE;
360}
361
362
365 IN PVCB Vcb,
367 IN VBO Vbo,
368 IN LBO Lbo,
370 )
371
372{
374#if DBG
375 VBO SparseVbo;
376 LONGLONG SparseByteCount;
377#endif
378
379 PAGED_CODE();
380
381 if (SectorCount) {
382
383 //
384 // Round up sectors, but be careful as SectorCount approaches 4Gb.
385 // Note that for x>0, (x+m-1)/m = ((x-1)/m)+(m/m) = ((x-1)/m)+1
386 //
387
388 SectorCount--;
390 SectorCount++;
391 }
392
395
396 NT_ASSERT( SectorCount != 0 );
397
398 if (Mcb != &Vcb->DirtyFatMcb) {
399 NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) ||
400 ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) );
401 }
402
404 ((LONGLONG) Vbo),
405 ((LONGLONG) Lbo),
406 ((LONGLONG) SectorCount) );
407
408 if (Mcb != &Vcb->DirtyFatMcb) {
409 NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) ||
410 ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) );
411 }
412
413 return Result;
414}
415
416
419 IN PVCB Vcb,
421 IN VBO Vbo,
422 OUT PLBO Lbo,
425 )
426{
427 BOOLEAN Results;
428 LONGLONG LiLbo;
429 LONGLONG LiSectorCount;
431
432 LiLbo = 0;
433 LiSectorCount = 0;
434
436
437 Results = FsRtlLookupLargeMcbEntry( Mcb,
438 (Vbo >> MCB_SCALE_LOG2),
439 &LiLbo,
440 ARGUMENT_PRESENT(ByteCount) ? &LiSectorCount : NULL,
441 NULL,
442 NULL,
443 Index );
444
445 if ((ULONG) LiLbo != -1) {
446
447 *Lbo = (((LBO) LiLbo) << MCB_SCALE_LOG2);
448
449 if (Results) {
450
451 *Lbo += Remainder;
452 }
453
454 } else {
455
456 *Lbo = 0;
457 }
458
460
461 *ByteCount = (ULONG) LiSectorCount;
462
463 if (*ByteCount) {
464
466
467 //
468 // If ByteCount overflows, then this is likely the case of
469 // a file of max-supported size (4GiB - 1), allocated in a
470 // single continuous run.
471 //
472
473 if (*ByteCount == 0) {
474
475 *ByteCount = 0xFFFFFFFF;
476 }
477
478 if (Results) {
479
481 }
482 }
483
484 }
485
486 return Results;
487}
488
489//
490// NOTE: Vbo/Lbn undefined if MCB is empty & return code false.
491//
492
495 IN PVCB Vcb,
497 OUT PVBO Vbo,
498 OUT PLBO Lbo,
500 )
501
502{
503 BOOLEAN Results;
504 LONGLONG LiVbo;
505 LONGLONG LiLbo;
506 ULONG LocalIndex;
507
508 PAGED_CODE();
509
510 LiVbo = LiLbo = 0;
511 LocalIndex = 0;
512
514 &LiVbo,
515 &LiLbo,
516 &LocalIndex );
517
518 *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
519
520 if (((ULONG) LiLbo) != -1) {
521
522 *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
523
524 *Lbo += (MCB_SCALE - 1);
525 *Vbo += (MCB_SCALE - 1);
526
527 } else {
528
529 *Lbo = 0;
530 }
531
532 if (Index) {
533 *Index = LocalIndex;
534 }
535
536 return Results;
537}
538
539
542 IN PVCB Vcb,
545 OUT PVBO Vbo,
546 OUT PLBO Lbo,
548 )
549
550{
551 BOOLEAN Results;
552 LONGLONG LiVbo;
553 LONGLONG LiLbo;
554 LONGLONG LiSectorCount;
555
556 PAGED_CODE();
557
558 LiVbo = LiLbo = 0;
559
561 RunIndex,
562 &LiVbo,
563 &LiLbo,
564 &LiSectorCount );
565
566 if (Results) {
567
568 *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
569
570 if (((ULONG) LiLbo) != -1) {
571
572 *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
573
574 } else {
575
576 *Lbo = 0;
577 }
578
579 *ByteCount = ((ULONG) LiSectorCount) << MCB_SCALE_LOG2;
580
581 if ((*ByteCount == 0) && (LiSectorCount != 0)) {
582
583 //
584 // If 'ByteCount' overflows, then this is likely a file of
585 // max supported size (2^32 - 1) in one contiguous run.
586 //
587
588 NT_ASSERT( RunIndex == 0 );
589
590 *ByteCount = 0xFFFFFFFF;
591 }
592 }
593
594 return Results;
595}
596
597
598VOID
600 IN PVCB Vcb,
602 IN VBO Vbo,
604 )
605{
606 PAGED_CODE();
607
608 if ((SectorCount) && (SectorCount != 0xFFFFFFFF)) {
609
610 SectorCount--;
612 SectorCount++;
613 }
614
616
617#if DBG
618 _SEH2_TRY {
619#endif
620
622 (LONGLONG) Vbo,
624
625#if DBG
626 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
627
628 NOTHING;
629 } _SEH2_END;
630#endif
631
632}
633
634
638NTAPI
639FatFsdFileSystemControl (
640 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
642 )
643
644/*++
645
646Routine Description:
647
648 This routine implements the FSD part of FileSystem control operations
649
650Arguments:
651
652 VolumeDeviceObject - Supplies the volume device object where the
653 file exists
654
655 Irp - Supplies the Irp being processed
656
657Return Value:
658
659 NTSTATUS - The FSD status for the IRP
660
661--*/
662
663{
666 PIRP_CONTEXT IrpContext = NULL;
667
669
670 PAGED_CODE();
671 UNREFERENCED_PARAMETER( VolumeDeviceObject );
672
673 DebugTrace(+1, Dbg,"FatFsdFileSystemControl\n", 0);
674
675 //
676 // Call the common FileSystem Control routine, with blocking allowed if
677 // synchronous. This opeation needs to special case the mount
678 // and verify suboperations because we know they are allowed to block.
679 // We identify these suboperations by looking at the file object field
680 // and seeing if its null.
681 //
682
684
685 Wait = TRUE;
686
687 } else {
688
689 Wait = CanFsdWait( Irp );
690 }
691
693
695
696 _SEH2_TRY {
697
699
701
702 //
703 // We need to made a special check here for the InvalidateVolumes
704 // FSCTL as that comes in with a FileSystem device object instead
705 // of a volume device object.
706 //
707
711 (IrpSp->Parameters.FileSystemControl.FsControlCode ==
713
714 Status = FatInvalidateVolumes( Irp );
715
716 } else {
717
718 IrpContext = FatCreateIrpContext( Irp, Wait );
719
720 Status = FatCommonFileSystemControl( IrpContext, Irp );
721 }
722
724
725 //
726 // We had some trouble trying to perform the requested
727 // operation, so we'll abort the I/O request with
728 // the error status that we get back from the
729 // execption code
730 //
731
732 Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
733 } _SEH2_END;
734
735 if (TopLevel) { IoSetTopLevelIrp( NULL ); }
736
738
739 //
740 // And return to our caller
741 //
742
743 DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status);
744
745 return Status;
746}
747
748
749_Requires_lock_held_(_Global_critical_region_)
751FatCommonFileSystemControl (
752 IN PIRP_CONTEXT IrpContext,
753 IN PIRP Irp
754 )
755
756/*++
757
758Routine Description:
759
760 This is the common routine for doing FileSystem control operations called
761 by both the fsd and fsp threads
762
763Arguments:
764
765 Irp - Supplies the Irp to process
766
767Return Value:
768
769 NTSTATUS - The return status for the operation
770
771--*/
772
773{
776
777 PAGED_CODE();
778
779 //
780 // Get a pointer to the current Irp stack location
781 //
782
784
785 DebugTrace(+1, Dbg,"FatCommonFileSystemControl\n", 0);
786 DebugTrace( 0, Dbg,"Irp = %p\n", Irp);
787 DebugTrace( 0, Dbg,"MinorFunction = %08lx\n", IrpSp->MinorFunction);
788
789 //
790 // We know this is a file system control so we'll case on the
791 // minor function, and call a internal worker routine to complete
792 // the irp.
793 //
794
795 switch (IrpSp->MinorFunction) {
796
798
799 Status = FatUserFsCtrl( IrpContext, Irp );
800 break;
801
803
804 Status = FatMountVolume( IrpContext,
805 IrpSp->Parameters.MountVolume.DeviceObject,
808
809 //
810 // Complete the request.
811 //
812 // We do this here because FatMountVolume can be called recursively,
813 // but the Irp is only to be completed once.
814 //
815 // NOTE: I don't think this is true anymore (danlo 3/15/1999). Probably
816 // an artifact of the old doublespace attempt.
817 //
818
819 FatCompleteRequest( IrpContext, Irp, Status );
820 break;
821
823
824 Status = FatVerifyVolume( IrpContext, Irp );
825 break;
826
827 default:
828
829 DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
830
833 break;
834 }
835
836 DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status);
837
838 return Status;
839}
840
841
842//
843// Local Support Routine
844//
845
846_Requires_lock_held_(_Global_critical_region_)
848FatMountVolume (
849 IN PIRP_CONTEXT IrpContext,
851 IN PVPB Vpb,
853 )
854
855/*++
856
857Routine Description:
858
859 This routine performs the mount volume operation. It is responsible for
860 either completing of enqueuing the input Irp.
861
862 Its job is to verify that the volume denoted in the IRP is a Fat volume,
863 and create the VCB and root DCB structures. The algorithm it uses is
864 essentially as follows:
865
866 1. Create a new Vcb Structure, and initialize it enough to do cached
867 volume file I/O.
868
869 2. Read the disk and check if it is a Fat volume.
870
871 3. If it is not a Fat volume then free the cached volume file, delete
872 the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME
873
874 4. Check if the volume was previously mounted and if it was then do a
875 remount operation. This involves reinitializing the cached volume
876 file, checking the dirty bit, resetting up the allocation support,
877 deleting the VCB, hooking in the old VCB, and completing the IRP.
878
879 5. Otherwise create a root DCB, create Fsp threads as necessary, and
880 complete the IRP.
881
882Arguments:
883
884 TargetDeviceObject - This is where we send all of our requests.
885
886 Vpb - This gives us additional information needed to complete the mount.
887
888Return Value:
889
890 NTSTATUS - The return status for the operation
891
892--*/
893
894{
895 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
897
898 PBCB BootBcb;
900
901 PBCB DirentBcb;
904
905 BOOLEAN MountNewVolume = FALSE;
906 BOOLEAN WeClearedVerifyRequiredBit = FALSE;
907 BOOLEAN DoARemount = FALSE;
908
909 PVCB OldVcb = NULL;
910 PVPB OldVpb = NULL;
911
912 PDEVICE_OBJECT RealDevice = NULL;
914 PVCB Vcb = NULL;
915 PFILE_OBJECT RootDirectoryFile = NULL;
916
917 PLIST_ENTRY Links;
918
919 IO_STATUS_BLOCK Iosb = {0};
920 ULONG ChangeCount = 0;
921
922 DISK_GEOMETRY Geometry;
923
924 PARTITION_INFORMATION_EX PartitionInformation;
925 NTSTATUS StatusPartInfo;
926
927#if (NTDDI_VERSION > NTDDI_WIN8)
928 GUID VolumeGuid = {0};
929#endif
930
931
932 PAGED_CODE();
933
934 DebugTrace(+1, Dbg, "FatMountVolume\n", 0);
935 DebugTrace( 0, Dbg, "TargetDeviceObject = %p\n", TargetDeviceObject);
936 DebugTrace( 0, Dbg, "Vpb = %p\n", Vpb);
937
938 NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
940
941 //
942 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
943 //
944
945 if (FlagOn(TargetDeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
946
947 //
948 // Verify that there is a disk here and pick up the change count.
949 //
950
951 Status = FatPerformDevIoCtrl( IrpContext,
954 NULL,
955 0,
956 &ChangeCount,
957 sizeof(ULONG),
958 FALSE,
959 TRUE,
960 &Iosb );
961
962 if (!NT_SUCCESS( Status )) {
963
964 //
965 // If we will allow a raw mount then avoid sending the popup.
966 //
967 // Only send this on "true" disk devices to handle the accidental
968 // legacy of FAT. No other FS will throw a harderror on empty
969 // drives.
970 //
971 // Cmd should really handle this per 9x.
972 //
973
975 Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK) {
976
977 FatNormalizeAndRaiseStatus( IrpContext, Status );
978 }
979
980 return Status;
981 }
982
983 }
984
985 if (Iosb.Information != sizeof(ULONG)) {
986
987 //
988 // Be safe about the count in case the driver didn't fill it in
989 //
990
991 ChangeCount = 0;
992 }
993
994 //
995 // If this is a CD class device, then check to see if there is a
996 // 'data track' or not. This is to avoid issuing paging reads which will
997 // fail later in the mount process (e.g. CD-DA or blank CD media)
998 //
999
1000 if ((TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
1001 !FatScanForDataTrack( IrpContext, TargetDeviceObject)) {
1002
1004 }
1005
1006 //
1007 // Ping the volume with a partition query and pick up the partition
1008 // type. We'll check this later to avoid some scurrilous volumes.
1009 //
1010
1011 StatusPartInfo = FatPerformDevIoCtrl( IrpContext,
1014 NULL,
1015 0,
1016 &PartitionInformation,
1018 FALSE,
1019 TRUE,
1020 &Iosb );
1021
1022 //
1023 // Make sure we can wait.
1024 //
1025
1026 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
1027
1028 //
1029 // Do a quick check to see if there any Vcb's which can be removed.
1030 //
1031
1032 FatScanForDismountedVcb( IrpContext );
1033
1034 //
1035 // Initialize the Bcbs and our final state so that the termination
1036 // handlers will know what to free or unpin
1037 //
1038
1039 BootBcb = NULL;
1040 DirentBcb = NULL;
1041
1042 Vcb = NULL;
1043 VolDo = NULL;
1044 MountNewVolume = FALSE;
1045
1046 _SEH2_TRY {
1047
1048 //
1049 // Synchronize with FatCheckForDismount(), which modifies the vpb.
1050 //
1051
1052#ifdef _MSC_VER
1053#pragma prefast( push )
1054#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1055#pragma prefast( disable: 28193, "this will always wait" )
1056#endif
1057
1058 (VOID)FatAcquireExclusiveGlobal( IrpContext );
1059
1060#ifdef _MSC_VER
1061#pragma prefast( pop )
1062#endif
1063
1064 //
1065 // Create a new volume device object. This will have the Vcb
1066 // hanging off of its end, and set its alignment requirement
1067 // from the device we talk to.
1068 //
1069
1071 sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
1072 NULL,
1074 0,
1075 FALSE,
1076 (PDEVICE_OBJECT *)&VolDo))) {
1077
1078 try_return( Status );
1079 }
1080
1081 //
1082 // Our alignment requirement is the larger of the processor alignment requirement
1083 // already in the volume device object and that in the TargetDeviceObject
1084 //
1085
1086 if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
1087
1088 VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement;
1089 }
1090
1091 //
1092 // Initialize the overflow queue for the volume
1093 //
1094
1095 VolDo->OverflowQueueCount = 0;
1096 InitializeListHead( &VolDo->OverflowQueue );
1097
1098 VolDo->PostedRequestCount = 0;
1099 KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock );
1100
1101 //
1102 // We must initialize the stack size in our device object before
1103 // the following reads, because the I/O system has not done it yet.
1104 // This must be done before we clear the device initializing flag
1105 // otherwise a filter could attach and copy the wrong stack size into
1106 // it's device object.
1107 //
1108
1109 VolDo->DeviceObject.StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1);
1110
1111 //
1112 // We must also set the sector size correctly in our device object
1113 // before clearing the device initializing flag.
1114 //
1115
1116 Status = FatPerformDevIoCtrl( IrpContext,
1119 NULL,
1120 0,
1121 &Geometry,
1122 sizeof( DISK_GEOMETRY ),
1123 FALSE,
1124 TRUE,
1125 NULL );
1126
1127 if (!NT_SUCCESS( Status )) {
1128
1129 try_return( Status );
1130 }
1131
1132#ifdef _MSC_VER
1133#pragma prefast( suppress: 28175, "this is a filesystem driver, touching SectorSize is fine" )
1134#endif
1135 VolDo->DeviceObject.SectorSize = (USHORT)Geometry.BytesPerSector;
1136
1137 //
1138 // Indicate that this device object is now completely initialized
1139 //
1140
1141 ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING);
1142
1143 //
1144 // Now Before we can initialize the Vcb we need to set up the device
1145 // object field in the Vpb to point to our new volume device object.
1146 // This is needed when we create the virtual volume file's file object
1147 // in initialize vcb.
1148 //
1149
1150 Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
1151
1152 //
1153 // If the real device needs verification, temporarily clear the
1154 // field.
1155 //
1156
1157 RealDevice = Vpb->RealDevice;
1158
1159 if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) {
1160
1161 ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
1162
1163 WeClearedVerifyRequiredBit = TRUE;
1164 }
1165
1166 //
1167 // Initialize the new vcb
1168 //
1169
1170 FatInitializeVcb( IrpContext,
1171 &VolDo->Vcb,
1173 Vpb,
1175 //
1176 // Get a reference to the Vcb hanging off the end of the device object
1177 //
1178
1179 Vcb = &VolDo->Vcb;
1180
1181 //
1182 // Read in the boot sector, and have the read be the minumum size
1183 // needed. We know we can wait.
1184 //
1185
1186 //
1187 // We need to commute errors on CD so that CDFS will get its crack. Audio
1188 // and even data media may not be universally readable on sector zero.
1189 //
1190
1191 _SEH2_TRY {
1192
1193 FatReadVolumeFile( IrpContext,
1194 Vcb,
1195 0, // Starting Byte
1196 sizeof(PACKED_BOOT_SECTOR),
1197 &BootBcb,
1198 (PVOID *)&BootSector );
1199
1200 } _SEH2_EXCEPT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ?
1202
1203 NOTHING;
1204 } _SEH2_END;
1205
1206 //
1207 // Call a routine to check the boot sector to see if it is fat
1208 //
1209
1210 if (BootBcb == NULL || !FatIsBootSectorFat( BootSector)) {
1211
1212 DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
1213
1214 //
1215 // Complete the request and return to our caller
1216 //
1217
1219 }
1220
1221#if (NTDDI_VERSION > NTDDI_WIN8)
1222 //
1223 // Initialize the volume guid.
1224 //
1225
1226 if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb->TargetDeviceObject, &VolumeGuid ))) {
1227
1228
1229 //
1230 // Stash a copy away in the VCB.
1231 //
1232
1233 RtlCopyMemory( &Vcb->VolumeGuid, &VolumeGuid, sizeof(GUID));
1234
1235 }
1236
1237
1238 //
1239 // Stash away a copy of the volume GUID path in our VCB.
1240 //
1241
1242 if (Vcb->VolumeGuidPath.Buffer) {
1243 ExFreePool( Vcb->VolumeGuidPath.Buffer );
1244 Vcb->VolumeGuidPath.Buffer = NULL;
1245 Vcb->VolumeGuidPath.Length = Vcb->VolumeGuidPath.MaximumLength = 0;
1246 }
1247
1248 IoVolumeDeviceToGuidPath( Vcb->TargetDeviceObject, &Vcb->VolumeGuidPath );
1249#endif
1250
1251 //
1252 // Unpack the BPB. We used to do some sanity checking of the FATs at
1253 // this point, but authoring errors on third-party devices prevent
1254 // us from continuing to safeguard ourselves. We can only hope the
1255 // boot sector check is good enough.
1256 //
1257 // (read: digital cameras)
1258 //
1259 // Win9x does the same.
1260 //
1261
1262 FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb );
1263
1264 //
1265 // Check if we have an OS/2 Boot Manager partition and treat it as an
1266 // unknown file system. We'll check the partition type in from the
1267 // partition table and we ensure that it has less than 0x80 sectors,
1268 // which is just a heuristic that will capture all real OS/2 BM partitions
1269 // and avoid the chance we'll discover partitions which erroneously
1270 // (but to this point, harmlessly) put down the OS/2 BM type.
1271 //
1272 // Note that this is only conceivable on good old MBR media.
1273 //
1274 // The OS/2 Boot Manager boot format mimics a FAT16 partition in sector
1275 // zero but does is not a real FAT16 file system. For example, the boot
1276 // sector indicates it has 2 FATs but only really has one, with the boot
1277 // manager code overlaying the second FAT. If we then set clean bits in
1278 // FAT[0] we'll corrupt that code.
1279 //
1280
1281 if (NT_SUCCESS( StatusPartInfo ) &&
1282 (PartitionInformation.PartitionStyle == PARTITION_STYLE_MBR &&
1283 PartitionInformation.Mbr.PartitionType == PARTITION_OS2BOOTMGR) &&
1284 (Vcb->Bpb.Sectors != 0 &&
1285 Vcb->Bpb.Sectors < 0x80)) {
1286
1287 DebugTrace( 0, Dbg, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 );
1288
1289 //
1290 // Complete the request and return to our caller
1291 //
1292
1294 }
1295
1296 //
1297 // Verify that the sector size recorded in the Bpb matches what the
1298 // device currently reports it's sector size to be.
1299 //
1300
1301 if ( !NT_SUCCESS( Status) ||
1302 (Geometry.BytesPerSector != Vcb->Bpb.BytesPerSector)) {
1303
1305 }
1306
1307 //
1308 // This is a fat volume, so extract the bpb, serial number. The
1309 // label we'll get later after we've created the root dcb.
1310 //
1311 // Note that the way data caching is done, we set neither the
1312 // direct I/O or Buffered I/O bit in the device object flags.
1313 //
1314
1315 if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; }
1316
1317 if (IsBpbFat32(&BootSector->PackedBpb)) {
1318
1319 CopyUchar4( &Vpb->SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id );
1320
1321 } else {
1322
1323 CopyUchar4( &Vpb->SerialNumber, BootSector->Id );
1324
1325 //
1326 // Allocate space for the stashed boot sector chunk. This only has meaning on
1327 // FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and
1328 // its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24
1329 // bytes.
1330 //
1331
1332 Vcb->First0x24BytesOfBootSector =
1334 0x24,
1336
1337 //
1338 // Stash a copy of the first 0x24 bytes
1339 //
1340
1341 RtlCopyMemory( Vcb->First0x24BytesOfBootSector,
1342 BootSector,
1343 0x24 );
1344 }
1345
1346 //
1347 // Now unpin the boot sector, so when we set up allocation eveything
1348 // works.
1349 //
1350
1351 FatUnpinBcb( IrpContext, BootBcb );
1352
1353 //
1354 // Compute a number of fields for Vcb.AllocationSupport
1355 //
1356
1357 FatSetupAllocationSupport( IrpContext, Vcb );
1358
1359 //
1360 // Sanity check the FsInfo information for FAT32 volumes. Silently deal
1361 // with messed up information by effectively disabling FsInfo updates.
1362 //
1363
1364 if (FatIsFat32( Vcb )) {
1365
1366 if (Vcb->Bpb.FsInfoSector >= Vcb->Bpb.ReservedSectors) {
1367
1368 Vcb->Bpb.FsInfoSector = 0;
1369 }
1370 }
1371
1372
1373 //
1374 // Create a root Dcb so we can read in the volume label. If this is FAT32, we can
1375 // discover corruption in the FAT chain.
1376 //
1377 // NOTE: this exception handler presumes that this is the only spot where we can
1378 // discover corruption in the mount process. If this ever changes, this handler
1379 // MUST be expanded. The reason we have this guy here is because we have to rip
1380 // the structures down now (in the finally below) and can't wait for the outer
1381 // exception handling to do it for us, at which point everything will have vanished.
1382 //
1383
1384 _SEH2_TRY {
1385
1386 FatCreateRootDcb( IrpContext, Vcb );
1387
1390
1391 //
1392 // The volume needs to be dirtied, do it now. Note that at this point we have built
1393 // enough of the Vcb to pull this off.
1394 //
1395
1396 FatCheckDirtyBit( IrpContext,
1397 Vcb );
1398
1399 //
1400 // Set the dirty bit if it is not set already
1401 //
1402
1403 if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
1404
1406 FatMarkVolume( IrpContext, Vcb, VolumeDirty );
1408 }
1409
1410 //
1411 // Now keep bailing out ...
1412 //
1413
1415 } _SEH2_END;
1416
1417 FatLocateVolumeLabel( IrpContext,
1418 Vcb,
1419 &Dirent,
1420 &DirentBcb,
1421 (PVBO)&ByteOffset );
1422
1423 if (Dirent != NULL) {
1424
1425 UCHAR OemBuffer[11];
1428
1429 OemString.Buffer = (PCHAR)&OemBuffer[0];
1430 OemString.MaximumLength = 11;
1431
1432 RtlCopyMemory( OemString.Buffer, Dirent->FileName, 11 );
1433
1434 //
1435 // Translate the first character from 0x5 to 0xe5.
1436 //
1437
1438 if (OemString.Buffer[0] == FAT_DIRENT_REALLY_0E5) {
1439
1440 OemString.Buffer[0] = 0xe5;
1441 }
1442
1443 //
1444 // Compute the length of the volume name
1445 //
1446
1447 for ( OemString.Length = 11;
1448 OemString.Length > 0;
1449 OemString.Length -= 1) {
1450
1451 if ( (OemString.Buffer[OemString.Length-1] != 0x00) &&
1452 (OemString.Buffer[OemString.Length-1] != 0x20) ) { break; }
1453 }
1454
1456 UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0];
1457
1459 &OemString,
1460 FALSE );
1461
1462 if ( !NT_SUCCESS( Status ) ) {
1463
1464 try_return( Status );
1465 }
1466
1467 Vpb->VolumeLabelLength = UnicodeString.Length;
1468
1469 } else {
1470
1471 Vpb->VolumeLabelLength = 0;
1472 }
1473
1474 //
1475 // Use the change count we noted initially *before* doing any work.
1476 // If something came along in the midst of this operation, we'll
1477 // verify and discover the problem.
1478 //
1479
1480 Vcb->ChangeCount = ChangeCount;
1481
1482 //
1483 // Now scan the list of previously mounted volumes and compare
1484 // serial numbers and volume labels off not currently mounted
1485 // volumes to see if we have a match.
1486 //
1487
1488 for (Links = FatData.VcbQueue.Flink;
1489 Links != &FatData.VcbQueue;
1490 Links = Links->Flink) {
1491
1492 OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
1493 OldVpb = OldVcb->Vpb;
1494
1495 //
1496 // Skip over ourselves since we're already in the VcbQueue
1497 //
1498
1499 if (OldVpb == Vpb) { continue; }
1500
1501 //
1502 // Check for a match:
1503 //
1504 // Serial Number, VolumeLabel and Bpb must all be the same.
1505 // Also the volume must have failed a verify before (ie.
1506 // VolumeNotMounted), and it must be in the same physical
1507 // drive than it was mounted in before.
1508 //
1509
1510 if ( (OldVpb->SerialNumber == Vpb->SerialNumber) &&
1511 (OldVcb->VcbCondition == VcbNotMounted) &&
1512 (OldVpb->RealDevice == RealDevice) &&
1513 (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) &&
1514 (RtlEqualMemory(&OldVpb->VolumeLabel[0],
1515 &Vpb->VolumeLabel[0],
1516 Vpb->VolumeLabelLength)) &&
1517 (RtlEqualMemory(&OldVcb->Bpb,
1518 &Vcb->Bpb,
1519 IsBpbFat32(&Vcb->Bpb) ?
1520 sizeof(BIOS_PARAMETER_BLOCK) :
1522 LargeSectorsPerFat) ))) {
1523
1524 DoARemount = TRUE;
1525
1526 break;
1527 }
1528 }
1529
1530 if ( DoARemount ) {
1531
1532 PVPB *IrpVpb;
1533
1534 DebugTrace(0, Dbg, "Doing a remount\n", 0);
1535 DebugTrace(0, Dbg, "Vcb = %p\n", Vcb);
1536 DebugTrace(0, Dbg, "Vpb = %p\n", Vpb);
1537 DebugTrace(0, Dbg, "OldVcb = %p\n", OldVcb);
1538 DebugTrace(0, Dbg, "OldVpb = %p\n", OldVpb);
1539
1540 //
1541 // Swap target device objects between the VCBs. That way
1542 // the old VCB will start using the new target device object,
1543 // and the new VCB will be torn down and deference the old
1544 // target device object.
1545 //
1546
1547 Vcb->TargetDeviceObject = OldVcb->TargetDeviceObject;
1549
1550 //
1551 // This is a remount, so link the old vpb in place
1552 // of the new vpb.
1553 //
1554
1556
1557 FatSetVcbCondition( OldVcb, VcbGood);
1558 OldVpb->RealDevice = Vpb->RealDevice;
1560
1561#ifdef _MSC_VER
1562#pragma prefast( suppress: 28175, "touching Vpb is ok for a filesystem" )
1563#endif
1564 OldVpb->RealDevice->Vpb = OldVpb;
1565
1566 //
1567 // Use the new changecount.
1568 //
1569
1570 OldVcb->ChangeCount = Vcb->ChangeCount;
1571
1572 //
1573 // If the new VPB is the VPB referenced in the original Irp, set
1574 // that reference back to the old VPB.
1575 //
1576
1577 IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb;
1578
1579 if (*IrpVpb == Vpb) {
1580
1581 *IrpVpb = OldVpb;
1582 }
1583
1584 //
1585 // We do not want to touch this VPB again. It will get cleaned up when
1586 // the new VCB is cleaned up.
1587 //
1588
1589 NT_ASSERT( Vcb->Vpb == Vpb );
1590
1591 Vpb = NULL;
1594
1595 //
1596 // Reinitialize the volume file cache and allocation support.
1597 //
1598
1599 {
1601
1603 FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 );
1605
1606 DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0);
1607
1609 &FileSizes,
1610 TRUE,
1612 Vcb );
1613
1614 //
1615 // Redo the allocation support
1616 //
1617
1618 FatSetupAllocationSupport( IrpContext, OldVcb );
1619
1620 //
1621 // Get the state of the dirty bit.
1622 //
1623
1624 FatCheckDirtyBit( IrpContext, OldVcb );
1625
1626 //
1627 // Check for write protected media.
1628 //
1629
1631
1633
1634 } else {
1635
1637 }
1638 }
1639
1640 //
1641 // Complete the request and return to our caller
1642 //
1643
1645 }
1646
1647 DebugTrace(0, Dbg, "Mount a new volume\n", 0);
1648
1649 //
1650 // This is a new mount
1651 //
1652 // Create a blank ea data file fcb, just not for Fat32.
1653 //
1654
1655 if (!FatIsFat32(Vcb)) {
1656
1657 DIRENT TempDirent;
1658 PFCB EaFcb;
1659
1660 RtlZeroMemory( &TempDirent, sizeof(DIRENT) );
1661 RtlCopyMemory( &TempDirent.FileName[0], "EA DATA SF", 11 );
1662
1663 EaFcb = FatCreateFcb( IrpContext,
1664 Vcb,
1665 Vcb->RootDcb,
1666 0,
1667 0,
1668 &TempDirent,
1669 NULL,
1670 NULL,
1671 FALSE,
1672 TRUE );
1673
1674 //
1675 // Deny anybody who trys to open the file.
1676 //
1677
1679
1680 Vcb->EaFcb = EaFcb;
1681 }
1682
1683 //
1684 // Get the state of the dirty bit.
1685 //
1686
1687 FatCheckDirtyBit( IrpContext, Vcb );
1688
1689
1690 //
1691 // Check for write protected media.
1692 //
1693
1695
1697
1698 } else {
1699
1701 }
1702
1703
1704 //
1705 // Lock volume in drive if we just mounted the boot drive.
1706 //
1707
1708 if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) {
1709
1711
1712 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
1713
1714 FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
1715 }
1716 }
1717
1718
1719 //
1720 // Indicate to our termination handler that we have mounted
1721 // a new volume.
1722 //
1723
1724 MountNewVolume = TRUE;
1725
1726 //
1727 // Complete the request
1728 //
1729
1731
1732 //
1733 // Ref the root dir stream object so we can send mount notification.
1734 //
1735
1736 RootDirectoryFile = Vcb->RootDcb->Specific.Dcb.DirectoryFile;
1737 ObReferenceObject( RootDirectoryFile );
1738
1739 //
1740 // Remove the extra reference to this target DO made on behalf of us
1741 // by the IO system. In the remount case, we permit regular Vcb
1742 // deletion to do this work.
1743 //
1744
1746
1747
1748 try_exit: NOTHING;
1749
1750 } _SEH2_FINALLY {
1751
1752 DebugUnwind( FatMountVolume );
1753
1754 FatUnpinBcb( IrpContext, BootBcb );
1755 FatUnpinBcb( IrpContext, DirentBcb );
1756
1757 //
1758 // Check if a volume was mounted. If not then we need to
1759 // mark the Vpb not mounted again.
1760 //
1761
1762 if ( !MountNewVolume ) {
1763
1764 if ( Vcb != NULL ) {
1765
1766 //
1767 // A VCB was created and initialized. We need to try to tear it down.
1768 //
1769
1770 FatCheckForDismount( IrpContext,
1771 Vcb,
1772 TRUE );
1773
1774 IrpContext->Vcb = NULL;
1775
1776 } else if (VolDo != NULL) {
1777
1778 //
1779 // The VCB was never initialized, so we need to delete the
1780 // device right here.
1781 //
1782
1783 IoDeleteDevice( &VolDo->DeviceObject );
1784 }
1785
1786 //
1787 // See if a remount failed.
1788 //
1789
1790 if (DoARemount && _SEH2_AbnormalTermination()) {
1791
1792 //
1793 // The remount failed. Try to tear down the old VCB as well.
1794 //
1795
1796 FatCheckForDismount( IrpContext,
1797 OldVcb,
1798 TRUE );
1799 }
1800 }
1801
1802 if ( WeClearedVerifyRequiredBit == TRUE ) {
1803
1804 SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
1805 }
1806
1807 FatReleaseGlobal( IrpContext );
1808
1809 DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status);
1810 } _SEH2_END;
1811
1812 //
1813 // Now send mount notification. Note that since this is outside of any
1814 // synchronization since the synchronous delivery of this may go to
1815 // folks that provoke re-entrance to the FS.
1816 //
1817
1818 if (RootDirectoryFile != NULL) {
1819
1820#if (NTDDI_VERSION >= NTDDI_WIN8)
1822
1823 CcSetAdditionalCacheAttributesEx( RootDirectoryFile, CC_ENABLE_DISK_IO_ACCOUNTING );
1824 }
1825#endif
1826
1827 FsRtlNotifyVolumeEvent( RootDirectoryFile, FSRTL_VOLUME_MOUNT );
1828 ObDereferenceObject( RootDirectoryFile );
1829 }
1830
1831 return Status;
1832}
1833
1834
1835//
1836// Local Support Routine
1837//
1838
1839_Requires_lock_held_(_Global_critical_region_)
1841FatVerifyVolume (
1842 IN PIRP_CONTEXT IrpContext,
1843 IN PIRP Irp
1844 )
1845
1846/*++
1847
1848Routine Description:
1849
1850 This routine performs the verify volume operation by checking the volume
1851 label and serial number physically on the media with the the Vcb
1852 currently claiming to have the volume mounted. It is responsible for
1853 either completing or enqueuing the input Irp.
1854
1855 Regardless of whether the verify operation succeeds, the following
1856 operations are performed:
1857
1858 - Set Vcb->VirtualEaFile back to its initial state.
1859 - Purge all cached data (flushing first if verify succeeds)
1860 - Mark all Fcbs as needing verification
1861
1862 If the volumes verifies correctly we also must:
1863
1864 - Check the volume dirty bit.
1865 - Reinitialize the allocation support
1866 - Flush any dirty data
1867
1868 If the volume verify fails, it may never be mounted again. If it is
1869 mounted again, it will happen as a remount operation. In preparation
1870 for that, and to leave the volume in a state that can be "lazy deleted"
1871 the following operations are performed:
1872
1873 - Set the Vcb condition to VcbNotMounted
1874 - Uninitialize the volume file cachemap
1875 - Tear down the allocation support
1876
1877 In the case of an abnormal termination we haven't determined the state
1878 of the volume, so we set the Device Object as needing verification again.
1879
1880Arguments:
1881
1882 Irp - Supplies the Irp to process
1883
1884Return Value:
1885
1886 NTSTATUS - If the verify operation completes, it will return either
1887 STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or
1888 other error is encountered, that status will be returned.
1889
1890--*/
1891
1892{
1894
1896
1899
1901
1903 PVCB Vcb;
1904 PVPB Vpb;
1905
1907 BOOLEAN ClearVerify = FALSE;
1908 BOOLEAN ReleaseEntireVolume = FALSE;
1909 BOOLEAN VerifyAlreadyDone = FALSE;
1910
1911 DISK_GEOMETRY DiskGeometry;
1912
1913 LBO RootDirectoryLbo;
1914 ULONG RootDirectorySize;
1915 BOOLEAN LabelFound;
1916
1917 ULONG ChangeCount = 0;
1918 IO_STATUS_BLOCK Iosb = {0};
1919
1920 PAGED_CODE();
1921
1922 //
1923 // Get the current Irp stack location
1924 //
1925
1927
1928 DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0);
1929 DebugTrace( 0, Dbg, "DeviceObject = %p\n", IrpSp->Parameters.VerifyVolume.DeviceObject);
1930 DebugTrace( 0, Dbg, "Vpb = %p\n", IrpSp->Parameters.VerifyVolume.Vpb);
1931
1932 //
1933 // Save some references to make our life a little easier. Note the Vcb for the purposes
1934 // of exception handling.
1935 //
1936
1937 VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject;
1938
1940 IrpContext->Vcb = Vcb = &VolDo->Vcb;
1941
1942 //
1943 // If we cannot wait then enqueue the irp to the fsp and
1944 // return the status to our caller.
1945 //
1946
1947 if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
1948
1949 DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0);
1950
1951 Status = FatFsdPostRequest( IrpContext, Irp );
1952
1953 DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status );
1954 return Status;
1955 }
1956
1957 //
1958 // We are serialized at this point allowing only one thread to
1959 // actually perform the verify operation. Any others will just
1960 // wait and then no-op when checking if the volume still needs
1961 // verification.
1962 //
1963
1964#ifdef _MSC_VER
1965#pragma prefast( push )
1966#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1967#pragma prefast( disable: 28193, "this will always wait" )
1968#endif
1969
1970 (VOID)FatAcquireExclusiveGlobal( IrpContext );
1971
1972#ifdef _MSC_VER
1973#pragma prefast( pop )
1974#endif
1975
1976 (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
1977
1978 _SEH2_TRY {
1979
1980 BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT );
1981
1982 //
1983 // Mark ourselves as verifying this volume so that recursive I/Os
1984 // will be able to complete.
1985 //
1986
1987 NT_ASSERT( Vcb->VerifyThread == NULL );
1988 Vcb->VerifyThread = KeGetCurrentThread();
1989
1990 //
1991 // Check if the real device still needs to be verified. If it doesn't
1992 // then obviously someone beat us here and already did the work
1993 // so complete the verify irp with success. Otherwise reenable
1994 // the real device and get to work.
1995 //
1996
1997 if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
1998
1999 DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0);
2000
2001 VerifyAlreadyDone = TRUE;
2003 }
2004
2005 //
2006 // Ping the volume with a partition query to make Jeff happy.
2007 //
2008
2009 {
2010 PARTITION_INFORMATION_EX PartitionInformation;
2011
2012 (VOID) FatPerformDevIoCtrl( IrpContext,
2014 Vcb->TargetDeviceObject,
2015 NULL,
2016 0,
2017 &PartitionInformation,
2019 FALSE,
2020 TRUE,
2021 &Iosb );
2022 }
2023
2024 //
2025 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
2026 //
2027
2028 if (FlagOn(Vcb->TargetDeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
2029
2030 //
2031 // Verify that there is a disk here and pick up the change count.
2032 //
2033
2034 Status = FatPerformDevIoCtrl( IrpContext,
2036 Vcb->TargetDeviceObject,
2037 NULL,
2038 0,
2039 &ChangeCount,
2040 sizeof(ULONG),
2041 FALSE,
2042 TRUE,
2043 &Iosb );
2044
2045 if (!NT_SUCCESS( Status )) {
2046
2047 //
2048 // If we will allow a raw mount then return WRONG_VOLUME to
2049 // allow the volume to be mounted by raw.
2050 //
2051
2052 if (AllowRawMount) {
2053
2055 }
2056
2057 FatNormalizeAndRaiseStatus( IrpContext, Status );
2058 }
2059
2060 }
2061
2062 if (Iosb.Information != sizeof(ULONG)) {
2063
2064 //
2065 // Be safe about the count in case the driver didn't fill it in
2066 //
2067
2068 ChangeCount = 0;
2069 }
2070
2071 //
2072 // Whatever happens we will have verified this volume at this change
2073 // count, so record that fact.
2074 //
2075
2076 Vcb->ChangeCount = ChangeCount;
2077
2078 //
2079 // If this is a CD class device, then check to see if there is a
2080 // 'data track' or not. This is to avoid issuing paging reads which will
2081 // fail later in the mount process (e.g. CD-DA or blank CD media)
2082 //
2083
2084 if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
2085 !FatScanForDataTrack( IrpContext, Vcb->TargetDeviceObject)) {
2086
2088 }
2089
2090 //
2091 // Some devices can change sector sizes on the fly. Obviously, it
2092 // isn't the same volume if that happens.
2093 //
2094
2095 Status = FatPerformDevIoCtrl( IrpContext,
2097 Vcb->TargetDeviceObject,
2098 NULL,
2099 0,
2100 &DiskGeometry,
2101 sizeof( DISK_GEOMETRY ),
2102 FALSE,
2103 TRUE,
2104 NULL );
2105
2106 if (!NT_SUCCESS( Status )) {
2107
2108 //
2109 // If we will allow a raw mount then return WRONG_VOLUME to
2110 // allow the volume to be mounted by raw.
2111 //
2112
2113 if (AllowRawMount) {
2114
2116 }
2117
2118 FatNormalizeAndRaiseStatus( IrpContext, Status );
2119 }
2120
2121 //
2122 // Read in the boot sector
2123 //
2124
2125 SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
2126
2127 if (SectorSize != DiskGeometry.BytesPerSector) {
2128
2130 }
2131
2132 BootSector = FsRtlAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
2135
2136 //
2137 // If this verify is on behalf of a DASD open, allow a RAW mount.
2138 //
2139
2140 if (!FatPerformVerifyDiskRead( IrpContext,
2141 Vcb,
2142 BootSector,
2143 0,
2144 SectorSize,
2145 AllowRawMount )) {
2146
2148 }
2149
2150 //
2151 // Call a routine to check the boot sector to see if it is fat.
2152 // If it is not fat then mark the vcb as not mounted tell our
2153 // caller its the wrong volume
2154 //
2155
2157
2158 DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
2159
2161 }
2162
2163 //
2164 // This is a fat volume, so extract serial number and see if it is
2165 // ours.
2166 //
2167
2168 {
2170
2171 if (IsBpbFat32(&BootSector->PackedBpb)) {
2173 } else {
2175 }
2176
2177 if (SerialNumber != Vpb->SerialNumber) {
2178
2179 DebugTrace(0, Dbg, "Not our serial number\n", 0);
2180
2182 }
2183 }
2184
2185 //
2186 // Make sure the Bpbs are not different. We have to zero out our
2187 // stack version of the Bpb since unpacking leaves holes.
2188 //
2189
2190 RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) );
2191
2192 FatUnpackBios( &Bpb, &BootSector->PackedBpb );
2193 if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
2194
2195 if ( !RtlEqualMemory( &Bpb,
2196 &Vcb->Bpb,
2197 IsBpbFat32(&Bpb) ?
2198 sizeof(BIOS_PARAMETER_BLOCK) :
2200 LargeSectorsPerFat) )) {
2201
2202 DebugTrace(0, Dbg, "Bpb is different\n", 0);
2203
2205 }
2206
2207 //
2208 // Check the volume label. We do this by trying to locate the
2209 // volume label, making two strings one for the saved volume label
2210 // and the other for the new volume label and then we compare the
2211 // two labels.
2212 //
2213
2214 if (FatRootDirectorySize(&Bpb) > 0) {
2215
2216 RootDirectorySize = FatRootDirectorySize(&Bpb);
2217
2218 } else {
2219
2220 RootDirectorySize = FatBytesPerCluster(&Bpb);
2221 }
2222
2223 RootDirectory = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned,
2224 (ULONG) ROUND_TO_PAGES( RootDirectorySize ),
2226
2227 if (!IsBpbFat32(&BootSector->PackedBpb)) {
2228
2229 //
2230 // The Fat12/16 case is simple -- read the root directory in and
2231 // search it.
2232 //
2233
2234 RootDirectoryLbo = FatRootDirectoryLbo(&Bpb);
2235
2236 if (!FatPerformVerifyDiskRead( IrpContext,
2237 Vcb,
2239 RootDirectoryLbo,
2240 RootDirectorySize,
2241 AllowRawMount )) {
2242
2244 }
2245
2246 Status = FatSearchBufferForLabel(IrpContext, Vpb,
2247 RootDirectory, RootDirectorySize,
2248 &LabelFound);
2249
2250 if (!NT_SUCCESS(Status)) {
2251
2252 try_return( Status );
2253 }
2254
2255 if (!LabelFound && Vpb->VolumeLabelLength > 0) {
2256
2258 }
2259
2260 } else {
2261
2262 ULONG RootDirectoryCluster;
2263
2264 RootDirectoryCluster = Bpb.RootDirFirstCluster;
2265
2266 while (RootDirectoryCluster != FAT_CLUSTER_LAST) {
2267
2268 RootDirectoryLbo = FatGetLboFromIndex(Vcb, RootDirectoryCluster);
2269
2270 if (!FatPerformVerifyDiskRead( IrpContext,
2271 Vcb,
2273 RootDirectoryLbo,
2274 RootDirectorySize,
2275 AllowRawMount )) {
2276
2278 }
2279
2280 Status = FatSearchBufferForLabel(IrpContext, Vpb,
2281 RootDirectory, RootDirectorySize,
2282 &LabelFound);
2283
2284 if (!NT_SUCCESS(Status)) {
2285
2286 try_return( Status );
2287 }
2288
2289 if (LabelFound) {
2290
2291 //
2292 // Found a matching label.
2293 //
2294
2295 break;
2296 }
2297
2298 //
2299 // Set ourselves up for the next loop iteration.
2300 //
2301
2302 FatVerifyLookupFatEntry( IrpContext, Vcb,
2303 RootDirectoryCluster,
2304 &RootDirectoryCluster );
2305
2306 switch (FatInterpretClusterType(Vcb, RootDirectoryCluster)) {
2307
2309 case FatClusterReserved:
2310 case FatClusterBad:
2311
2312 //
2313 // Bail all the way out if we have a bad root.
2314 //
2315
2317 break;
2318
2319 default:
2320
2321 break;
2322 }
2323
2324 }
2325
2326 if (RootDirectoryCluster == FAT_CLUSTER_LAST &&
2327 Vpb->VolumeLabelLength > 0) {
2328
2329 //
2330 // Should have found a label, didn't find any.
2331 //
2332
2334 }
2335 }
2336
2337
2338 try_exit: NOTHING;
2339
2340 //
2341 // Note that we have previously acquired the Vcb to serialize
2342 // the EA file stuff the marking all the Fcbs as NeedToBeVerified.
2343 //
2344 // Put the Ea file back in a initial state.
2345 //
2346
2347 FatCloseEaFile( IrpContext, Vcb, (BOOLEAN)(Status == STATUS_SUCCESS) );
2348
2349 //
2350 // Mark all Fcbs as needing verification, but only if we really have
2351 // to do it.
2352 //
2353
2354 if (!VerifyAlreadyDone) {
2355
2356 FatAcquireExclusiveVolume( IrpContext, Vcb );
2357 ReleaseEntireVolume = TRUE;
2358
2359 FatMarkFcbCondition( IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified, TRUE );
2360 }
2361
2362 //
2363 // If the verify didn't succeed, get the volume ready for a
2364 // remount or eventual deletion.
2365 //
2366
2367 if (Vcb->VcbCondition == VcbNotMounted) {
2368
2369 //
2370 // If the volume was already in an unmounted state, just bail
2371 // and make sure we return STATUS_WRONG_VOLUME.
2372 //
2373
2375
2376 } else if ( Status == STATUS_WRONG_VOLUME ) {
2377
2378 //
2379 // Grab everything so we can safely transition the volume state without
2380 // having a thread stumble into the torn-down allocation engine.
2381 //
2382
2383 if (!ReleaseEntireVolume) {
2384 FatAcquireExclusiveVolume( IrpContext, Vcb );
2385 ReleaseEntireVolume = TRUE;
2386 }
2387
2388 //
2389 // Get rid of any cached data, without flushing
2390 //
2391
2392 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush );
2393
2394 //
2395 // Uninitialize the volume file cache map. Note that we cannot
2396 // do a "FatSyncUninit" because of deadlock problems. However,
2397 // since this FileObject is referenced by us, and thus included
2398 // in the Vpb residual count, it is OK to do a normal CcUninit.
2399 //
2400
2401 CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
2402 &FatLargeZero,
2403 NULL );
2404
2405 FatTearDownAllocationSupport( IrpContext, Vcb );
2406
2408
2409 ClearVerify = TRUE;
2410
2411 } else if (!VerifyAlreadyDone) {
2412
2413 //
2414 // Grab everything so we can safely transition the volume state without
2415 // having a thread stumble into the torn-down allocation engine.
2416 //
2417
2418 if (!ReleaseEntireVolume) {
2419 FatAcquireExclusiveVolume( IrpContext, Vcb );
2420 ReleaseEntireVolume = TRUE;
2421 }
2422
2423 //
2424 // Get rid of any cached data, flushing first.
2425 //
2426 // Future work (and for bonus points, around the other flush points)
2427 // could address the possibility that the dirent filesize hasn't been
2428 // updated yet, causing us to fail the re-verification of a file in
2429 // DetermineAndMark. This is pretty subtle and very very uncommon.
2430 //
2431
2432 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
2433
2434 //
2435 // Flush and Purge the volume file.
2436 //
2437
2438 (VOID)FatFlushFat( IrpContext, Vcb );
2439 CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE );
2440
2441 //
2442 // Redo the allocation support with newly paged stuff.
2443 //
2444
2445 FatTearDownAllocationSupport( IrpContext, Vcb );
2446 FatSetupAllocationSupport( IrpContext, Vcb );
2447
2448 FatCheckDirtyBit( IrpContext, Vcb );
2449
2450 //
2451 // Check for write protected media.
2452 //
2453
2454 if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) {
2455
2457
2458 } else {
2459
2461 }
2462
2463 ClearVerify = TRUE;
2464 }
2465
2466 if (ClearVerify) {
2467
2468 //
2469 // Mark the device as no longer needing verification.
2470 //
2471
2472 ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
2473 }
2474
2475 } _SEH2_FINALLY {
2476
2477 DebugUnwind( FatVerifyVolume );
2478
2479 //
2480 // Free any buffer we may have allocated
2481 //
2482
2483 if ( BootSector != NULL ) { ExFreePool( BootSector ); }
2484 if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); }
2485
2486 //
2487 // Show that we are done with this volume.
2488 //
2489
2490 NT_ASSERT( Vcb->VerifyThread == KeGetCurrentThread() );
2491 Vcb->VerifyThread = NULL;
2492
2493 if (ReleaseEntireVolume) {
2494
2495 FatReleaseVolume( IrpContext, Vcb );
2496 }
2497
2498 FatReleaseVcb( IrpContext, Vcb );
2499 FatReleaseGlobal( IrpContext );
2500
2501 //
2502 // If this was not an abnormal termination, complete the irp.
2503 //
2504
2506
2507 FatCompleteRequest( IrpContext, Irp, Status );
2508 }
2509
2510 DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status);
2511 } _SEH2_END;
2512
2513 return Status;
2514}
2515
2516
2517//
2518// Local Support Routine
2519//
2520
2521BOOLEAN
2524 )
2525
2526/*++
2527
2528Routine Description:
2529
2530 This routine checks if the boot sector is for a fat file volume.
2531
2532Arguments:
2533
2534 BootSector - Supplies the packed boot sector to check
2535
2536Return Value:
2537
2538 BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
2539
2540--*/
2541
2542{
2544 BIOS_PARAMETER_BLOCK Bpb = {0};
2545
2546 DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %p\n", BootSector);
2547
2548 //
2549 // The result is true unless we decide that it should be false
2550 //
2551
2552 Result = TRUE;
2553
2554 //
2555 // Unpack the bios and then test everything
2556 //
2557
2558 FatUnpackBios( &Bpb, &BootSector->PackedBpb );
2559 if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
2560
2561 if ((BootSector->Jump[0] != 0xe9) &&
2562 (BootSector->Jump[0] != 0xeb) &&
2563 (BootSector->Jump[0] != 0x49)) {
2564
2565 Result = FALSE;
2566
2567 //
2568 // Enforce some sanity on the sector size (easy check)
2569 //
2570
2571 } else if ((Bpb.BytesPerSector != 128) &&
2572 (Bpb.BytesPerSector != 256) &&
2573 (Bpb.BytesPerSector != 512) &&
2574 (Bpb.BytesPerSector != 1024) &&
2575 (Bpb.BytesPerSector != 2048) &&
2576 (Bpb.BytesPerSector != 4096)) {
2577
2578 Result = FALSE;
2579
2580 //
2581 // Likewise on the clustering.
2582 //
2583
2584 } else if ((Bpb.SectorsPerCluster != 1) &&
2585 (Bpb.SectorsPerCluster != 2) &&
2586 (Bpb.SectorsPerCluster != 4) &&
2587 (Bpb.SectorsPerCluster != 8) &&
2588 (Bpb.SectorsPerCluster != 16) &&
2589 (Bpb.SectorsPerCluster != 32) &&
2590 (Bpb.SectorsPerCluster != 64) &&
2591 (Bpb.SectorsPerCluster != 128)) {
2592
2593 Result = FALSE;
2594
2595 //
2596 // Likewise on the reserved sectors (must reflect at least the boot sector!)
2597 //
2598
2599 } else if (Bpb.ReservedSectors == 0) {
2600
2601 Result = FALSE;
2602
2603 //
2604 // No FATs? Wrong ...
2605 //
2606
2607 } else if (Bpb.Fats == 0) {
2608
2609 Result = FALSE;
2610
2611 //
2612 // Prior to DOS 3.2 might contains value in both of Sectors and
2613 // Sectors Large.
2614 //
2615
2616 } else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) {
2617
2618 Result = FALSE;
2619
2620 //
2621 // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and
2622 // is of a version we recognize, currently Version 0.0.
2623 //
2624
2625 } else if (Bpb.SectorsPerFat == 0 && ( Bpb.LargeSectorsPerFat == 0 ||
2626 Bpb.FsVersion != 0 )) {
2627
2628 Result = FALSE;
2629
2630 } else if ((Bpb.Media != 0xf0) &&
2631 (Bpb.Media != 0xf8) &&
2632 (Bpb.Media != 0xf9) &&
2633 (Bpb.Media != 0xfb) &&
2634 (Bpb.Media != 0xfc) &&
2635 (Bpb.Media != 0xfd) &&
2636 (Bpb.Media != 0xfe) &&
2637 (Bpb.Media != 0xff) &&
2638 (!FatData.FujitsuFMR || ((Bpb.Media != 0x00) &&
2639 (Bpb.Media != 0x01) &&
2640 (Bpb.Media != 0xfa)))) {
2641
2642 Result = FALSE;
2643
2644 //
2645 // If this isn't FAT32, then there better be a claimed root directory
2646 // size here ...
2647 //
2648
2649 } else if (Bpb.SectorsPerFat != 0 && Bpb.RootEntries == 0) {
2650
2651 Result = FALSE;
2652
2653 //
2654 // If this is FAT32 (i.e., extended BPB), look for and refuse to mount
2655 // mirror-disabled volumes. If we did, we would need to only write to
2656 // the FAT# indicated in the ActiveFat field. The only user of this is
2657 // the FAT->FAT32 converter after the first pass of protected mode work
2658 // (booting into realmode) and NT should absolutely not be attempting
2659 // to mount such an in-transition volume.
2660 //
2661
2662 } else if (Bpb.SectorsPerFat == 0 && Bpb.MirrorDisabled) {
2663
2664 Result = FALSE;
2665 }
2666
2667 DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result);
2668
2669 return Result;
2670}
2671
2672
2673//
2674// Local Support Routine
2675//
2676
2677BOOLEAN
2679 IN PIRP_CONTEXT IrpContext,
2681 )
2682
2683/*++
2684
2685Routine Description:
2686
2687 This routine determines if the target media is write protected.
2688
2689Arguments:
2690
2691 TargetDeviceObject - The target of the query
2692
2693Return Value:
2694
2695 NTSTATUS - The return status for the operation
2696
2697--*/
2698
2699{
2700 PIRP Irp;
2701 KEVENT Event;
2704
2705 PAGED_CODE();
2706 UNREFERENCED_PARAMETER( IrpContext );
2707
2708 //
2709 // Query the partition table
2710 //
2711
2713
2714 //
2715 // See if the media is write protected. On success or any kind
2716 // of error (possibly illegal device function), assume it is
2717 // writeable, and only complain if he tells us he is write protected.
2718 //
2719
2722 NULL,
2723 0,
2724 NULL,
2725 0,
2726 FALSE,
2727 &Event,
2728 &Iosb );
2729
2730 //
2731 // Just return FALSE in the unlikely event we couldn't allocate an Irp.
2732 //
2733
2734 if ( Irp == NULL ) {
2735
2736 return FALSE;
2737 }
2738
2740
2742
2743 if ( Status == STATUS_PENDING ) {
2744
2746 Executive,
2747 KernelMode,
2748 FALSE,
2750
2751 Status = Iosb.Status;
2752 }
2753
2755}
2756
2757
2758//
2759// Local Support Routine
2760//
2761
2762_Requires_lock_held_(_Global_critical_region_)
2764FatUserFsCtrl (
2765 IN PIRP_CONTEXT IrpContext,
2766 IN PIRP Irp
2767 )
2768
2769/*++
2770
2771Routine Description:
2772
2773 This is the common routine for implementing the user's requests made
2774 through NtFsControlFile.
2775
2776Arguments:
2777
2778 Irp - Supplies the Irp being processed
2779
2780Return Value:
2781
2782 NTSTATUS - The return status for the operation
2783
2784--*/
2785
2786{
2789
2791
2792 PAGED_CODE();
2793
2794 //
2795 // Save some references to make our life a little easier
2796 //
2797
2799
2800 DebugTrace(+1, Dbg,"FatUserFsCtrl...\n", 0);
2801 DebugTrace( 0, Dbg,"FsControlCode = %08lx\n", FsControlCode);
2802
2803 //
2804 // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode
2805 // of the caller was userspace and this is a METHOD_NEITHER, we have the choice
2806 // of realy buffering the request through so we can possibly post, or making the
2807 // request synchronous. Since the former was not done by design, do the latter.
2808 //
2809
2810 if (Irp->RequestorMode != KernelMode && (FsControlCode & 3) == METHOD_NEITHER) {
2811
2812 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
2813 }
2814
2815 //
2816 // Case on the control code.
2817 //
2818
2819 switch ( FsControlCode ) {
2820
2829#if (NTDDI_VERSION >= NTDDI_WIN7)
2830 case FSCTL_REQUEST_OPLOCK:
2831#endif
2832 Status = FatOplockRequest( IrpContext, Irp );
2833 break;
2834
2835 case FSCTL_LOCK_VOLUME:
2836
2837 Status = FatLockVolume( IrpContext, Irp );
2838 break;
2839
2841
2842 Status = FatUnlockVolume( IrpContext, Irp );
2843 break;
2844
2846
2847 Status = FatDismountVolume( IrpContext, Irp );
2848 break;
2849
2851
2852 Status = FatDirtyVolume( IrpContext, Irp );
2853 break;
2854
2856
2857 Status = FatIsVolumeDirty( IrpContext, Irp );
2858 break;
2859
2861
2862 Status = FatIsVolumeMounted( IrpContext, Irp );
2863 break;
2864
2866 Status = FatIsPathnameValid( IrpContext, Irp );
2867 break;
2868
2870 Status = FatQueryRetrievalPointers( IrpContext, Irp );
2871 break;
2872
2874 Status = FatQueryBpb( IrpContext, Irp );
2875 break;
2876
2878 Status = FatGetStatistics( IrpContext, Irp );
2879 break;
2880
2881#if (NTDDI_VERSION >= NTDDI_WIN7)
2883 Status = FatGetRetrievalPointerBase( IrpContext, Irp );
2884 break;
2885
2886 case FSCTL_GET_BOOT_AREA_INFO:
2887 Status = FatGetBootAreaInfo( IrpContext, Irp );
2888 break;
2889#endif
2890
2892 Status = FatGetVolumeBitmap( IrpContext, Irp );
2893 break;
2894
2896 Status = FatGetRetrievalPointers( IrpContext, Irp );
2897 break;
2898
2899 case FSCTL_MOVE_FILE:
2900 Status = FatMoveFile( IrpContext, Irp );
2901 break;
2902
2904 Status = FatAllowExtendedDasdIo( IrpContext, Irp );
2905 break;
2906
2907 case FSCTL_MARK_HANDLE:
2908 Status = FatMarkHandle( IrpContext, Irp );
2909 break;
2910
2911#if (NTDDI_VERSION >= NTDDI_WIN8)
2912
2913 case FSCTL_SET_PURGE_FAILURE_MODE:
2914 Status = FatSetPurgeFailureMode( IrpContext, Irp );
2915 break;
2916
2917#endif
2918
2919
2920#if (NTDDI_VERSION >= NTDDI_WIN7)
2922 Status = FatSetZeroOnDeallocate( IrpContext, Irp );
2923 break;
2924#endif
2925
2926 default :
2927
2928 DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode );
2929
2932 break;
2933 }
2934
2935 DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status );
2936 return Status;
2937}
2938
2939
2940
2941//
2942// Local support routine
2943//
2944
2945_Requires_lock_held_(_Global_critical_region_)
2947FatOplockRequest (
2948 _In_ PIRP_CONTEXT IrpContext,
2949 _In_ PIRP Irp
2950 )
2951
2952/*++
2953
2954Routine Description:
2955
2956 This is the common routine to handle oplock requests made via the
2957 NtFsControlFile call.
2958
2959Arguments:
2960
2961 Irp - Supplies the Irp being processed
2962
2963Return Value:
2964
2965 NTSTATUS - The return status for the operation
2966
2967--*/
2968
2969{
2972 PFCB Fcb;
2973 PVCB Vcb;
2974 PCCB Ccb;
2975
2976 ULONG OplockCount = 0;
2977
2979
2980 BOOLEAN AcquiredVcb = FALSE;
2981 BOOLEAN AcquiredFcb = FALSE;
2982
2983#if (NTDDI_VERSION >= NTDDI_WIN7)
2984 PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer = NULL;
2987#endif
2988
2990
2991 PAGED_CODE();
2992
2993 //
2994 // Save some references to make our life a little easier
2995 //
2996
2998
3000
3001 DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0);
3002 DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
3003
3004 //
3005 // We permit oplock requests on files and directories.
3006 //
3007
3008 if ((TypeOfOpen != UserFileOpen)
3010 &&
3012#endif
3013 ) {
3014
3016 DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3018 }
3019
3020#if (NTDDI_VERSION >= NTDDI_WIN7)
3021
3022 //
3023 // Get the input & output buffer lengths and pointers.
3024 //
3025
3026 if (FsControlCode == FSCTL_REQUEST_OPLOCK) {
3027
3029 InputBuffer = (PREQUEST_OPLOCK_INPUT_BUFFER) Irp->AssociatedIrp.SystemBuffer;
3030
3032
3033 //
3034 // Check for a minimum length on the input and ouput buffers.
3035 //
3036
3037 if ((InputBufferLength < sizeof( REQUEST_OPLOCK_INPUT_BUFFER )) ||
3038 (OutputBufferLength < sizeof( REQUEST_OPLOCK_OUTPUT_BUFFER ))) {
3039
3041 DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_BUFFER_TOO_SMALL\n", 0);
3043 }
3044 }
3045
3046 //
3047 // If the oplock request is on a directory it must be for a Read or Read-Handle
3048 // oplock only.
3049 //
3050
3051 if ((TypeOfOpen == UserDirectoryOpen) &&
3052 ((FsControlCode != FSCTL_REQUEST_OPLOCK) ||
3053 !FsRtlOplockIsSharedRequest( Irp ))) {
3054
3056 DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3058 }
3059
3060#endif
3061
3062 //
3063 // Make this a waitable Irpcontext so we don't fail to acquire
3064 // the resources.
3065 //
3066
3067 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
3068
3069 //
3070 // Use a try finally to free the Fcb/Vcb
3071 //
3072
3073 _SEH2_TRY {
3074
3075 //
3076 // We grab the Fcb exclusively for oplock requests, shared for oplock
3077 // break acknowledgement.
3078 //
3079
3085 ||
3086 ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_REQUEST ))
3087#endif
3088 ) {
3089
3090 FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
3091 AcquiredVcb = TRUE;
3092 FatAcquireExclusiveFcb( IrpContext, Fcb );
3093 AcquiredFcb = TRUE;
3094
3095#if (NTDDI_VERSION >= NTDDI_WIN7)
3096 if (FsRtlOplockIsSharedRequest( Irp )) {
3097#else
3099#endif
3100
3101 //
3102 // Byte-range locks are only valid on files.
3103 //
3104
3105 if (TypeOfOpen == UserFileOpen) {
3106
3107 //
3108 // Set OplockCount to nonzero if FsRtl denies access
3109 // based on current byte-range lock state.
3110 //
3111
3112#if (NTDDI_VERSION >= NTDDI_WIN8)
3113 OplockCount = (ULONG) !FsRtlCheckLockForOplockRequest( &Fcb->Specific.Fcb.FileLock, &Fcb->Header.AllocationSize );
3114#elif (NTDDI_VERSION >= NTDDI_WIN7)
3115 OplockCount = (ULONG) FsRtlAreThereCurrentOrInProgressFileLocks( &Fcb->Specific.Fcb.FileLock );
3116#else
3117 OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock );
3118#endif
3119
3120 }
3121
3122 } else {
3123
3124 OplockCount = Fcb->UncleanCount;
3125 }
3126
3132 ||
3133 ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_ACK ))
3134#endif
3135 ) {
3136
3137 FatAcquireSharedFcb( IrpContext, Fcb );
3138 AcquiredFcb = TRUE;
3139#if (NTDDI_VERSION >= NTDDI_WIN7)
3140 } else if (FsControlCode == FSCTL_REQUEST_OPLOCK) {
3141
3142 //
3143 // The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or
3144 // REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer.
3145 //
3146
3148
3149 } else {
3150#else
3151 } else {
3152#endif
3153
3154#ifdef _MSC_VER
3155#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
3156#endif
3157 FatBugCheck( FsControlCode, 0, 0 );
3158 }
3159
3160 //
3161 // Fail batch, filter, and handle oplock requests if the file is marked
3162 // for delete.
3163 //
3164
3168 ||
3169 ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->RequestedOplockLevel, OPLOCK_LEVEL_CACHE_HANDLE ))
3170#endif
3171 ) &&
3173
3175 }
3176
3177 //
3178 // Call the FsRtl routine to grant/acknowledge oplock.
3179 //
3180
3182 Irp,
3183 OplockCount );
3184
3185 //
3186 // Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should not complete it.
3187 //
3188
3189 Irp = NULL;
3190
3191 //
3192 // Set the flag indicating if Fast I/O is possible
3193 //
3194
3195 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
3196
3197 } _SEH2_FINALLY {
3198
3199 DebugUnwind( FatOplockRequest );
3200
3201 //
3202 // Release all of our resources
3203 //
3204
3205 if (AcquiredVcb) {
3206
3207 FatReleaseVcb( IrpContext, Fcb->Vcb );
3208 }
3209
3210 if (AcquiredFcb) {
3211
3212 FatReleaseFcb( IrpContext, Fcb );
3213 }
3214
3215 DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
3216 } _SEH2_END;
3217
3218 FatCompleteRequest( IrpContext, Irp, Status );
3219
3220 return Status;
3221}
3222
3223
3224//
3225// Local Support Routine
3226//
3227
3228_Requires_lock_held_(_Global_critical_region_)
3230FatLockVolume (
3231 IN PIRP_CONTEXT IrpContext,
3232 IN PIRP Irp
3233 )
3234
3235/*++
3236
3237Routine Description:
3238
3239 This routine performs the lock volume operation. It is responsible for
3240 either completing of enqueuing the input Irp.
3241
3242Arguments:
3243
3244 Irp - Supplies the Irp to process
3245
3246Return Value:
3247
3248 NTSTATUS - The return status for the operation
3249
3250--*/
3251
3252{
3254
3256
3257 PVCB Vcb;
3258 PFCB Fcb;
3259 PCCB Ccb;
3260
3261 PAGED_CODE();
3262
3264
3265 DebugTrace(+1, Dbg, "FatLockVolume...\n", 0);
3266
3267 //
3268 // Decode the file object, the only type of opens we accept are
3269 // user volume opens.
3270 //
3271
3273
3275
3276 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3278 }
3279
3281
3283
3284 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3286 }
3287
3288 //
3289 // Send our notification so that folks that like to hold handles on
3290 // volumes can get out of the way.
3291 //
3292
3294
3295 //
3296 // Acquire exclusive access to the Vcb and enqueue the Irp if we
3297 // didn't get access.
3298 //
3299
3300 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
3301
3302 DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
3303
3304 Status = FatFsdPostRequest( IrpContext, Irp );
3305
3306 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
3307 return Status;
3308 }
3309
3310 _SEH2_TRY {
3311
3312 Status = FatLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
3313
3314 } _SEH2_FINALLY {
3315
3316 //
3317 // Since we drop and release the vcb while trying to punch the volume
3318 // down, it may be the case that we decide the operation should not
3319 // continue if the user raced a CloeseHandle() with us (and it finished
3320 // the cleanup) while we were waiting for our closes to finish.
3321 //
3322 // In this case, we will have been raised out of the acquire logic with
3323 // STATUS_FILE_CLOSED, and the volume will not be held.
3324 //
3325
3327
3328 FatReleaseVcb( IrpContext, Vcb );
3329 }
3330
3332
3333 //
3334 // The volume lock will be failing.
3335 //
3336
3338 }
3339 } _SEH2_END;
3340
3341 FatCompleteRequest( IrpContext, Irp, Status );
3342
3343 DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status);
3344
3345 return Status;
3346}
3347
3348
3349//
3350// Local Support Routine
3351//
3352
3355 IN PIRP_CONTEXT IrpContext,
3356 IN PIRP Irp
3357 )
3358
3359/*++
3360
3361Routine Description:
3362
3363 This routine performs the unlock volume operation. It is responsible for
3364 either completing of enqueuing the input Irp.
3365
3366Arguments:
3367
3368 Irp - Supplies the Irp to process
3369
3370Return Value:
3371
3372 NTSTATUS - The return status for the operation
3373
3374--*/
3375
3376{
3378
3380
3381 PVCB Vcb;
3382 PFCB Fcb;
3383 PCCB Ccb;
3384
3385 PAGED_CODE();
3386
3388
3389 DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0);
3390
3391 //
3392 // Decode the file object, the only type of opens we accept are
3393 // user volume opens.
3394 //
3395
3397
3399
3400 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3402 }
3403
3405
3407
3408 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3410 }
3411
3413
3414 //
3415 // Send notification that the volume is avaliable.
3416 //
3417
3418 if (NT_SUCCESS( Status )) {
3419
3421 }
3422
3423 FatCompleteRequest( IrpContext, Irp, Status );
3424
3425 DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
3426
3427 return Status;
3428}
3429
3430
3431_Requires_lock_held_(_Global_critical_region_)
3433FatLockVolumeInternal (
3434 IN PIRP_CONTEXT IrpContext,
3435 IN PVCB Vcb,
3437 )
3438
3439/*++
3440
3441Routine Description:
3442
3443 This routine performs the actual lock volume operation. It will be called
3444 by anyone wishing to try to protect the volume for a long duration. PNP
3445 operations are such a user.
3446
3447 The volume must be held exclusive by the caller.
3448
3449Arguments:
3450
3451 Vcb - The volume being locked.
3452
3453 FileObject - File corresponding to the handle locking the volume. If this
3454 is not specified, a system lock is assumed.
3455
3456Return Value:
3457
3458 NTSTATUS - The return status for the operation
3459
3460--*/
3461
3462{
3464 KIRQL SavedIrql;
3465 ULONG RemainingUserReferences = (FileObject? 1: 0);
3466
3469 //
3470 // Go synchronous for the rest of the lock operation. It may be
3471 // reasonable to try to revisit this in the future, but for now
3472 // the purge below expects to be able to wait.
3473 //
3474 // We know it is OK to leave the flag up given how we're used at
3475 // the moment.
3476 //
3477
3478 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
3479
3480 //
3481 // If there are any open handles, this will fail.
3482 //
3483
3484 if (!FatIsHandleCountZero( IrpContext, Vcb )) {
3485
3486 return STATUS_ACCESS_DENIED;
3487 }
3488
3489 //
3490 // Force Mm to get rid of its referenced file objects.
3491 //
3492
3493 FatFlushFat( IrpContext, Vcb );
3494
3495 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
3496
3497 FatCloseEaFile( IrpContext, Vcb, TRUE );
3498
3499 //
3500 // Now back out of our synchronization and wait for the lazy writer
3501 // to finish off any lazy closes that could have been outstanding.
3502 //
3503 // Since we flushed, we know that the lazy writer will issue all
3504 // possible lazy closes in the next tick - if we hadn't, an otherwise
3505 // unopened file with a large amount of dirty data could have hung
3506 // around for a while as the data trickled out to the disk.
3507 //
3508 // This is even more important now since we send notification to
3509 // alert other folks that this style of check is about to happen so
3510 // that they can close their handles. We don't want to enter a fast
3511 // race with the lazy writer tearing down his references to the file.
3512 //
3513
3514 FatReleaseVcb( IrpContext, Vcb );
3515
3517
3518 FatAcquireExclusiveVcb( IrpContext, Vcb );
3519
3520 if (!NT_SUCCESS( Status )) {
3521
3522 return Status;
3523 }
3524
3525 //
3526 // The act of closing and purging may have touched pages in various
3527 // parent DCBs. We handle this by purging a second time.
3528 //
3529
3530 FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
3531
3532 FatReleaseVcb( IrpContext, Vcb );
3533
3535
3536 FatAcquireExclusiveVcb( IrpContext, Vcb );
3537
3538 if (!NT_SUCCESS( Status )) {
3539
3540 return Status;
3541 }
3542
3543 //
3544 // Now rundown the delayed closes one last time. We appear to be able
3545 // to have additional collisions.
3546 //
3547
3548 FatFspClose( Vcb );
3549
3550 //
3551 // Check if the Vcb is already locked, or if the open file count
3552 // is greater than 1 (which implies that someone else also is
3553 // currently using the volume, or a file on the volume), and that the
3554 // VPB reference count only includes our residual and the handle (as
3555 // appropriate).
3556 //
3557 // We used to only check for the vpb refcount. This is unreliable since
3558 // the vpb refcount is dropped immediately before final close, meaning
3559 // that even though we had a good refcount, the close was inflight and
3560 // subsequent operations could get confused. Especially if the PNP path
3561 // was the lock caller, we delete the VCB with an outstanding opencount!
3562 //
3563
3564 IoAcquireVpbSpinLock( &SavedIrql );
3565
3566 if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
3567 (Vcb->Vpb->ReferenceCount <= 2 + RemainingUserReferences) &&
3568 (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))) {
3569
3571 SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED);
3572 Vcb->FileObjectWithVcbLocked = FileObject;
3573
3574 } else {
3575
3577 }
3578
3579 IoReleaseVpbSpinLock( SavedIrql );
3580
3581 //
3582 // If we successully locked the volume, see if it is clean now.
3583 //
3584
3585 if (NT_SUCCESS( Status ) &&
3586 FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
3587 !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
3588 !CcIsThereDirtyData(Vcb->Vpb)) {
3589
3590 FatMarkVolume( IrpContext, Vcb, VolumeClean );
3592 }
3593
3594 NT_ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 )));
3595
3596 return Status;
3597}
3598
3599
3602 IN PIRP_CONTEXT IrpContext,
3603 IN PVCB Vcb,
3605 )
3606
3607/*++
3608
3609Routine Description:
3610
3611 This routine performs the actual unlock volume operation.
3612
3613 The volume must be held exclusive by the caller.
3614
3615Arguments:
3616
3617 Vcb - The volume being locked.
3618
3619 FileObject - File corresponding to the handle locking the volume. If this
3620 is not specified, a system lock is assumed.
3621
3622Return Value:
3623
3624 NTSTATUS - The return status for the operation
3625
3626 Attempting to remove a system lock that did not exist is OK.
3627
3628--*/
3629
3630{
3631 KIRQL SavedIrql;
3633
3634 UNREFERENCED_PARAMETER( IrpContext );
3635
3636 IoAcquireVpbSpinLock( &SavedIrql );
3637
3638 if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && FileObject == Vcb->FileObjectWithVcbLocked) {
3639
3640 //
3641 // This one locked it, unlock the volume
3642 //
3643
3645 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED );
3646 Vcb->FileObjectWithVcbLocked = NULL;
3647
3649 }
3650
3651 IoReleaseVpbSpinLock( SavedIrql );
3652
3653 return Status;
3654}
3655
3656
3657//
3658// Local Support Routine
3659//
3660
3661_Requires_lock_held_(_Global_critical_region_)
3663FatDismountVolume (
3664 IN PIRP_CONTEXT IrpContext,
3665 IN PIRP Irp
3666 )
3667
3668/*++
3669
3670Routine Description:
3671
3672 This routine performs the dismount volume operation. It is responsible for
3673 either completing of enqueuing the input Irp.
3674
3675Arguments:
3676
3677 Irp - Supplies the Irp to process
3678
3679Return Value:
3680
3681 NTSTATUS - The return status for the operation
3682
3683--*/
3684
3685{
3688 BOOLEAN VcbHeld = FALSE;
3689 KIRQL SavedIrql;
3690
3691 PVCB Vcb;
3692 PFCB Fcb;
3693 PCCB Ccb;
3694
3696
3697 DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0);
3698
3699 //
3700 // Decode the file object, the only type of opens we accept are
3701 // user volume opens on media that is not boot/paging and is not
3702 // already dismounted ... (but we need to check that stuff while
3703 // synchronized)
3704 //
3705
3707
3709 goto fn_return;
3710 }
3711
3713
3715
3716 DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3718 }
3719
3720 //
3721 // Make some unsynchronized checks to see if this operation is possible.
3722 // We will repeat the appropriate ones inside synchronization, but it is
3723 // good to avoid bogus notifications.
3724 //
3725
3726 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE )) {
3727
3729 goto fn_return;
3730 }
3731
3732 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3733
3735 goto fn_return;
3736 }
3737
3738 //
3739 // A bit of historical comment is in order.
3740 //
3741 // In all versions prior to NT5, we only permitted dismount if the volume had
3742 // previously been locked. Now we must permit a forced dismount, meaning that
3743 // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open
3744 // handles, etc. - to flush and invalidate the volume.
3745 //
3746 // Previously, dismount assumed that lock had come along earlier and done some
3747 // of the work that we are now going to do - i.e., flush, tear down the eas. All
3748 // we had to do here is flush the device out and kill off as many of the orphan
3749 // fcbs as possible. This now changes.
3750 //
3751 // In fact, everything is a forced dismount now. This changes one interesting
3752 // aspect, which is that it used to be the case that the handle used to dismount
3753 // could come back, read, and induce a verify/remount. This is just not possible
3754 // now. The point of forced dismount is that very shortly someone will come along
3755 // and be destructive to the possibility of using the media further - format, eject,
3756 // etc. By using this path, callers are expected to tolerate the consequences.
3757 //
3758 // Note that the volume can still be successfully unlocked by this handle.
3759 //
3760
3761 //
3762 // Send notification.
3763 //
3764
3766
3767 //
3768 // Force ourselves to wait and grab everything.
3769 //
3770
3771 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
3772
3773#ifdef _MSC_VER
3774#pragma prefast( push )
3775#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
3776#pragma prefast( disable: 28193, "this will always wait" )
3777#endif
3778
3779 (VOID)FatAcquireExclusiveGlobal( IrpContext );
3780
3781#ifdef _MSC_VER
3782#pragma prefast( pop )
3783#endif
3784
3785 _SEH2_TRY {
3786
3787 //
3788 // Guess what? This can raise if a cleanup on the fileobject we
3789 // got races in ahead of us.
3790 //
3791
3792 FatAcquireExclusiveVolume( IrpContext, Vcb );
3793 VcbHeld = TRUE;
3794
3795 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3796
3798 }
3799
3800 FatFlushAndCleanVolume( IrpContext, Irp, Vcb, FlushAndInvalidate );
3801
3802 //
3803 // We defer the physical dismount until this handle is closed, per symmetric
3804 // implemntation in the other FS. This permits a dismounter to issue IOCTL
3805 // through this handle and perform device manipulation without racing with
3806 // creates attempting to mount the volume again.
3807 //
3808 // Raise a flag to tell the cleanup path to complete the dismount.
3809 //
3810
3812
3813 //
3814 // Indicate that the volume was dismounted so that we may return the
3815 // correct error code when operations are attempted via open handles.
3816 //
3817
3819
3821
3822 //
3823 // Set a flag in the VPB to let others know that direct volume access is allowed.
3824 //
3825
3826 IoAcquireVpbSpinLock( &SavedIrql );
3827 SetFlag( Vcb->Vpb->Flags, VPB_DIRECT_WRITES_ALLOWED );
3828 IoReleaseVpbSpinLock( SavedIrql );
3829
3831
3832 try_exit: NOTHING;
3833
3834 } _SEH2_FINALLY {
3835
3836#if (NTDDI_VERSION >= NTDDI_WIN8)
3837
3838 FsRtlDismountComplete( Vcb->TargetDeviceObject, Status );
3839
3840#endif
3841
3842 if (VcbHeld) {
3843
3844 FatReleaseVolume( IrpContext, Vcb );
3845 }
3846
3847 FatReleaseGlobal( IrpContext );
3848
3849 //
3850 // I do not believe it is possible to raise, but for completeness
3851 // notice and send notification of failure. We absolutely
3852 // cannot have raised in CheckForDismount.
3853 //
3854 // We decline to call an attempt to dismount a dismounted volume
3855 // a failure to do so.
3856 //
3857
3860
3862 }
3863 } _SEH2_END;
3864
3865 fn_return:
3866
3867 FatCompleteRequest( IrpContext, Irp, Status );
3868 DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", Status);
3869 return Status;
3870}
3871
3872
3873//
3874// Local Support Routine
3875//
3876
3877_Requires_lock_held_(_Global_critical_region_)
3879FatDirtyVolume (
3880 IN PIRP_CONTEXT IrpContext,
3881 IN PIRP Irp
3882 )
3883
3884/*++
3885
3886Routine Description:
3887
3888 This routine marks the volume as dirty.
3889
3890Arguments:
3891
3892 Irp - Supplies the Irp to process
3893
3894Return Value:
3895
3896 NTSTATUS - The return status for the operation
3897
3898--*/
3899
3900{
3902
3903 PVCB Vcb;
3904 PFCB Fcb;
3905 PCCB Ccb;
3906
3907 PAGED_CODE();
3908
3910
3911 DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0);
3912
3913 //
3914 // Decode the file object, the only type of opens we accept are
3915 // user volume opens.
3916 //
3917
3919
3921
3922 DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3924 }
3925
3927
3929
3930 DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3932 }
3933
3934
3935 //
3936 // Disable popups, we will just return any error.
3937 //
3938
3939 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
3940
3941 //
3942 // Verify the Vcb. We want to make sure we don't dirty some
3943 // random chunk of media that happens to be in the drive now.
3944 //
3945
3946 FatVerifyVcb( IrpContext, Vcb );
3947
3949
3950 FatMarkVolume( IrpContext, Vcb, VolumeDirty );
3951
3952 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3953
3954 DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
3955
3956 return STATUS_SUCCESS;
3957}
3958
3959
3960//
3961// Local Support Routine
3962//
3963
3966 IN PIRP_CONTEXT IrpContext,
3967 IN PIRP Irp
3968 )
3969
3970/*++
3971
3972Routine Description:
3973
3974 This routine determines if a volume is currently dirty.
3975
3976Arguments:
3977
3978 Irp - Supplies the Irp to process
3979
3980Return Value:
3981
3982 NTSTATUS - The return status for the operation
3983
3984--*/
3985
3986{
3988
3990 PVCB Vcb;
3991 PFCB Fcb;
3992 PCCB Ccb;
3993
3995
3996 PAGED_CODE();
3997
3998 //
3999 // Get the current stack location and extract the output
4000 // buffer information.
4001 //
4002
4004
4005 //
4006 // Get a pointer to the output buffer. Look at the system buffer field in the
4007 // irp first. Then the Irp Mdl.
4008 //
4009
4010 if (Irp->AssociatedIrp.SystemBuffer != NULL) {
4011
4012 VolumeState = Irp->AssociatedIrp.SystemBuffer;
4013
4014 } else if (Irp->MdlAddress != NULL) {
4015
4016 VolumeState = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, LowPagePriority | MdlMappingNoExecute );
4017
4018 if (VolumeState == NULL) {
4019
4022 }
4023
4024 } else {
4025
4028 }
4029
4030 //
4031 // Make sure the output buffer is large enough and then initialize
4032 // the answer to be that the volume isn't dirty.
4033 //
4034
4035 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
4036
4039 }
4040
4041 *VolumeState = 0;
4042
4043 //
4044 // Decode the file object
4045 //
4046
4048
4049 if (TypeOfOpen != UserVolumeOpen) {
4050
4053 }
4054
4055 if (Vcb->VcbCondition != VcbGood) {
4056
4059 }
4060
4061 //
4062 // Disable PopUps, we want to return any error.
4063 //
4064
4065 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
4066
4067 //
4068 // Verify the Vcb. We want to make double sure that this volume
4069 // is around so that we know our information is good.
4070 //
4071
4072 FatVerifyVcb( IrpContext, Vcb );
4073
4074 //
4075 // Now set the returned information. We can avoid probing the disk since
4076 // we know our internal state is in sync.
4077 //
4078
4079 if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY) ) {
4080
4082 }
4083
4084 Irp->IoStatus.Information = sizeof( ULONG );
4085
4086 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4087 return STATUS_SUCCESS;
4088}
4089
4090
4091//
4092// Local Support Routine
4093//
4094
4097 IN PIRP_CONTEXT IrpContext,
4098 IN PIRP Irp
4099 )
4100
4101/*++
4102
4103Routine Description:
4104
4105 This routine determines if a volume is currently mounted.
4106
4107Arguments:
4108
4109 Irp - Supplies the Irp to process
4110
4111Return Value:
4112
4113 NTSTATUS - The return status for the operation
4114
4115--*/
4116
4117{
4119
4121
4122 PVCB Vcb = NULL;
4123 PFCB Fcb;
4124 PCCB Ccb;
4125
4126 PAGED_CODE();
4127
4129
4131
4132 DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0);
4133
4134 //
4135 // Decode the file object.
4136 //
4137
4139
4140 NT_ASSERT( Vcb != NULL );
4142
4143 //
4144 // Disable PopUps, we want to return any error.
4145 //
4146
4147 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
4148
4149 //
4150 // Verify the Vcb.
4151 //
4152
4153 FatVerifyVcb( IrpContext, Vcb );
4154
4155 FatCompleteRequest( IrpContext, Irp, Status );
4156
4157 DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status);
4158
4159 return Status;
4160}
4161
4162
4163//
4164// Local Support Routine
4165//
4166
4169 IN PIRP_CONTEXT IrpContext,
4170 IN PIRP Irp
4171 )
4172
4173/*++
4174
4175Routine Description:
4176
4177 This routine determines if a pathname is a-priori illegal by inspecting
4178 the the characters used. It is required to be correct on a FALSE return.
4179
4180 N.B.: current implementation is intentioanlly a no-op. This may change
4181 in the future. A careful reader of the previous implementation of this
4182 FSCTL in FAT would discover that it violated the requirement stated above
4183 and could return FALSE for a valid (createable) pathname.
4184
4185Arguments:
4186
4187 Irp - Supplies the Irp to process
4188
4189Return Value:
4190
4191 NTSTATUS - The return status for the operation
4192
4193--*/
4194
4195{
4196 PAGED_CODE();
4197
4198 DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0);
4199
4200 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4201
4202 DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS);
4203
4204 return STATUS_SUCCESS;
4205}
4206
4207
4208//
4209// Local Support Routine
4210//
4211
4214 IN PIRP_CONTEXT IrpContext,
4215 IN PIRP Irp
4216 )
4217
4218/*++
4219
4220Routine Description:
4221
4222 This routine simply returns the first 0x24 bytes of sector 0.
4223
4224Arguments:
4225
4226 Irp - Supplies the Irp to process
4227
4228Return Value:
4229
4230 NTSTATUS - The return status for the operation
4231
4232--*/
4233
4234{
4236
4237 PVCB Vcb;
4238
4240
4241 PAGED_CODE();
4242
4244
4245 DebugTrace(+1, Dbg, "FatQueryBpb...\n", 0);
4246
4247 //
4248 // Get the Vcb. If we didn't keep the information needed for this call,
4249 // we had a reason ...
4250 //
4251
4253
4254 if (Vcb->First0x24BytesOfBootSector == NULL) {
4255
4257 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST );
4259 }
4260
4261 //
4262 // Extract the buffer
4263 //
4264
4265 BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer;
4266
4267 //
4268 // Make sure the buffer is big enough.
4269 //
4270
4271 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) {
4272
4274 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
4276 }
4277
4278 //
4279 // Fill in the output buffer
4280 //
4281
4283 Vcb->First0x24BytesOfBootSector,
4284 0x24 );
4285
4286 Irp->IoStatus.Information = 0x24;
4287
4288 FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4289 DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS);
4290 return STATUS_SUCCESS;
4291}
4292
4293
4294//
4295// Local Support Routine
4296//
4297
4298_Requires_lock_held_(_Global_critical_region_)
4300FatInvalidateVolumes (
4301 IN PIRP Irp
4302 )
4303
4304/*++
4305
4306Routine Description:
4307
4308 This routine searches for all the volumes mounted on the same real device
4309 of the current DASD handle, and marks them all bad. The only operation
4310 that can be done on such handles is cleanup and close.
4311
4312Arguments:
4313
4314 Irp - Supplies the Irp to process
4315
4316Return Value:
4317
4318 NTSTATUS - The return status for the operation
4319
4320--*/
4321
4322{
4324 IRP_CONTEXT IrpContext;
4326
4327 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
4328
4329 HANDLE Handle;
4330
4331 PLIST_ENTRY Links;
4332
4333 PFILE_OBJECT FileToMarkBad;
4334 PDEVICE_OBJECT DeviceToMarkBad;
4335
4337
4338 DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0);
4339
4340 //
4341 // Check for the correct security access.
4342 // The caller must have the SeTcbPrivilege.
4343 //
4344
4345 if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) {
4346
4348
4349 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD);
4351 }
4352
4353 //
4354 // Try to get a pointer to the device object from the handle passed in.
4355 //
4356
4357#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4358 if (IoIs32bitProcess( Irp )) {
4359
4360 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) {
4361
4363
4364 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
4366 }
4367
4368 Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) );
4369 } else {
4370#endif
4371 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) {
4372
4374
4375 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
4377 }
4378
4379 Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
4380#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4381 }
4382#endif
4383
4384
4386 0,
4388 KernelMode,
4389#ifndef __REACTOS__
4390 &FileToMarkBad,
4391#else
4392 (PVOID *)&FileToMarkBad,
4393#endif
4394 NULL );
4395
4396 if (!NT_SUCCESS(Status)) {
4397
4399
4400 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status);
4401 return Status;
4402
4403 } else {
4404
4405 //
4406 // We only needed the pointer, not a reference.
4407 //
4408
4409 ObDereferenceObject( FileToMarkBad );
4410
4411 //
4412 // Grab the DeviceObject from the FileObject.
4413 //
4414
4415 DeviceToMarkBad = FileToMarkBad->DeviceObject;
4416 }
4417
4418 RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
4419
4420 SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
4421 IrpContext.MajorFunction = IrpSp->MajorFunction;
4422 IrpContext.MinorFunction = IrpSp->MinorFunction;
4423
4424#ifdef _MSC_VER
4425#pragma prefast( push )
4426#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
4427#pragma prefast( disable: 28193, "this will always wait" )
4428#endif
4429
4430 FatAcquireExclusiveGlobal( &IrpContext );
4431
4432#ifdef _MSC_VER
4433#pragma prefast( pop )
4434#endif
4435
4436 //
4437 // First acquire the FatData resource shared, then walk through all the
4438 // mounted VCBs looking for candidates to mark BAD.
4439 //
4440 // On volumes we mark bad, check for dismount possibility (which is
4441 // why we have to get the next link early).
4442 //
4443
4444 Links = FatData.VcbQueue.Flink;
4445
4446 while (Links != &FatData.VcbQueue) {
4447
4448 PVCB ExistingVcb;
4449
4450 ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
4451
4452 Links = Links->Flink;
4453
4454 //
4455 // If we get a match, mark the volume Bad, and also check to
4456 // see if the volume should go away.
4457 //
4458
4459 if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) {
4460
4461 BOOLEAN VcbDeleted = FALSE;
4462
4463 //
4464 // Here we acquire the Vcb exclusive and try to purge
4465 // all the open files. The idea is to have as little as
4466 // possible stale data visible and to hasten the volume
4467 // going away.
4468 //
4469
4470 (VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb );
4471
4472#ifdef _MSC_VER
4473#pragma prefast( push )
4474#pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
4475#endif
4476
4477 if (ExistingVcb->Vpb == DeviceToMarkBad->Vpb) {
4478
4479 KIRQL OldIrql;
4480
4482
4483 if (FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_MOUNTED )) {
4484
4485 PVPB NewVpb;
4486
4487 NewVpb = ExistingVcb->SwapVpb;
4488 ExistingVcb->SwapVpb = NULL;
4490
4491 RtlZeroMemory( NewVpb, sizeof( VPB ) );
4492 NewVpb->Type = IO_TYPE_VPB;
4493 NewVpb->Size = sizeof( VPB );
4494 NewVpb->RealDevice = DeviceToMarkBad;
4495 NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING );
4496
4497 DeviceToMarkBad->Vpb = NewVpb;
4498 }
4499
4500 NT_ASSERT( DeviceToMarkBad->Vpb->DeviceObject == NULL );
4501
4502#ifdef _MSC_VER
4503#pragma prefast( pop )
4504#endif
4505
4507 }
4508
4509 FatSetVcbCondition( ExistingVcb, VcbBad );
4510
4511 //
4512 // Process the root directory, if it is present.
4513 //
4514
4515 if (ExistingVcb->RootDcb != NULL) {
4516
4517 //
4518 // In order to safely mark all FCBs bad, we must acquire everything.
4519 //
4520
4521 FatAcquireExclusiveVolume(&IrpContext, ExistingVcb);
4522 FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad, TRUE );
4523 FatReleaseVolume(&IrpContext, ExistingVcb);
4524
4525 //
4526 // Purging the file objects on this volume could result in the memory manager
4527 // dereferencing it's file pointer which could be the last reference and
4528 // trigger object deletion and VCB deletion. Protect against that here by
4529 // temporarily biasing the file count, and later checking for dismount.
4530 //
4531
4532 ExistingVcb->OpenFileCount += 1;
4533
4534 FatPurgeReferencedFileObjects( &IrpContext,
4535 ExistingVcb->RootDcb,
4536 NoFlush );
4537
4538 ExistingVcb->OpenFileCount -= 1;
4539
4540 VcbDeleted = FatCheckForDismount( &IrpContext, ExistingVcb, FALSE );
4541 }
4542
4543 //
4544 // Only release the VCB if it did not go away.
4545 //
4546
4547 if (!VcbDeleted) {
4548
4549 FatReleaseVcb( &IrpContext, ExistingVcb );
4550 }
4551 }
4552 }
4553
4554 FatReleaseGlobal( &IrpContext );
4555
4557
4558 DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
4559
4560 return STATUS_SUCCESS;
4561}
4562
4563
4564//
4565// Local Support routine
4566//
4567
4568BOOLEAN
4570 IN PIRP_CONTEXT IrpContext,
4571 IN PVCB Vcb,
4572 IN PVOID Buffer,
4573 IN LBO Lbo,
4574 IN ULONG NumberOfBytesToRead,
4575 IN BOOLEAN ReturnOnError
4576 )
4577
4578/*++
4579
4580Routine Description:
4581
4582 This routine is used to read in a range of bytes from the disk. It
4583 bypasses all of the caching and regular I/O logic, and builds and issues
4584 the requests itself. It does this operation overriding the verify
4585 volume flag in the device object.
4586
4587Arguments:
4588
4589 Vcb - Supplies the target device object for this operation.
4590
4591 Buffer - Supplies the buffer that will recieve the results of this operation
4592
4593 Lbo - Supplies the byte offset of where to start reading
4594
4595 NumberOfBytesToRead - Supplies the number of bytes to read, this must
4596 be in multiple of bytes units acceptable to the disk driver.
4597
4598 ReturnOnError - Indicates that we should return on an error, instead
4599 of raising.
4600
4601Return Value:
4602
4603 BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
4604
4605--*/
4606
4607{
4608 KEVENT Event;
4609 PIRP Irp;
4613
4614 PAGED_CODE();
4615
4616 DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo );
4617
4618 //
4619 // Initialize the event we're going to use
4620 //
4621
4623
4624 //
4625 // Build the irp for the operation and also set the overrride flag
4626 //
4627
4628 ByteOffset.QuadPart = Lbo;
4629
4631 Vcb->TargetDeviceObject,
4632 Buffer,
4633 NumberOfBytesToRead,
4634 &ByteOffset,
4635 &Event,
4636 &Iosb );
4637
4638 if ( Irp == NULL ) {
4639
4641 }
4642
4644
4645 //
4646 // Call the device to do the read and wait for it to finish.
4647 //
4648
4649 Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
4650
4651 if (Status == STATUS_PENDING) {
4652
4654
4655 Status = Iosb.Status;
4656 }
4657
4659
4660 //
4661 // Special case this error code because this probably means we used
4662 // the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
4663 //
4664
4666
4667 return FALSE;
4668 }
4669
4670 //
4671 // If it doesn't succeed then either return or raise the error.
4672 //
4673
4674 if (!NT_SUCCESS(Status)) {
4675
4676 if (ReturnOnError) {
4677
4678 return FALSE;
4679
4680 } else {
4681
4682 FatNormalizeAndRaiseStatus( IrpContext, Status );
4683 }
4684 }
4685
4686 //
4687 // And return to our caller
4688 //
4689
4690 return TRUE;
4691}
4692
4693
4694//
4695// Local Support Routine
4696//
4697
4698_Requires_lock_held_(_Global_critical_region_)
4700FatQueryRetrievalPointers (
4701 IN PIRP_CONTEXT IrpContext,
4702 IN PIRP Irp
4703 )
4704
4705/*++
4706
4707Routine Description:
4708
4709 This routine performs the query retrieval pointers operation.
4710 It returns the retrieval pointers for the specified input
4711 file from the start of the file to the request map size specified
4712 in the input buffer.
4713
4714Arguments:
4715
4716 Irp - Supplies the Irp to process
4717
4718Return Value:
4719
4720 NTSTATUS - The return status for the operation
4721
4722--*/
4723
4724{
4726
4728
4729 PVCB Vcb;
4730 PFCB Fcb;
4731 PCCB Ccb;
4732
4733 PLARGE_INTEGER RequestedMapSize;
4734 PLARGE_INTEGER *MappingPairs;
4735
4736 ULONG Index;
4737 ULONG i;
4739 LBO Lbo;
4740 ULONG Vbo;
4741 ULONG MapSize;
4743
4744 PAGED_CODE();
4745
4746 //
4747 // Get the current stack location
4748 //
4749
4751
4752 //
4753 // Make this a synchronous IRP because we need access to the input buffer and
4754 // this Irp is marked METHOD_NEITHER.
4755 //
4756
4757 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
4758
4759 //
4760 // Decode the file object and ensure that it is the paging file
4761 //
4762 // Only Kernel mode clients may query retrieval pointer information about
4763 // a file. Ensure that this is the case for this caller.
4764 //
4765
4767
4768 if (Irp->RequestorMode != KernelMode ||
4769 Fcb == NULL ||
4771
4774 }
4775
4776 //
4777 // Extract the input and output buffer information. The input contains
4778 // the requested size of the mappings in terms of VBO. The output
4779 // parameter will receive a pointer to nonpaged pool where the mapping
4780 // pairs are stored.
4781 //
4782
4783 NT_ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) );
4784 NT_ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) );
4785
4786 RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
4787 MappingPairs = Irp->UserBuffer;
4788
4789 //
4790 // Acquire exclusive access to the Fcb
4791 //
4792
4793 FatAcquireExclusiveFcb( IrpContext, Fcb );
4794
4795 _SEH2_TRY {
4796
4797 //
4798 // Verify the Fcb is still OK
4799 //
4800
4801 FatVerifyFcb( IrpContext, Fcb );
4802
4803 //
4804 // Check if the mapping the caller requested is too large
4805 //
4806
4807 if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) {
4808
4810 }
4811
4812 //
4813 // Now get the index for the mcb entry that will contain the
4814 // callers request and allocate enough pool to hold the
4815 // output mapping pairs. Mapping should always be present, but handle
4816 // the case where it isn't.
4817 //
4818
4820 &Fcb->Mcb,
4821 RequestedMapSize->LowPart - 1,
4822 &Lbo,
4823 NULL,
4824 &Index );
4825
4826 if (!Result) {
4827
4830 }
4831
4832 *MappingPairs = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
4833 (Index + 2) * (2 * sizeof(LARGE_INTEGER)),
4835
4836 //
4837 // Now copy over the mapping pairs from the mcb
4838 // to the output buffer. We store in [sector count, lbo]
4839 // mapping pairs and end with a zero sector count.
4840 //
4841
4842 MapSize = RequestedMapSize->LowPart;
4843
4844 for (i = 0; i <= Index; i += 1) {
4845
4847
4848 if (SectorCount > MapSize) {
4849 SectorCount = MapSize;
4850 }
4851
4852 (*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount;
4853 (*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo;
4854
4855 MapSize -= SectorCount;
4856 }
4857
4858 (*MappingPairs)[ i*2 + 0 ].QuadPart = 0;
4859
4861 }
4863
4864 DebugUnwind( FatQueryRetrievalPointers );
4865
4866 //
4867 // Release all of our resources
4868 //
4869
4870 FatReleaseFcb( IrpContext, Fcb );
4871
4872 //
4873 // If this is an abnormal termination then undo our work, otherwise
4874 // complete the irp
4875 //
4876
4878
4879 FatCompleteRequest( IrpContext, Irp, Status );
4880 }
4881 } _SEH2_END;
4882
4883 return Status;
4884}
4885
4886
4887//
4888// Local Support Routine
4889//
4890
4893 IN PIRP_CONTEXT IrpContext,
4894 IN PIRP Irp
4895 )
4896
4897/*++
4898
4899Routine Description:
4900
4901 This routine returns the filesystem performance counters from the
4902 appropriate VCB.
4903
4904Arguments:
4905
4906 Irp - Supplies the Irp to process
4907
4908Return Value:
4909
4910 NTSTATUS - The return status for the operation
4911
4912--*/
4913
4914{
4917 PVCB Vcb;
4918
4921 ULONG StatsSize;
4923
4924 PAGED_CODE();
4925
4927
4928 DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0);
4929
4930 //
4931 // Extract the buffer
4932 //
4933
4934 BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
4935
4936 //
4937 // Get a pointer to the output buffer.
4938 //
4939
4940 Buffer = Irp->AssociatedIrp.SystemBuffer;
4941
4942 //
4943 // Make sure the buffer is big enough for at least the common part.
4944 //
4945
4946 if (BufferLength < sizeof(FILESYSTEM_STATISTICS)) {
4947
4949
4950 DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
4951
4953 }
4954
4955 //
4956 // Now see how many bytes we can copy.
4957 //
4958
4959 StatsSize = sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors;
4960
4961 if (BufferLength < StatsSize) {
4962
4965
4966 } else {
4967
4968 BytesToCopy = StatsSize;
4970 }
4971
4972 //
4973 // Get the Vcb.
4974 //
4975
4977
4978 //
4979 // Fill in the output buffer
4980 //
4981
4982 RtlCopyMemory( Buffer, Vcb->Statistics, BytesToCopy );
4983
4984 Irp->IoStatus.Information = BytesToCopy;
4985
4986 FatCompleteRequest( IrpContext, Irp, Status );
4987
4988 DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", Status);
4989
4990 return Status;
4991}
4992
4993//
4994// Local Support Routine
4995//
4996
4997_Requires_lock_held_(_Global_critical_region_)
4999FatGetVolumeBitmap(
5000 IN PIRP_CONTEXT IrpContext,
5001 IN PIRP Irp
5002 )
5003
5004/*++
5005
5006Routine Description:
5007
5008 This routine returns the volume allocation bitmap.
5009
5010 Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
5011 through the input buffer.
5012 Output = the VOLUME_BITMAP_BUFFER data structure is returned through
5013 the output buffer.
5014
5015 We return as much as the user buffer allows starting the specified input
5016 LCN (trucated to a byte). If there is no input buffer, we start at zero.
5017
5018Arguments:
5019
5020 Irp - Supplies the Irp being processed.
5021
5022Return Value:
5023
5024 NTSTATUS - The return status for the operation.
5025
5026--*/
5027{
5030
5031 PVCB Vcb;
5032 PFCB Fcb;
5033 PCCB Ccb;
5034
5036 ULONG TotalClusters;
5037 ULONG DesiredClusters;
5038 ULONG StartingCluster;
5039 ULONG EndingCluster;
5042 LARGE_INTEGER StartingLcn;
5044
5045 PAGED_CODE();
5046
5047 //
5048 // Get the current Irp stack location and save some references.
5049 //
5050
5052
5053 DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
5054 IrpSp->Parameters.FileSystemControl.FsControlCode);
5055
5056 //
5057 // Make this a synchronous IRP because we need access to the input buffer and
5058 // this Irp is marked METHOD_NEITHER.
5059 //
5060
5061 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
5062
5063 //
5064 // Extract and decode the file object and check for type of open.
5065 //
5066
5068
5071 }
5072
5074
5076
5077 DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER);
5079 }
5080
5083
5085
5086 //
5087 // Check for a minimum length on the input and output buffers.
5088 //
5089
5092
5095 }
5096
5097 //
5098 // Check if a starting cluster was specified.
5099 //
5100
5101 TotalClusters = Vcb->AllocationSupport.NumberOfClusters;
5102
5103 //
5104 // Check for valid buffers
5105 //
5106
5107 _SEH2_TRY {
5108
5109 if (Irp->RequestorMode != KernelMode) {
5110
5113 sizeof(UCHAR) );
5114
5116 }
5117
5118 StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn;
5119
5121
5123
5124 FatRaiseStatus( IrpContext,
5127 } _SEH2_END;
5128
5129 if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) {
5130
5133
5134 } else {
5135
5136 StartingCluster = StartingLcn.LowPart & ~7;
5137 }
5138
5139 (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
5140
5141 //
5142 // Only return what will fit in the user buffer.
5143 //
5144
5146 DesiredClusters = TotalClusters - StartingCluster;
5147
5148 if (OutputBufferLength < (DesiredClusters + 7) / 8) {
5149
5152
5153 } else {
5154
5155 BytesToCopy = (DesiredClusters + 7) / 8;
5157 }
5158
5159 //
5160 // Use try/finally for cleanup.
5161 //
5162
5163 _SEH2_TRY {
5164
5165 _SEH2_TRY {
5166
5167 //
5168 // Verify the Vcb is still OK
5169 //
5170
5171 FatQuickVerifyVcb( IrpContext, Vcb );
5172
5173 //
5174 // Fill in the fixed part of the output buffer
5175 //
5176
5177 OutputBuffer->StartingLcn.QuadPart = StartingCluster;
5178 OutputBuffer->BitmapSize.QuadPart = DesiredClusters;
5179
5180 if (Vcb->NumberOfWindows == 1) {
5181
5182 //
5183 // Just copy the volume bitmap into the user buffer.
5184 //
5185
5186 NT_ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL );
5187
5188 RtlCopyMemory( &OutputBuffer->Buffer[0],
5189 (PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8,
5190 BytesToCopy );
5191 } else {
5192
5193 //
5194 // Call out to analyze the FAT. We must bias by two to account for
5195 // the zero base of this API and FAT's physical reality of starting
5196 // the file heap at cluster 2.
5197 //
5198 // Note that the end index is inclusive - we need to subtract one to
5199 // calculcate it.
5200 //
5201 // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster
5202 // of 2 and end cluster of 9, a run of eight clusters.
5203 //
5204
5205 EndingCluster = StartingCluster + (BytesToCopy * 8);
5206
5207 //
5208 // Make sure we do not read past the end of the entries.
5209 //
5210
5211 if (EndingCluster > TotalClusters) {
5212
5213 EndingCluster = TotalClusters;
5214 }
5215
5216 FatExamineFatEntries( IrpContext,
5217 Vcb,
5218 StartingCluster + 2,
5219 EndingCluster + 2 - 1,
5220 FALSE,
5221 NULL,
5222 (PULONG)&OutputBuffer->Buffer[0] );
5223 }
5224
5226
5228
5229 FatRaiseStatus( IrpContext,
5232 } _SEH2_END;
5233
5234 } _SEH2_FINALLY {
5235
5236 FatReleaseVcb( IrpContext, Vcb );
5237 } _SEH2_END;
5238
5239 Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) +
5241
5242 FatCompleteRequest( IrpContext, Irp, Status );
5243
5244 DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0);
5245
5246 return Status;
5247}
5248
5249
5250//
5251// Local Support Routine
5252//
5253
5254_Requires_lock_held_(_Global_critical_region_)
5256FatGetRetrievalPointers (
5257 IN PIRP_CONTEXT IrpContext,
5258 IN PIRP Irp
5259 )
5260
5261/*++
5262
5263Routine Description:
5264
5265 This routine scans the MCB and builds an extent list. The first run in
5266 the output extent list will start at the begining of the contiguous
5267 run specified by the input parameter.
5268
5269 Input = STARTING_VCN_INPUT_BUFFER;
5270 Output = RETRIEVAL_POINTERS_BUFFER.
5271
5272Arguments:
5273
5274 Irp - Supplies the Irp being processed.
5275
5276Return Value:
5277
5278 NTSTATUS - The return status for the operation.
5279
5280--*/
5281{
5284
5285 PVCB Vcb;
5286 PFCB FcbOrDcb;
5287 PCCB Ccb;
5288 PLARGE_MCB McbToUse = NULL;
5290
5291 ULONG Index;
5292 ULONG ClusterShift = 0;
5295
5296 ULONG Run;
5297 ULONG RunCount;
5298 ULONG StartingRun;