4 Copyright (c) 1989-2000 Microsoft Corporation
12 This module implements the File System Control routines for Fat called
13 by the dispatch driver.
21 // The Bug check file id for this module
24 #define BugCheckFileId (FAT_BUG_CHECK_FSCTRL)
27 // The local debug trace level
30 #define Dbg (DEBUG_TRACE_FSCTRL)
33 // Local procedure prototypes
36 _Requires_lock_held_(_Global_critical_region_
)
39 IN PIRP_CONTEXT IrpContext
,
40 IN PDEVICE_OBJECT TargetDeviceObject
,
42 IN PDEVICE_OBJECT FsDeviceObject
45 _Requires_lock_held_(_Global_critical_region_
)
48 IN PIRP_CONTEXT IrpContext
,
53 FatIsMediaWriteProtected (
54 IN PIRP_CONTEXT IrpContext
,
55 IN PDEVICE_OBJECT TargetDeviceObject
58 _Requires_lock_held_(_Global_critical_region_
)
61 IN PIRP_CONTEXT IrpContext
,
65 _Requires_lock_held_(_Global_critical_region_
)
68 _In_ PIRP_CONTEXT IrpContext
,
72 _Requires_lock_held_(_Global_critical_region_
)
75 IN PIRP_CONTEXT IrpContext
,
81 IN PIRP_CONTEXT IrpContext
,
85 _Requires_lock_held_(_Global_critical_region_
)
88 IN PIRP_CONTEXT IrpContext
,
92 _Requires_lock_held_(_Global_critical_region_
)
95 IN PIRP_CONTEXT IrpContext
,
101 IN PIRP_CONTEXT IrpContext
,
107 IN PIRP_CONTEXT IrpContext
,
113 IN PIRP_CONTEXT IrpContext
,
117 _Requires_lock_held_(_Global_critical_region_
)
119 FatInvalidateVolumes (
123 _Requires_lock_held_(_Global_critical_region_
)
125 FatScanForDismountedVcb (
126 IN PIRP_CONTEXT IrpContext
130 FatPerformVerifyDiskRead (
131 IN PIRP_CONTEXT IrpContext
,
135 IN ULONG NumberOfBytesToRead
,
136 IN BOOLEAN ReturnOnError
139 _Requires_lock_held_(_Global_critical_region_
)
141 FatQueryRetrievalPointers (
142 IN PIRP_CONTEXT IrpContext
,
148 IN PIRP_CONTEXT IrpContext
,
154 IN PIRP_CONTEXT IrpContext
,
159 FatAllowExtendedDasdIo (
160 IN PIRP_CONTEXT IrpContext
,
164 _Requires_lock_held_(_Global_critical_region_
)
167 _In_ PIRP_CONTEXT IrpContext
,
171 _Requires_lock_held_(_Global_critical_region_
)
173 FatGetRetrievalPointerBase (
174 _In_ PIRP_CONTEXT IrpContext
,
178 _Requires_lock_held_(_Global_critical_region_
)
181 _In_ PIRP_CONTEXT IrpContext
,
186 FatSetZeroOnDeallocate (
187 __in PIRP_CONTEXT IrpContext
,
191 _Requires_lock_held_(_Global_critical_region_
)
193 FatSetPurgeFailureMode (
194 _In_ PIRP_CONTEXT IrpContext
,
199 // Local support routine prototypes
202 _Requires_lock_held_(_Global_critical_region_
)
205 IN PIRP_CONTEXT IrpContext
,
209 _Requires_lock_held_(_Global_critical_region_
)
211 FatGetRetrievalPointers (
212 IN PIRP_CONTEXT IrpContext
,
216 _Requires_lock_held_(_Global_critical_region_
)
218 FatMoveFileNeedsWriteThrough (
219 _In_ PIRP_CONTEXT IrpContext
,
221 _In_ ULONG OldWriteThroughFlags
224 _Requires_lock_held_(_Global_critical_region_
)
227 IN PIRP_CONTEXT IrpContext
,
232 FatComputeMoveFileSplicePoints (
233 PIRP_CONTEXT IrpContext
,
237 ULONG BytesToReallocate
,
238 PULONG FirstSpliceSourceCluster
,
239 PULONG FirstSpliceTargetCluster
,
240 PULONG SecondSpliceSourceCluster
,
241 PULONG SecondSpliceTargetCluster
,
245 _Requires_lock_held_(_Global_critical_region_
)
247 FatComputeMoveFileParameter (
248 IN PIRP_CONTEXT IrpContext
,
252 IN OUT PULONG ByteCount
,
253 OUT PULONG BytesToReallocate
,
254 OUT PULONG BytesToWrite
,
255 OUT PLARGE_INTEGER SourceLbo
259 FatSearchBufferForLabel(
260 IN PIRP_CONTEXT IrpContext
,
264 OUT PBOOLEAN LabelFound
268 FatVerifyLookupFatEntry (
269 IN PIRP_CONTEXT IrpContext
,
272 IN OUT PULONG FatEntry
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)
316 BOOLEAN FatMoveFileDebug
= 0;
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
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
330 #define MCB_SCALE_LOG2 (Vcb->AllocationSupport.LogOfBytesPerSector)
331 #define MCB_SCALE (1 << MCB_SCALE_LOG2)
332 #define MCB_SCALE_MODULO (MCB_SCALE - 1)
339 _Out_ PLONGLONG ByteCount
346 UNREFERENCED_PARAMETER(Vcb
);
348 while (FsRtlGetNextLargeMcbEntry(Mcb
, Index
, &llVbo
, &Lbo
, ByteCount
)) {
350 if (((ULONG
)Lbo
) == -1) {
376 LONGLONG SparseByteCount
;
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
389 SectorCount
>>= MCB_SCALE_LOG2
;
393 Vbo
>>= MCB_SCALE_LOG2
;
394 Lbo
>>= MCB_SCALE_LOG2
;
396 NT_ASSERT( SectorCount
!= 0 );
398 if (Mcb
!= &Vcb
->DirtyFatMcb
) {
399 NT_ASSERT( FatNonSparseMcb( Vcb
, Mcb
, &SparseVbo
, &SparseByteCount
) ||
400 ((SparseVbo
== Vbo
) && (SparseByteCount
== SectorCount
)) );
403 Result
= FsRtlAddLargeMcbEntry( Mcb
,
406 ((LONGLONG
) SectorCount
) );
408 if (Mcb
!= &Vcb
->DirtyFatMcb
) {
409 NT_ASSERT( FatNonSparseMcb( Vcb
, Mcb
, &SparseVbo
, &SparseByteCount
) ||
410 ((SparseVbo
== Vbo
) && (SparseByteCount
== SectorCount
)) );
423 OUT PULONG ByteCount OPTIONAL
,
424 OUT PULONG Index OPTIONAL
429 LONGLONG LiSectorCount
;
435 Remainder
= Vbo
& MCB_SCALE_MODULO
;
437 Results
= FsRtlLookupLargeMcbEntry( Mcb
,
438 (Vbo
>> MCB_SCALE_LOG2
),
440 ARGUMENT_PRESENT(ByteCount
) ? &LiSectorCount
: NULL
,
445 if ((ULONG
) LiLbo
!= -1) {
447 *Lbo
= (((LBO
) LiLbo
) << MCB_SCALE_LOG2
);
459 if (ARGUMENT_PRESENT(ByteCount
)) {
461 *ByteCount
= (ULONG
) LiSectorCount
;
465 *ByteCount
<<= MCB_SCALE_LOG2
;
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.
473 if (*ByteCount
== 0) {
475 *ByteCount
= 0xFFFFFFFF;
480 *ByteCount
-= Remainder
;
490 // NOTE: Vbo/Lbn undefined if MCB is empty & return code false.
494 FatLookupLastMcbEntry (
513 Results
= FsRtlLookupLastLargeMcbEntryAndIndex( Mcb
,
518 *Vbo
= ((VBO
) LiVbo
) << MCB_SCALE_LOG2
;
520 if (((ULONG
) LiLbo
) != -1) {
522 *Lbo
= ((LBO
) LiLbo
) << MCB_SCALE_LOG2
;
524 *Lbo
+= (MCB_SCALE
- 1);
525 *Vbo
+= (MCB_SCALE
- 1);
554 LONGLONG LiSectorCount
;
560 Results
= FsRtlGetNextLargeMcbEntry( Mcb
,
568 *Vbo
= ((VBO
) LiVbo
) << MCB_SCALE_LOG2
;
570 if (((ULONG
) LiLbo
) != -1) {
572 *Lbo
= ((LBO
) LiLbo
) << MCB_SCALE_LOG2
;
579 *ByteCount
= ((ULONG
) LiSectorCount
) << MCB_SCALE_LOG2
;
581 if ((*ByteCount
== 0) && (LiSectorCount
!= 0)) {
584 // If 'ByteCount' overflows, then this is likely a file of
585 // max supported size (2^32 - 1) in one contiguous run.
588 NT_ASSERT( RunIndex
== 0 );
590 *ByteCount
= 0xFFFFFFFF;
608 if ((SectorCount
) && (SectorCount
!= 0xFFFFFFFF)) {
611 SectorCount
>>= MCB_SCALE_LOG2
;
615 Vbo
>>= MCB_SCALE_LOG2
;
621 FsRtlRemoveLargeMcbEntry( Mcb
,
623 (LONGLONG
) SectorCount
);
626 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
635 _Function_class_(IRP_MJ_FILE_SYSTEM_CONTROL
)
636 _Function_class_(DRIVER_DISPATCH
)
639 FatFsdFileSystemControl (
640 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
648 This routine implements the FSD part of FileSystem control operations
652 VolumeDeviceObject - Supplies the volume device object where the
655 Irp - Supplies the Irp being processed
659 NTSTATUS - The FSD status for the IRP
666 PIRP_CONTEXT IrpContext
= NULL
;
671 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
673 DebugTrace(+1, Dbg
,"FatFsdFileSystemControl\n", 0);
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.
683 if (IoGetCurrentIrpStackLocation(Irp
)->FileObject
== NULL
) {
689 Wait
= CanFsdWait( Irp
);
692 FsRtlEnterFileSystem();
694 TopLevel
= FatIsIrpTopLevel( Irp
);
698 PIO_STACK_LOCATION IrpSp
;
700 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
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.
708 if (FatDeviceIsFatFsdo( IrpSp
->DeviceObject
) &&
709 (IrpSp
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
) &&
710 (IrpSp
->MinorFunction
== IRP_MN_USER_FS_REQUEST
) &&
711 (IrpSp
->Parameters
.FileSystemControl
.FsControlCode
==
712 FSCTL_INVALIDATE_VOLUMES
)) {
714 Status
= FatInvalidateVolumes( Irp
);
718 IrpContext
= FatCreateIrpContext( Irp
, Wait
);
720 Status
= FatCommonFileSystemControl( IrpContext
, Irp
);
723 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
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
732 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
735 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
737 FsRtlExitFileSystem();
740 // And return to our caller
743 DebugTrace(-1, Dbg
, "FatFsdFileSystemControl -> %08lx\n", Status
);
749 _Requires_lock_held_(_Global_critical_region_
)
751 FatCommonFileSystemControl (
752 IN PIRP_CONTEXT IrpContext
,
760 This is the common routine for doing FileSystem control operations called
761 by both the fsd and fsp threads
765 Irp - Supplies the Irp to process
769 NTSTATUS - The return status for the operation
775 PIO_STACK_LOCATION IrpSp
;
780 // Get a pointer to the current Irp stack location
783 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
785 DebugTrace(+1, Dbg
,"FatCommonFileSystemControl\n", 0);
786 DebugTrace( 0, Dbg
,"Irp = %p\n", Irp
);
787 DebugTrace( 0, Dbg
,"MinorFunction = %08lx\n", IrpSp
->MinorFunction
);
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
795 switch (IrpSp
->MinorFunction
) {
797 case IRP_MN_USER_FS_REQUEST
:
799 Status
= FatUserFsCtrl( IrpContext
, Irp
);
802 case IRP_MN_MOUNT_VOLUME
:
804 Status
= FatMountVolume( IrpContext
,
805 IrpSp
->Parameters
.MountVolume
.DeviceObject
,
806 IrpSp
->Parameters
.MountVolume
.Vpb
,
807 IrpSp
->DeviceObject
);
810 // Complete the request.
812 // We do this here because FatMountVolume can be called recursively,
813 // but the Irp is only to be completed once.
815 // NOTE: I don't think this is true anymore (danlo 3/15/1999). Probably
816 // an artifact of the old doublespace attempt.
819 FatCompleteRequest( IrpContext
, Irp
, Status
);
822 case IRP_MN_VERIFY_VOLUME
:
824 Status
= FatVerifyVolume( IrpContext
, Irp
);
829 DebugTrace( 0, Dbg
, "Invalid FS Control Minor Function %08lx\n", IrpSp
->MinorFunction
);
831 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
832 Status
= STATUS_INVALID_DEVICE_REQUEST
;
836 DebugTrace(-1, Dbg
, "FatCommonFileSystemControl -> %08lx\n", Status
);
843 // Local Support Routine
846 _Requires_lock_held_(_Global_critical_region_
)
849 IN PIRP_CONTEXT IrpContext
,
850 IN PDEVICE_OBJECT TargetDeviceObject
,
852 IN PDEVICE_OBJECT FsDeviceObject
859 This routine performs the mount volume operation. It is responsible for
860 either completing of enqueuing the input Irp.
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:
866 1. Create a new Vcb Structure, and initialize it enough to do cached
869 2. Read the disk and check if it is a Fat volume.
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
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.
879 5. Otherwise create a root DCB, create Fsp threads as necessary, and
884 TargetDeviceObject - This is where we send all of our requests.
886 Vpb - This gives us additional information needed to complete the mount.
890 NTSTATUS - The return status for the operation
895 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( IrpContext
->OriginatingIrp
);
896 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
899 PPACKED_BOOT_SECTOR BootSector
= NULL
;
905 BOOLEAN MountNewVolume
= FALSE
;
906 BOOLEAN WeClearedVerifyRequiredBit
= FALSE
;
907 BOOLEAN DoARemount
= FALSE
;
912 PDEVICE_OBJECT RealDevice
= NULL
;
913 PVOLUME_DEVICE_OBJECT VolDo
= NULL
;
915 PFILE_OBJECT RootDirectoryFile
= NULL
;
919 IO_STATUS_BLOCK Iosb
= {0};
920 ULONG ChangeCount
= 0;
922 DISK_GEOMETRY Geometry
;
924 PARTITION_INFORMATION_EX PartitionInformation
;
925 NTSTATUS StatusPartInfo
;
927 #if (NTDDI_VERSION > NTDDI_WIN8)
928 GUID VolumeGuid
= {0};
934 DebugTrace(+1, Dbg
, "FatMountVolume\n", 0);
935 DebugTrace( 0, Dbg
, "TargetDeviceObject = %p\n", TargetDeviceObject
);
936 DebugTrace( 0, Dbg
, "Vpb = %p\n", Vpb
);
938 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
939 NT_ASSERT( FatDeviceIsFatFsdo( FsDeviceObject
));
942 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
945 if (FlagOn(TargetDeviceObject
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
948 // Verify that there is a disk here and pick up the change count.
951 Status
= FatPerformDevIoCtrl( IrpContext
,
952 IOCTL_DISK_CHECK_VERIFY
,
962 if (!NT_SUCCESS( Status
)) {
965 // If we will allow a raw mount then avoid sending the popup.
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
971 // Cmd should really handle this per 9x.
974 if (!FlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
) &&
975 Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_DISK
) {
977 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
985 if (Iosb
.Information
!= sizeof(ULONG
)) {
988 // Be safe about the count in case the driver didn't fill it in
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)
1000 if ((TargetDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) &&
1001 !FatScanForDataTrack( IrpContext
, TargetDeviceObject
)) {
1003 return STATUS_UNRECOGNIZED_VOLUME
;
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.
1011 StatusPartInfo
= FatPerformDevIoCtrl( IrpContext
,
1012 IOCTL_DISK_GET_PARTITION_INFO_EX
,
1016 &PartitionInformation
,
1017 sizeof(PARTITION_INFORMATION_EX
),
1023 // Make sure we can wait.
1026 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
1029 // Do a quick check to see if there any Vcb's which can be removed.
1032 FatScanForDismountedVcb( IrpContext
);
1035 // Initialize the Bcbs and our final state so that the termination
1036 // handlers will know what to free or unpin
1044 MountNewVolume
= FALSE
;
1049 // Synchronize with FatCheckForDismount(), which modifies the vpb.
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" )
1058 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
1061 #pragma prefast( pop )
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.
1070 if (!NT_SUCCESS(Status
= IoCreateDevice( FatData
.DriverObject
,
1071 sizeof(VOLUME_DEVICE_OBJECT
) - sizeof(DEVICE_OBJECT
),
1073 FILE_DEVICE_DISK_FILE_SYSTEM
,
1076 (PDEVICE_OBJECT
*)&VolDo
))) {
1078 try_return( Status
);
1082 // Our alignment requirement is the larger of the processor alignment requirement
1083 // already in the volume device object and that in the TargetDeviceObject
1086 if (TargetDeviceObject
->AlignmentRequirement
> VolDo
->DeviceObject
.AlignmentRequirement
) {
1088 VolDo
->DeviceObject
.AlignmentRequirement
= TargetDeviceObject
->AlignmentRequirement
;
1092 // Initialize the overflow queue for the volume
1095 VolDo
->OverflowQueueCount
= 0;
1096 InitializeListHead( &VolDo
->OverflowQueue
);
1098 VolDo
->PostedRequestCount
= 0;
1099 KeInitializeSpinLock( &VolDo
->OverflowQueueSpinLock
);
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.
1109 VolDo
->DeviceObject
.StackSize
= (CCHAR
)(TargetDeviceObject
->StackSize
+ 1);
1112 // We must also set the sector size correctly in our device object
1113 // before clearing the device initializing flag.
1116 Status
= FatPerformDevIoCtrl( IrpContext
,
1117 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1122 sizeof( DISK_GEOMETRY
),
1127 if (!NT_SUCCESS( Status
)) {
1129 try_return( Status
);
1133 #pragma prefast( suppress: 28175, "this is a filesystem driver, touching SectorSize is fine" )
1135 VolDo
->DeviceObject
.SectorSize
= (USHORT
)Geometry
.BytesPerSector
;
1138 // Indicate that this device object is now completely initialized
1141 ClearFlag(VolDo
->DeviceObject
.Flags
, DO_DEVICE_INITIALIZING
);
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.
1150 Vpb
->DeviceObject
= (PDEVICE_OBJECT
)VolDo
;
1153 // If the real device needs verification, temporarily clear the
1157 RealDevice
= Vpb
->RealDevice
;
1159 if ( FlagOn(RealDevice
->Flags
, DO_VERIFY_VOLUME
) ) {
1161 ClearFlag(RealDevice
->Flags
, DO_VERIFY_VOLUME
);
1163 WeClearedVerifyRequiredBit
= TRUE
;
1167 // Initialize the new vcb
1170 FatInitializeVcb( IrpContext
,
1176 // Get a reference to the Vcb hanging off the end of the device object
1182 // Read in the boot sector, and have the read be the minumum size
1183 // needed. We know we can wait.
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.
1193 FatReadVolumeFile( IrpContext
,
1196 sizeof(PACKED_BOOT_SECTOR
),
1198 (PVOID
*)&BootSector
);
1200 } _SEH2_EXCEPT( Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_CD_ROM
?
1201 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
1207 // Call a routine to check the boot sector to see if it is fat
1210 if (BootBcb
== NULL
|| !FatIsBootSectorFat( BootSector
)) {
1212 DebugTrace(0, Dbg
, "Not a Fat Volume\n", 0);
1215 // Complete the request and return to our caller
1218 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
1221 #if (NTDDI_VERSION > NTDDI_WIN8)
1223 // Initialize the volume guid.
1226 if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb
->TargetDeviceObject
, &VolumeGuid
))) {
1230 // Stash a copy away in the VCB.
1233 RtlCopyMemory( &Vcb
->VolumeGuid
, &VolumeGuid
, sizeof(GUID
));
1239 // Stash away a copy of the volume GUID path in our VCB.
1242 if (Vcb
->VolumeGuidPath
.Buffer
) {
1243 ExFreePool( Vcb
->VolumeGuidPath
.Buffer
);
1244 Vcb
->VolumeGuidPath
.Buffer
= NULL
;
1245 Vcb
->VolumeGuidPath
.Length
= Vcb
->VolumeGuidPath
.MaximumLength
= 0;
1248 IoVolumeDeviceToGuidPath( Vcb
->TargetDeviceObject
, &Vcb
->VolumeGuidPath
);
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.
1257 // (read: digital cameras)
1259 // Win9x does the same.
1262 FatUnpackBios( &Vcb
->Bpb
, &BootSector
->PackedBpb
);
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.
1272 // Note that this is only conceivable on good old MBR media.
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.
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)) {
1287 DebugTrace( 0, Dbg
, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 );
1290 // Complete the request and return to our caller
1293 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
1297 // Verify that the sector size recorded in the Bpb matches what the
1298 // device currently reports it's sector size to be.
1301 if ( !NT_SUCCESS( Status
) ||
1302 (Geometry
.BytesPerSector
!= Vcb
->Bpb
.BytesPerSector
)) {
1304 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
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.
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.
1315 if (Vcb
->Bpb
.Sectors
!= 0) { Vcb
->Bpb
.LargeSectors
= 0; }
1317 if (IsBpbFat32(&BootSector
->PackedBpb
)) {
1319 CopyUchar4( &Vpb
->SerialNumber
, ((PPACKED_BOOT_SECTOR_EX
)BootSector
)->Id
);
1323 CopyUchar4( &Vpb
->SerialNumber
, BootSector
->Id
);
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
1332 Vcb
->First0x24BytesOfBootSector
=
1333 FsRtlAllocatePoolWithTag( PagedPool
,
1338 // Stash a copy of the first 0x24 bytes
1341 RtlCopyMemory( Vcb
->First0x24BytesOfBootSector
,
1347 // Now unpin the boot sector, so when we set up allocation eveything
1351 FatUnpinBcb( IrpContext
, BootBcb
);
1354 // Compute a number of fields for Vcb.AllocationSupport
1357 FatSetupAllocationSupport( IrpContext
, Vcb
);
1360 // Sanity check the FsInfo information for FAT32 volumes. Silently deal
1361 // with messed up information by effectively disabling FsInfo updates.
1364 if (FatIsFat32( Vcb
)) {
1366 if (Vcb
->Bpb
.FsInfoSector
>= Vcb
->Bpb
.ReservedSectors
) {
1368 Vcb
->Bpb
.FsInfoSector
= 0;
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.
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.
1386 FatCreateRootDcb( IrpContext
, Vcb
);
1388 } _SEH2_EXCEPT (_SEH2_GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR
? EXCEPTION_EXECUTE_HANDLER
:
1389 EXCEPTION_CONTINUE_SEARCH
) {
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.
1396 FatCheckDirtyBit( IrpContext
,
1400 // Set the dirty bit if it is not set already
1403 if ( !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
)) {
1405 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNT_IN_PROGRESS
);
1406 FatMarkVolume( IrpContext
, Vcb
, VolumeDirty
);
1407 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNT_IN_PROGRESS
);
1411 // Now keep bailing out ...
1414 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1417 FatLocateVolumeLabel( IrpContext
,
1421 (PVBO
)&ByteOffset
);
1423 if (Dirent
!= NULL
) {
1425 OEM_STRING OemString
;
1426 UNICODE_STRING UnicodeString
;
1429 // Compute the length of the volume name
1432 OemString
.Buffer
= (PCHAR
)&Dirent
->FileName
[0];
1433 OemString
.MaximumLength
= 11;
1435 for ( OemString
.Length
= 11;
1436 OemString
.Length
> 0;
1437 OemString
.Length
-= 1) {
1439 if ( (Dirent
->FileName
[OemString
.Length
-1] != 0x00) &&
1440 (Dirent
->FileName
[OemString
.Length
-1] != 0x20) ) { break; }
1443 UnicodeString
.MaximumLength
= MAXIMUM_VOLUME_LABEL_LENGTH
;
1444 UnicodeString
.Buffer
= &Vcb
->Vpb
->VolumeLabel
[0];
1446 Status
= RtlOemStringToCountedUnicodeString( &UnicodeString
,
1450 if ( !NT_SUCCESS( Status
) ) {
1452 try_return( Status
);
1455 Vpb
->VolumeLabelLength
= UnicodeString
.Length
;
1459 Vpb
->VolumeLabelLength
= 0;
1463 // Use the change count we noted initially *before* doing any work.
1464 // If something came along in the midst of this operation, we'll
1465 // verify and discover the problem.
1468 Vcb
->ChangeCount
= ChangeCount
;
1471 // Now scan the list of previously mounted volumes and compare
1472 // serial numbers and volume labels off not currently mounted
1473 // volumes to see if we have a match.
1476 for (Links
= FatData
.VcbQueue
.Flink
;
1477 Links
!= &FatData
.VcbQueue
;
1478 Links
= Links
->Flink
) {
1480 OldVcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
1481 OldVpb
= OldVcb
->Vpb
;
1484 // Skip over ourselves since we're already in the VcbQueue
1487 if (OldVpb
== Vpb
) { continue; }
1490 // Check for a match:
1492 // Serial Number, VolumeLabel and Bpb must all be the same.
1493 // Also the volume must have failed a verify before (ie.
1494 // VolumeNotMounted), and it must be in the same physical
1495 // drive than it was mounted in before.
1498 if ( (OldVpb
->SerialNumber
== Vpb
->SerialNumber
) &&
1499 (OldVcb
->VcbCondition
== VcbNotMounted
) &&
1500 (OldVpb
->RealDevice
== RealDevice
) &&
1501 (OldVpb
->VolumeLabelLength
== Vpb
->VolumeLabelLength
) &&
1502 (RtlEqualMemory(&OldVpb
->VolumeLabel
[0],
1503 &Vpb
->VolumeLabel
[0],
1504 Vpb
->VolumeLabelLength
)) &&
1505 (RtlEqualMemory(&OldVcb
->Bpb
,
1507 IsBpbFat32(&Vcb
->Bpb
) ?
1508 sizeof(BIOS_PARAMETER_BLOCK
) :
1509 FIELD_OFFSET(BIOS_PARAMETER_BLOCK
,
1510 LargeSectorsPerFat
) ))) {
1522 DebugTrace(0, Dbg
, "Doing a remount\n", 0);
1523 DebugTrace(0, Dbg
, "Vcb = %p\n", Vcb
);
1524 DebugTrace(0, Dbg
, "Vpb = %p\n", Vpb
);
1525 DebugTrace(0, Dbg
, "OldVcb = %p\n", OldVcb
);
1526 DebugTrace(0, Dbg
, "OldVpb = %p\n", OldVpb
);
1529 // Swap target device objects between the VCBs. That way
1530 // the old VCB will start using the new target device object,
1531 // and the new VCB will be torn down and deference the old
1532 // target device object.
1535 Vcb
->TargetDeviceObject
= OldVcb
->TargetDeviceObject
;
1536 OldVcb
->TargetDeviceObject
= TargetDeviceObject
;
1539 // This is a remount, so link the old vpb in place
1543 NT_ASSERT( !FlagOn( OldVcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
) );
1545 FatSetVcbCondition( OldVcb
, VcbGood
);
1546 OldVpb
->RealDevice
= Vpb
->RealDevice
;
1547 ClearFlag( OldVcb
->VcbState
, VCB_STATE_VPB_NOT_ON_DEVICE
);
1550 #pragma prefast( suppress: 28175, "touching Vpb is ok for a filesystem" )
1552 OldVpb
->RealDevice
->Vpb
= OldVpb
;
1555 // Use the new changecount.
1558 OldVcb
->ChangeCount
= Vcb
->ChangeCount
;
1561 // If the new VPB is the VPB referenced in the original Irp, set
1562 // that reference back to the old VPB.
1565 IrpVpb
= &IoGetCurrentIrpStackLocation(IrpContext
->OriginatingIrp
)->Parameters
.MountVolume
.Vpb
;
1567 if (*IrpVpb
== Vpb
) {
1573 // We do not want to touch this VPB again. It will get cleaned up when
1574 // the new VCB is cleaned up.
1577 NT_ASSERT( Vcb
->Vpb
== Vpb
);
1580 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
1581 FatSetVcbCondition( Vcb
, VcbBad
);
1584 // Reinitialize the volume file cache and allocation support.
1588 CC_FILE_SIZES FileSizes
;
1590 FileSizes
.AllocationSize
.QuadPart
=
1591 FileSizes
.FileSize
.QuadPart
= ( 0x40000 + 0x1000 );
1592 FileSizes
.ValidDataLength
= FatMaxLarge
;
1594 DebugTrace(0, Dbg
, "Truncate and reinitialize the volume file\n", 0);
1596 FatInitializeCacheMap( OldVcb
->VirtualVolumeFile
,
1599 &FatData
.CacheManagerNoOpCallbacks
,
1603 // Redo the allocation support
1606 FatSetupAllocationSupport( IrpContext
, OldVcb
);
1609 // Get the state of the dirty bit.
1612 FatCheckDirtyBit( IrpContext
, OldVcb
);
1615 // Check for write protected media.
1618 if (FatIsMediaWriteProtected(IrpContext
, TargetDeviceObject
)) {
1620 SetFlag( OldVcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1624 ClearFlag( OldVcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1629 // Complete the request and return to our caller
1632 try_return( Status
= STATUS_SUCCESS
);
1635 DebugTrace(0, Dbg
, "Mount a new volume\n", 0);
1638 // This is a new mount
1640 // Create a blank ea data file fcb, just not for Fat32.
1643 if (!FatIsFat32(Vcb
)) {
1648 RtlZeroMemory( &TempDirent
, sizeof(DIRENT
) );
1649 RtlCopyMemory( &TempDirent
.FileName
[0], "EA DATA SF", 11 );
1651 EaFcb
= FatCreateFcb( IrpContext
,
1663 // Deny anybody who trys to open the file.
1666 SetFlag( EaFcb
->FcbState
, FCB_STATE_SYSTEM_FILE
);
1672 // Get the state of the dirty bit.
1675 FatCheckDirtyBit( IrpContext
, Vcb
);
1679 // Check for write protected media.
1682 if (FatIsMediaWriteProtected(IrpContext
, TargetDeviceObject
)) {
1684 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1688 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1693 // Lock volume in drive if we just mounted the boot drive.
1696 if (FlagOn(RealDevice
->Flags
, DO_SYSTEM_BOOT_PARTITION
)) {
1698 SetFlag(Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
);
1700 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
)) {
1702 FatToggleMediaEjectDisable( IrpContext
, Vcb
, TRUE
);
1708 // Indicate to our termination handler that we have mounted
1712 MountNewVolume
= TRUE
;
1715 // Complete the request
1718 Status
= STATUS_SUCCESS
;
1721 // Ref the root dir stream object so we can send mount notification.
1724 RootDirectoryFile
= Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
;
1725 ObReferenceObject( RootDirectoryFile
);
1728 // Remove the extra reference to this target DO made on behalf of us
1729 // by the IO system. In the remount case, we permit regular Vcb
1730 // deletion to do this work.
1733 ObDereferenceObject( TargetDeviceObject
);
1740 DebugUnwind( FatMountVolume
);
1742 FatUnpinBcb( IrpContext
, BootBcb
);
1743 FatUnpinBcb( IrpContext
, DirentBcb
);
1746 // Check if a volume was mounted. If not then we need to
1747 // mark the Vpb not mounted again.
1750 if ( !MountNewVolume
) {
1752 if ( Vcb
!= NULL
) {
1755 // A VCB was created and initialized. We need to try to tear it down.
1758 FatCheckForDismount( IrpContext
,
1762 IrpContext
->Vcb
= NULL
;
1764 } else if (VolDo
!= NULL
) {
1767 // The VCB was never initialized, so we need to delete the
1768 // device right here.
1771 IoDeleteDevice( &VolDo
->DeviceObject
);
1775 // See if a remount failed.
1778 if (DoARemount
&& _SEH2_AbnormalTermination()) {
1781 // The remount failed. Try to tear down the old VCB as well.
1784 FatCheckForDismount( IrpContext
,
1790 if ( WeClearedVerifyRequiredBit
== TRUE
) {
1792 SetFlag(RealDevice
->Flags
, DO_VERIFY_VOLUME
);
1795 FatReleaseGlobal( IrpContext
);
1797 DebugTrace(-1, Dbg
, "FatMountVolume -> %08lx\n", Status
);
1801 // Now send mount notification. Note that since this is outside of any
1802 // synchronization since the synchronous delivery of this may go to
1803 // folks that provoke re-entrance to the FS.
1806 if (RootDirectoryFile
!= NULL
) {
1808 #if (NTDDI_VERSION >= NTDDI_WIN8)
1809 if (FatDiskAccountingEnabled
) {
1811 CcSetAdditionalCacheAttributesEx( RootDirectoryFile
, CC_ENABLE_DISK_IO_ACCOUNTING
);
1815 FsRtlNotifyVolumeEvent( RootDirectoryFile
, FSRTL_VOLUME_MOUNT
);
1816 ObDereferenceObject( RootDirectoryFile
);
1824 // Local Support Routine
1827 _Requires_lock_held_(_Global_critical_region_
)
1830 IN PIRP_CONTEXT IrpContext
,
1836 Routine Description:
1838 This routine performs the verify volume operation by checking the volume
1839 label and serial number physically on the media with the the Vcb
1840 currently claiming to have the volume mounted. It is responsible for
1841 either completing or enqueuing the input Irp.
1843 Regardless of whether the verify operation succeeds, the following
1844 operations are performed:
1846 - Set Vcb->VirtualEaFile back to its initial state.
1847 - Purge all cached data (flushing first if verify succeeds)
1848 - Mark all Fcbs as needing verification
1850 If the volumes verifies correctly we also must:
1852 - Check the volume dirty bit.
1853 - Reinitialize the allocation support
1854 - Flush any dirty data
1856 If the volume verify fails, it may never be mounted again. If it is
1857 mounted again, it will happen as a remount operation. In preparation
1858 for that, and to leave the volume in a state that can be "lazy deleted"
1859 the following operations are performed:
1861 - Set the Vcb condition to VcbNotMounted
1862 - Uninitialize the volume file cachemap
1863 - Tear down the allocation support
1865 In the case of an abnormal termination we haven't determined the state
1866 of the volume, so we set the Device Object as needing verification again.
1870 Irp - Supplies the Irp to process
1874 NTSTATUS - If the verify operation completes, it will return either
1875 STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or
1876 other error is encountered, that status will be returned.
1881 NTSTATUS Status
= STATUS_SUCCESS
;
1883 PIO_STACK_LOCATION IrpSp
;
1885 PDIRENT RootDirectory
= NULL
;
1886 PPACKED_BOOT_SECTOR BootSector
= NULL
;
1888 BIOS_PARAMETER_BLOCK Bpb
;
1890 PVOLUME_DEVICE_OBJECT VolDo
;
1895 BOOLEAN ClearVerify
= FALSE
;
1896 BOOLEAN ReleaseEntireVolume
= FALSE
;
1897 BOOLEAN VerifyAlreadyDone
= FALSE
;
1899 DISK_GEOMETRY DiskGeometry
;
1901 LBO RootDirectoryLbo
;
1902 ULONG RootDirectorySize
;
1905 ULONG ChangeCount
= 0;
1906 IO_STATUS_BLOCK Iosb
= {0};
1911 // Get the current Irp stack location
1914 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
1916 DebugTrace(+1, Dbg
, "FatVerifyVolume\n", 0);
1917 DebugTrace( 0, Dbg
, "DeviceObject = %p\n", IrpSp
->Parameters
.VerifyVolume
.DeviceObject
);
1918 DebugTrace( 0, Dbg
, "Vpb = %p\n", IrpSp
->Parameters
.VerifyVolume
.Vpb
);
1921 // Save some references to make our life a little easier. Note the Vcb for the purposes
1922 // of exception handling.
1925 VolDo
= (PVOLUME_DEVICE_OBJECT
)IrpSp
->Parameters
.VerifyVolume
.DeviceObject
;
1927 Vpb
= IrpSp
->Parameters
.VerifyVolume
.Vpb
;
1928 IrpContext
->Vcb
= Vcb
= &VolDo
->Vcb
;
1931 // If we cannot wait then enqueue the irp to the fsp and
1932 // return the status to our caller.
1935 if (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
)) {
1937 DebugTrace(0, Dbg
, "Cannot wait for verify.\n", 0);
1939 Status
= FatFsdPostRequest( IrpContext
, Irp
);
1941 DebugTrace(-1, Dbg
, "FatVerifyVolume -> %08lx\n", Status
);
1946 // We are serialized at this point allowing only one thread to
1947 // actually perform the verify operation. Any others will just
1948 // wait and then no-op when checking if the volume still needs
1953 #pragma prefast( push )
1954 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1955 #pragma prefast( disable: 28193, "this will always wait" )
1958 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
1961 #pragma prefast( pop )
1964 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
1968 BOOLEAN AllowRawMount
= BooleanFlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
);
1971 // Mark ourselves as verifying this volume so that recursive I/Os
1972 // will be able to complete.
1975 NT_ASSERT( Vcb
->VerifyThread
== NULL
);
1976 Vcb
->VerifyThread
= KeGetCurrentThread();
1979 // Check if the real device still needs to be verified. If it doesn't
1980 // then obviously someone beat us here and already did the work
1981 // so complete the verify irp with success. Otherwise reenable
1982 // the real device and get to work.
1985 if (!FlagOn(Vpb
->RealDevice
->Flags
, DO_VERIFY_VOLUME
)) {
1987 DebugTrace(0, Dbg
, "RealDevice has already been verified\n", 0);
1989 VerifyAlreadyDone
= TRUE
;
1990 try_return( Status
= STATUS_SUCCESS
);
1994 // Ping the volume with a partition query to make Jeff happy.
1998 PARTITION_INFORMATION_EX PartitionInformation
;
2000 (VOID
) FatPerformDevIoCtrl( IrpContext
,
2001 IOCTL_DISK_GET_PARTITION_INFO_EX
,
2002 Vcb
->TargetDeviceObject
,
2005 &PartitionInformation
,
2006 sizeof(PARTITION_INFORMATION_EX
),
2013 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
2016 if (FlagOn(Vcb
->TargetDeviceObject
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
2019 // Verify that there is a disk here and pick up the change count.
2022 Status
= FatPerformDevIoCtrl( IrpContext
,
2023 IOCTL_DISK_CHECK_VERIFY
,
2024 Vcb
->TargetDeviceObject
,
2033 if (!NT_SUCCESS( Status
)) {
2036 // If we will allow a raw mount then return WRONG_VOLUME to
2037 // allow the volume to be mounted by raw.
2040 if (AllowRawMount
) {
2042 try_return( Status
= STATUS_WRONG_VOLUME
);
2045 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
2050 if (Iosb
.Information
!= sizeof(ULONG
)) {
2053 // Be safe about the count in case the driver didn't fill it in
2060 // Whatever happens we will have verified this volume at this change
2061 // count, so record that fact.
2064 Vcb
->ChangeCount
= ChangeCount
;
2067 // If this is a CD class device, then check to see if there is a
2068 // 'data track' or not. This is to avoid issuing paging reads which will
2069 // fail later in the mount process (e.g. CD-DA or blank CD media)
2072 if ((Vcb
->TargetDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) &&
2073 !FatScanForDataTrack( IrpContext
, Vcb
->TargetDeviceObject
)) {
2075 try_return( Status
= STATUS_WRONG_VOLUME
);
2079 // Some devices can change sector sizes on the fly. Obviously, it
2080 // isn't the same volume if that happens.
2083 Status
= FatPerformDevIoCtrl( IrpContext
,
2084 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
2085 Vcb
->TargetDeviceObject
,
2089 sizeof( DISK_GEOMETRY
),
2094 if (!NT_SUCCESS( Status
)) {
2097 // If we will allow a raw mount then return WRONG_VOLUME to
2098 // allow the volume to be mounted by raw.
2101 if (AllowRawMount
) {
2103 try_return( Status
= STATUS_WRONG_VOLUME
);
2106 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
2110 // Read in the boot sector
2113 SectorSize
= (ULONG
)Vcb
->Bpb
.BytesPerSector
;
2115 if (SectorSize
!= DiskGeometry
.BytesPerSector
) {
2117 try_return( Status
= STATUS_WRONG_VOLUME
);
2120 BootSector
= FsRtlAllocatePoolWithTag(NonPagedPoolNxCacheAligned
,
2121 (ULONG
) ROUND_TO_PAGES( SectorSize
),
2122 TAG_VERIFY_BOOTSECTOR
);
2125 // If this verify is on behalf of a DASD open, allow a RAW mount.
2128 if (!FatPerformVerifyDiskRead( IrpContext
,
2135 try_return( Status
= STATUS_WRONG_VOLUME
);
2139 // Call a routine to check the boot sector to see if it is fat.
2140 // If it is not fat then mark the vcb as not mounted tell our
2141 // caller its the wrong volume
2144 if (!FatIsBootSectorFat( BootSector
)) {
2146 DebugTrace(0, Dbg
, "Not a Fat Volume\n", 0);
2148 try_return( Status
= STATUS_WRONG_VOLUME
);
2152 // This is a fat volume, so extract serial number and see if it is
2159 if (IsBpbFat32(&BootSector
->PackedBpb
)) {
2160 CopyUchar4( &SerialNumber
, ((PPACKED_BOOT_SECTOR_EX
)BootSector
)->Id
);
2162 CopyUchar4( &SerialNumber
, BootSector
->Id
);
2165 if (SerialNumber
!= Vpb
->SerialNumber
) {
2167 DebugTrace(0, Dbg
, "Not our serial number\n", 0);
2169 try_return( Status
= STATUS_WRONG_VOLUME
);
2174 // Make sure the Bpbs are not different. We have to zero out our
2175 // stack version of the Bpb since unpacking leaves holes.
2178 RtlZeroMemory( &Bpb
, sizeof(BIOS_PARAMETER_BLOCK
) );
2180 FatUnpackBios( &Bpb
, &BootSector
->PackedBpb
);
2181 if (Bpb
.Sectors
!= 0) { Bpb
.LargeSectors
= 0; }
2183 if ( !RtlEqualMemory( &Bpb
,
2186 sizeof(BIOS_PARAMETER_BLOCK
) :
2187 FIELD_OFFSET(BIOS_PARAMETER_BLOCK
,
2188 LargeSectorsPerFat
) )) {
2190 DebugTrace(0, Dbg
, "Bpb is different\n", 0);
2192 try_return( Status
= STATUS_WRONG_VOLUME
);
2196 // Check the volume label. We do this by trying to locate the
2197 // volume label, making two strings one for the saved volume label
2198 // and the other for the new volume label and then we compare the
2202 if (FatRootDirectorySize(&Bpb
) > 0) {
2204 RootDirectorySize
= FatRootDirectorySize(&Bpb
);
2208 RootDirectorySize
= FatBytesPerCluster(&Bpb
);
2211 RootDirectory
= FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned
,
2212 (ULONG
) ROUND_TO_PAGES( RootDirectorySize
),
2213 TAG_VERIFY_ROOTDIR
);
2215 if (!IsBpbFat32(&BootSector
->PackedBpb
)) {
2218 // The Fat12/16 case is simple -- read the root directory in and
2222 RootDirectoryLbo
= FatRootDirectoryLbo(&Bpb
);
2224 if (!FatPerformVerifyDiskRead( IrpContext
,
2231 try_return( Status
= STATUS_WRONG_VOLUME
);
2234 Status
= FatSearchBufferForLabel(IrpContext
, Vpb
,
2235 RootDirectory
, RootDirectorySize
,
2238 if (!NT_SUCCESS(Status
)) {
2240 try_return( Status
);
2243 if (!LabelFound
&& Vpb
->VolumeLabelLength
> 0) {
2245 try_return( Status
= STATUS_WRONG_VOLUME
);
2250 ULONG RootDirectoryCluster
;
2252 RootDirectoryCluster
= Bpb
.RootDirFirstCluster
;
2254 while (RootDirectoryCluster
!= FAT_CLUSTER_LAST
) {
2256 RootDirectoryLbo
= FatGetLboFromIndex(Vcb
, RootDirectoryCluster
);
2258 if (!FatPerformVerifyDiskRead( IrpContext
,
2265 try_return( Status
= STATUS_WRONG_VOLUME
);
2268 Status
= FatSearchBufferForLabel(IrpContext
, Vpb
,
2269 RootDirectory
, RootDirectorySize
,
2272 if (!NT_SUCCESS(Status
)) {
2274 try_return( Status
);
2280 // Found a matching label.
2287 // Set ourselves up for the next loop iteration.
2290 FatVerifyLookupFatEntry( IrpContext
, Vcb
,
2291 RootDirectoryCluster
,
2292 &RootDirectoryCluster
);
2294 switch (FatInterpretClusterType(Vcb
, RootDirectoryCluster
)) {
2296 case FatClusterAvailable
:
2297 case FatClusterReserved
:
2301 // Bail all the way out if we have a bad root.
2304 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
2314 if (RootDirectoryCluster
== FAT_CLUSTER_LAST
&&
2315 Vpb
->VolumeLabelLength
> 0) {
2318 // Should have found a label, didn't find any.
2321 try_return( Status
= STATUS_WRONG_VOLUME
);
2329 // Note that we have previously acquired the Vcb to serialize
2330 // the EA file stuff the marking all the Fcbs as NeedToBeVerified.
2332 // Put the Ea file back in a initial state.
2335 FatCloseEaFile( IrpContext
, Vcb
, (BOOLEAN
)(Status
== STATUS_SUCCESS
) );
2338 // Mark all Fcbs as needing verification, but only if we really have
2342 if (!VerifyAlreadyDone
) {
2344 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2345 ReleaseEntireVolume
= TRUE
;
2347 FatMarkFcbCondition( IrpContext
, Vcb
->RootDcb
, FcbNeedsToBeVerified
, TRUE
);
2351 // If the verify didn't succeed, get the volume ready for a
2352 // remount or eventual deletion.
2355 if (Vcb
->VcbCondition
== VcbNotMounted
) {
2358 // If the volume was already in an unmounted state, just bail
2359 // and make sure we return STATUS_WRONG_VOLUME.
2362 Status
= STATUS_WRONG_VOLUME
;
2364 } else if ( Status
== STATUS_WRONG_VOLUME
) {
2367 // Grab everything so we can safely transition the volume state without
2368 // having a thread stumble into the torn-down allocation engine.
2371 if (!ReleaseEntireVolume
) {
2372 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2373 ReleaseEntireVolume
= TRUE
;
2377 // Get rid of any cached data, without flushing
2380 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, NoFlush
);
2383 // Uninitialize the volume file cache map. Note that we cannot
2384 // do a "FatSyncUninit" because of deadlock problems. However,
2385 // since this FileObject is referenced by us, and thus included
2386 // in the Vpb residual count, it is OK to do a normal CcUninit.
2389 CcUninitializeCacheMap( Vcb
->VirtualVolumeFile
,
2393 FatTearDownAllocationSupport( IrpContext
, Vcb
);
2395 FatSetVcbCondition( Vcb
, VcbNotMounted
);
2399 } else if (!VerifyAlreadyDone
) {
2402 // Grab everything so we can safely transition the volume state without
2403 // having a thread stumble into the torn-down allocation engine.
2406 if (!ReleaseEntireVolume
) {
2407 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2408 ReleaseEntireVolume
= TRUE
;
2412 // Get rid of any cached data, flushing first.
2414 // Future work (and for bonus points, around the other flush points)
2415 // could address the possibility that the dirent filesize hasn't been
2416 // updated yet, causing us to fail the re-verification of a file in
2417 // DetermineAndMark. This is pretty subtle and very very uncommon.
2420 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
2423 // Flush and Purge the volume file.
2426 (VOID
)FatFlushFat( IrpContext
, Vcb
);
2427 CcPurgeCacheSection( &Vcb
->SectionObjectPointers
, NULL
, 0, FALSE
);
2430 // Redo the allocation support with newly paged stuff.
2433 FatTearDownAllocationSupport( IrpContext
, Vcb
);
2434 FatSetupAllocationSupport( IrpContext
, Vcb
);
2436 FatCheckDirtyBit( IrpContext
, Vcb
);
2439 // Check for write protected media.
2442 if (FatIsMediaWriteProtected(IrpContext
, Vcb
->TargetDeviceObject
)) {
2444 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
2448 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
2457 // Mark the device as no longer needing verification.
2460 ClearFlag( Vpb
->RealDevice
->Flags
, DO_VERIFY_VOLUME
);
2465 DebugUnwind( FatVerifyVolume
);
2468 // Free any buffer we may have allocated
2471 if ( BootSector
!= NULL
) { ExFreePool( BootSector
); }
2472 if ( RootDirectory
!= NULL
) { ExFreePool( RootDirectory
); }
2475 // Show that we are done with this volume.
2478 NT_ASSERT( Vcb
->VerifyThread
== KeGetCurrentThread() );
2479 Vcb
->VerifyThread
= NULL
;
2481 if (ReleaseEntireVolume
) {
2483 FatReleaseVolume( IrpContext
, Vcb
);
2486 FatReleaseVcb( IrpContext
, Vcb
);
2487 FatReleaseGlobal( IrpContext
);
2490 // If this was not an abnormal termination, complete the irp.
2493 if (!_SEH2_AbnormalTermination()) {
2495 FatCompleteRequest( IrpContext
, Irp
, Status
);
2498 DebugTrace(-1, Dbg
, "FatVerifyVolume -> %08lx\n", Status
);
2506 // Local Support Routine
2510 FatIsBootSectorFat (
2511 IN PPACKED_BOOT_SECTOR BootSector
2516 Routine Description:
2518 This routine checks if the boot sector is for a fat file volume.
2522 BootSector - Supplies the packed boot sector to check
2526 BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
2532 BIOS_PARAMETER_BLOCK Bpb
= {0};
2534 DebugTrace(+1, Dbg
, "FatIsBootSectorFat, BootSector = %p\n", BootSector
);
2537 // The result is true unless we decide that it should be false
2543 // Unpack the bios and then test everything
2546 FatUnpackBios( &Bpb
, &BootSector
->PackedBpb
);
2547 if (Bpb
.Sectors
!= 0) { Bpb
.LargeSectors
= 0; }
2549 if ((BootSector
->Jump
[0] != 0xe9) &&
2550 (BootSector
->Jump
[0] != 0xeb) &&
2551 (BootSector
->Jump
[0] != 0x49)) {
2556 // Enforce some sanity on the sector size (easy check)
2559 } else if ((Bpb
.BytesPerSector
!= 128) &&
2560 (Bpb
.BytesPerSector
!= 256) &&
2561 (Bpb
.BytesPerSector
!= 512) &&
2562 (Bpb
.BytesPerSector
!= 1024) &&
2563 (Bpb
.BytesPerSector
!= 2048) &&
2564 (Bpb
.BytesPerSector
!= 4096)) {
2569 // Likewise on the clustering.
2572 } else if ((Bpb
.SectorsPerCluster
!= 1) &&
2573 (Bpb
.SectorsPerCluster
!= 2) &&
2574 (Bpb
.SectorsPerCluster
!= 4) &&
2575 (Bpb
.SectorsPerCluster
!= 8) &&
2576 (Bpb
.SectorsPerCluster
!= 16) &&
2577 (Bpb
.SectorsPerCluster
!= 32) &&
2578 (Bpb
.SectorsPerCluster
!= 64) &&
2579 (Bpb
.SectorsPerCluster
!= 128)) {
2584 // Likewise on the reserved sectors (must reflect at least the boot sector!)
2587 } else if (Bpb
.ReservedSectors
== 0) {
2592 // No FATs? Wrong ...
2595 } else if (Bpb
.Fats
== 0) {
2600 // Prior to DOS 3.2 might contains value in both of Sectors and
2604 } else if ((Bpb
.Sectors
== 0) && (Bpb
.LargeSectors
== 0)) {
2609 // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and
2610 // is of a version we recognize, currently Version 0.0.
2613 } else if (Bpb
.SectorsPerFat
== 0 && ( Bpb
.LargeSectorsPerFat
== 0 ||
2614 Bpb
.FsVersion
!= 0 )) {
2618 } else if ((Bpb
.Media
!= 0xf0) &&
2619 (Bpb
.Media
!= 0xf8) &&
2620 (Bpb
.Media
!= 0xf9) &&
2621 (Bpb
.Media
!= 0xfb) &&
2622 (Bpb
.Media
!= 0xfc) &&
2623 (Bpb
.Media
!= 0xfd) &&
2624 (Bpb
.Media
!= 0xfe) &&
2625 (Bpb
.Media
!= 0xff) &&
2626 (!FatData
.FujitsuFMR
|| ((Bpb
.Media
!= 0x00) &&
2627 (Bpb
.Media
!= 0x01) &&
2628 (Bpb
.Media
!= 0xfa)))) {
2633 // If this isn't FAT32, then there better be a claimed root directory
2637 } else if (Bpb
.SectorsPerFat
!= 0 && Bpb
.RootEntries
== 0) {
2642 // If this is FAT32 (i.e., extended BPB), look for and refuse to mount
2643 // mirror-disabled volumes. If we did, we would need to only write to
2644 // the FAT# indicated in the ActiveFat field. The only user of this is
2645 // the FAT->FAT32 converter after the first pass of protected mode work
2646 // (booting into realmode) and NT should absolutely not be attempting
2647 // to mount such an in-transition volume.
2650 } else if (Bpb
.SectorsPerFat
== 0 && Bpb
.MirrorDisabled
) {
2655 DebugTrace(-1, Dbg
, "FatIsBootSectorFat -> %08lx\n", Result
);
2662 // Local Support Routine
2666 FatIsMediaWriteProtected (
2667 IN PIRP_CONTEXT IrpContext
,
2668 IN PDEVICE_OBJECT TargetDeviceObject
2673 Routine Description:
2675 This routine determines if the target media is write protected.
2679 TargetDeviceObject - The target of the query
2683 NTSTATUS - The return status for the operation
2691 IO_STATUS_BLOCK Iosb
;
2694 UNREFERENCED_PARAMETER( IrpContext
);
2697 // Query the partition table
2700 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
2703 // See if the media is write protected. On success or any kind
2704 // of error (possibly illegal device function), assume it is
2705 // writeable, and only complain if he tells us he is write protected.
2708 Irp
= IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE
,
2719 // Just return FALSE in the unlikely event we couldn't allocate an Irp.
2722 if ( Irp
== NULL
) {
2727 SetFlag( IoGetNextIrpStackLocation( Irp
)->Flags
, SL_OVERRIDE_VERIFY_VOLUME
);
2729 Status
= IoCallDriver( TargetDeviceObject
, Irp
);
2731 if ( Status
== STATUS_PENDING
) {
2733 (VOID
) KeWaitForSingleObject( &Event
,
2737 (PLARGE_INTEGER
)NULL
);
2739 Status
= Iosb
.Status
;
2742 return (BOOLEAN
)(Status
== STATUS_MEDIA_WRITE_PROTECTED
);
2747 // Local Support Routine
2750 _Requires_lock_held_(_Global_critical_region_
)
2753 IN PIRP_CONTEXT IrpContext
,
2759 Routine Description:
2761 This is the common routine for implementing the user's requests made
2762 through NtFsControlFile.
2766 Irp - Supplies the Irp being processed
2770 NTSTATUS - The return status for the operation
2776 ULONG FsControlCode
;
2778 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2783 // Save some references to make our life a little easier
2786 FsControlCode
= IrpSp
->Parameters
.FileSystemControl
.FsControlCode
;
2788 DebugTrace(+1, Dbg
,"FatUserFsCtrl...\n", 0);
2789 DebugTrace( 0, Dbg
,"FsControlCode = %08lx\n", FsControlCode
);
2792 // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode
2793 // of the caller was userspace and this is a METHOD_NEITHER, we have the choice
2794 // of realy buffering the request through so we can possibly post, or making the
2795 // request synchronous. Since the former was not done by design, do the latter.
2798 if (Irp
->RequestorMode
!= KernelMode
&& (FsControlCode
& 3) == METHOD_NEITHER
) {
2800 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
2804 // Case on the control code.
2807 switch ( FsControlCode
) {
2809 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
2810 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
2811 case FSCTL_REQUEST_BATCH_OPLOCK
:
2812 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
2813 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
2814 case FSCTL_OPLOCK_BREAK_NOTIFY
:
2815 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
2816 case FSCTL_REQUEST_FILTER_OPLOCK
:
2817 #if (NTDDI_VERSION >= NTDDI_WIN7)
2818 case FSCTL_REQUEST_OPLOCK
:
2820 Status
= FatOplockRequest( IrpContext
, Irp
);
2823 case FSCTL_LOCK_VOLUME
:
2825 Status
= FatLockVolume( IrpContext
, Irp
);
2828 case FSCTL_UNLOCK_VOLUME
:
2830 Status
= FatUnlockVolume( IrpContext
, Irp
);
2833 case FSCTL_DISMOUNT_VOLUME
:
2835 Status
= FatDismountVolume( IrpContext
, Irp
);
2838 case FSCTL_MARK_VOLUME_DIRTY
:
2840 Status
= FatDirtyVolume( IrpContext
, Irp
);
2843 case FSCTL_IS_VOLUME_DIRTY
:
2845 Status
= FatIsVolumeDirty( IrpContext
, Irp
);
2848 case FSCTL_IS_VOLUME_MOUNTED
:
2850 Status
= FatIsVolumeMounted( IrpContext
, Irp
);
2853 case FSCTL_IS_PATHNAME_VALID
:
2854 Status
= FatIsPathnameValid( IrpContext
, Irp
);
2857 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
2858 Status
= FatQueryRetrievalPointers( IrpContext
, Irp
);
2861 case FSCTL_QUERY_FAT_BPB
:
2862 Status
= FatQueryBpb( IrpContext
, Irp
);
2865 case FSCTL_FILESYSTEM_GET_STATISTICS
:
2866 Status
= FatGetStatistics( IrpContext
, Irp
);
2869 #if (NTDDI_VERSION >= NTDDI_WIN7)
2870 case FSCTL_GET_RETRIEVAL_POINTER_BASE
:
2871 Status
= FatGetRetrievalPointerBase( IrpContext
, Irp
);
2874 case FSCTL_GET_BOOT_AREA_INFO
:
2875 Status
= FatGetBootAreaInfo( IrpContext
, Irp
);
2879 case FSCTL_GET_VOLUME_BITMAP
:
2880 Status
= FatGetVolumeBitmap( IrpContext
, Irp
);
2883 case FSCTL_GET_RETRIEVAL_POINTERS
:
2884 Status
= FatGetRetrievalPointers( IrpContext
, Irp
);
2887 case FSCTL_MOVE_FILE
:
2888 Status
= FatMoveFile( IrpContext
, Irp
);
2891 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
2892 Status
= FatAllowExtendedDasdIo( IrpContext
, Irp
);
2895 case FSCTL_MARK_HANDLE
:
2896 Status
= FatMarkHandle( IrpContext
, Irp
);
2899 #if (NTDDI_VERSION >= NTDDI_WIN8)
2901 case FSCTL_SET_PURGE_FAILURE_MODE
:
2902 Status
= FatSetPurgeFailureMode( IrpContext
, Irp
);
2908 #if (NTDDI_VERSION >= NTDDI_WIN7)
2909 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
2910 Status
= FatSetZeroOnDeallocate( IrpContext
, Irp
);
2916 DebugTrace(0, Dbg
, "Invalid control code -> %08lx\n", FsControlCode
);
2918 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
2919 Status
= STATUS_INVALID_DEVICE_REQUEST
;
2923 DebugTrace(-1, Dbg
, "FatUserFsCtrl -> %08lx\n", Status
);
2930 // Local support routine
2933 _Requires_lock_held_(_Global_critical_region_
)
2936 _In_ PIRP_CONTEXT IrpContext
,
2942 Routine Description:
2944 This is the common routine to handle oplock requests made via the
2945 NtFsControlFile call.
2949 Irp - Supplies the Irp being processed
2953 NTSTATUS - The return status for the operation
2958 NTSTATUS Status
= STATUS_SUCCESS
;
2959 ULONG FsControlCode
;
2964 ULONG OplockCount
= 0;
2966 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2968 BOOLEAN AcquiredVcb
= FALSE
;
2969 BOOLEAN AcquiredFcb
= FALSE
;
2971 #if (NTDDI_VERSION >= NTDDI_WIN7)
2972 PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer
= NULL
;
2973 ULONG InputBufferLength
;
2974 ULONG OutputBufferLength
;
2977 TYPE_OF_OPEN TypeOfOpen
;
2982 // Save some references to make our life a little easier
2985 FsControlCode
= IrpSp
->Parameters
.FileSystemControl
.FsControlCode
;
2987 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
2989 DebugTrace(+1, Dbg
, "FatOplockRequest...\n", 0);
2990 DebugTrace( 0, Dbg
, "FsControlCode = %08lx\n", FsControlCode
);
2993 // We permit oplock requests on files and directories.
2996 if ((TypeOfOpen
!= UserFileOpen
)
2997 #if (NTDDI_VERSION >= NTDDI_WIN8)
2999 (TypeOfOpen
!= UserDirectoryOpen
)
3003 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3004 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3005 return STATUS_INVALID_PARAMETER
;
3008 #if (NTDDI_VERSION >= NTDDI_WIN7)
3011 // Get the input & output buffer lengths and pointers.
3014 if (FsControlCode
== FSCTL_REQUEST_OPLOCK
) {
3016 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
3017 InputBuffer
= (PREQUEST_OPLOCK_INPUT_BUFFER
) Irp
->AssociatedIrp
.SystemBuffer
;
3019 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
3022 // Check for a minimum length on the input and ouput buffers.
3025 if ((InputBufferLength
< sizeof( REQUEST_OPLOCK_INPUT_BUFFER
)) ||
3026 (OutputBufferLength
< sizeof( REQUEST_OPLOCK_OUTPUT_BUFFER
))) {
3028 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
3029 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_BUFFER_TOO_SMALL\n", 0);
3030 return STATUS_BUFFER_TOO_SMALL
;
3035 // If the oplock request is on a directory it must be for a Read or Read-Handle
3039 if ((TypeOfOpen
== UserDirectoryOpen
) &&
3040 ((FsControlCode
!= FSCTL_REQUEST_OPLOCK
) ||
3041 !FsRtlOplockIsSharedRequest( Irp
))) {
3043 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3044 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3045 return STATUS_INVALID_PARAMETER
;
3051 // Make this a waitable Irpcontext so we don't fail to acquire
3055 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3058 // Use a try finally to free the Fcb/Vcb
3064 // We grab the Fcb exclusively for oplock requests, shared for oplock
3065 // break acknowledgement.
3068 if ((FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_1
) ||
3069 (FsControlCode
== FSCTL_REQUEST_BATCH_OPLOCK
) ||
3070 (FsControlCode
== FSCTL_REQUEST_FILTER_OPLOCK
) ||
3071 (FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_2
)
3072 #if (NTDDI_VERSION >= NTDDI_WIN7)
3074 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->Flags
, REQUEST_OPLOCK_INPUT_FLAG_REQUEST
))
3078 FatAcquireSharedVcb( IrpContext
, Fcb
->Vcb
);
3080 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
3083 #if (NTDDI_VERSION >= NTDDI_WIN7)
3084 if (FsRtlOplockIsSharedRequest( Irp
)) {
3086 if (FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_2
) {
3090 // Byte-range locks are only valid on files.
3093 if (TypeOfOpen
== UserFileOpen
) {
3096 // Set OplockCount to nonzero if FsRtl denies access
3097 // based on current byte-range lock state.
3100 #if (NTDDI_VERSION >= NTDDI_WIN8)
3101 OplockCount
= (ULONG
) !FsRtlCheckLockForOplockRequest( &Fcb
->Specific
.Fcb
.FileLock
, &Fcb
->Header
.AllocationSize
);
3102 #elif (NTDDI_VERSION >= NTDDI_WIN7)
3103 OplockCount
= (ULONG
) FsRtlAreThereCurrentOrInProgressFileLocks( &Fcb
->Specific
.Fcb
.FileLock
);
3105 OplockCount
= (ULONG
) FsRtlAreThereCurrentFileLocks( &Fcb
->Specific
.Fcb
.FileLock
);
3112 OplockCount
= Fcb
->UncleanCount
;
3115 } else if ((FsControlCode
== FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
) ||
3116 (FsControlCode
== FSCTL_OPBATCH_ACK_CLOSE_PENDING
) ||
3117 (FsControlCode
== FSCTL_OPLOCK_BREAK_NOTIFY
) ||
3118 (FsControlCode
== FSCTL_OPLOCK_BREAK_ACK_NO_2
)
3119 #if (NTDDI_VERSION >= NTDDI_WIN7)
3121 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->Flags
, REQUEST_OPLOCK_INPUT_FLAG_ACK
))
3125 FatAcquireSharedFcb( IrpContext
, Fcb
);
3127 #if (NTDDI_VERSION >= NTDDI_WIN7)
3128 } else if (FsControlCode
== FSCTL_REQUEST_OPLOCK
) {
3131 // The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or
3132 // REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer.
3135 try_leave( Status
= STATUS_INVALID_PARAMETER
);
3143 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
3145 FatBugCheck( FsControlCode
, 0, 0 );
3149 // Fail batch, filter, and handle oplock requests if the file is marked
3153 if (((FsControlCode
== FSCTL_REQUEST_FILTER_OPLOCK
) ||
3154 (FsControlCode
== FSCTL_REQUEST_BATCH_OPLOCK
)
3155 #if (NTDDI_VERSION >= NTDDI_WIN7)
3157 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->RequestedOplockLevel
, OPLOCK_LEVEL_CACHE_HANDLE
))
3160 FlagOn( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
)) {
3162 try_leave( Status
= STATUS_DELETE_PENDING
);
3166 // Call the FsRtl routine to grant/acknowledge oplock.
3169 Status
= FsRtlOplockFsctrl( FatGetFcbOplock(Fcb
),
3174 // Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should not complete it.
3180 // Set the flag indicating if Fast I/O is possible
3183 Fcb
->Header
.IsFastIoPossible
= FatIsFastIoPossible( Fcb
);
3187 DebugUnwind( FatOplockRequest
);
3190 // Release all of our resources
3195 FatReleaseVcb( IrpContext
, Fcb
->Vcb
);
3200 FatReleaseFcb( IrpContext
, Fcb
);
3203 DebugTrace(-1, Dbg
, "FatOplockRequest -> %08lx\n", Status
);
3206 FatCompleteRequest( IrpContext
, Irp
, Status
);
3213 // Local Support Routine
3216 _Requires_lock_held_(_Global_critical_region_
)
3219 IN PIRP_CONTEXT IrpContext
,
3225 Routine Description:
3227 This routine performs the lock volume operation. It is responsible for
3228 either completing of enqueuing the input Irp.
3232 Irp - Supplies the Irp to process
3236 NTSTATUS - The return status for the operation
3241 NTSTATUS Status
= STATUS_SUCCESS
;
3243 PIO_STACK_LOCATION IrpSp
;
3251 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3253 DebugTrace(+1, Dbg
, "FatLockVolume...\n", 0);
3256 // Decode the file object, the only type of opens we accept are
3257 // user volume opens.
3260 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3262 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3264 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3265 return STATUS_INVALID_PARAMETER
;
3268 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3270 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3272 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3273 return STATUS_INVALID_PARAMETER
;
3277 // Send our notification so that folks that like to hold handles on
3278 // volumes can get out of the way.
3281 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
3284 // Acquire exclusive access to the Vcb and enqueue the Irp if we
3285 // didn't get access.
3288 if (!FatAcquireExclusiveVcb( IrpContext
, Vcb
)) {
3290 DebugTrace( 0, Dbg
, "Cannot acquire Vcb\n", 0);
3292 Status
= FatFsdPostRequest( IrpContext
, Irp
);
3294 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", Status
);
3300 Status
= FatLockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
3305 // Since we drop and release the vcb while trying to punch the volume
3306 // down, it may be the case that we decide the operation should not
3307 // continue if the user raced a CloeseHandle() with us (and it finished
3308 // the cleanup) while we were waiting for our closes to finish.
3310 // In this case, we will have been raised out of the acquire logic with
3311 // STATUS_FILE_CLOSED, and the volume will not be held.
3314 if (!_SEH2_AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb
->Resource
)) {
3316 FatReleaseVcb( IrpContext
, Vcb
);
3319 if (!NT_SUCCESS( Status
) || _SEH2_AbnormalTermination()) {
3322 // The volume lock will be failing.
3325 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
3329 FatCompleteRequest( IrpContext
, Irp
, Status
);
3331 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", Status
);
3338 // Local Support Routine
3343 IN PIRP_CONTEXT IrpContext
,
3349 Routine Description:
3351 This routine performs the unlock volume operation. It is responsible for
3352 either completing of enqueuing the input Irp.
3356 Irp - Supplies the Irp to process
3360 NTSTATUS - The return status for the operation
3367 PIO_STACK_LOCATION IrpSp
;
3375 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3377 DebugTrace(+1, Dbg
, "FatUnlockVolume...\n", 0);
3380 // Decode the file object, the only type of opens we accept are
3381 // user volume opens.
3384 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3386 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3388 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3389 return STATUS_INVALID_PARAMETER
;
3392 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3394 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3396 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3397 return STATUS_INVALID_PARAMETER
;
3400 Status
= FatUnlockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
3403 // Send notification that the volume is avaliable.
3406 if (NT_SUCCESS( Status
)) {
3408 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
3411 FatCompleteRequest( IrpContext
, Irp
, Status
);
3413 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", Status
);
3419 _Requires_lock_held_(_Global_critical_region_
)
3421 FatLockVolumeInternal (
3422 IN PIRP_CONTEXT IrpContext
,
3424 IN PFILE_OBJECT FileObject OPTIONAL
3429 Routine Description:
3431 This routine performs the actual lock volume operation. It will be called
3432 by anyone wishing to try to protect the volume for a long duration. PNP
3433 operations are such a user.
3435 The volume must be held exclusive by the caller.
3439 Vcb - The volume being locked.
3441 FileObject - File corresponding to the handle locking the volume. If this
3442 is not specified, a system lock is assumed.
3446 NTSTATUS - The return status for the operation
3451 NTSTATUS Status
= STATUS_SUCCESS
;
3453 ULONG RemainingUserReferences
= (FileObject
? 1: 0);
3455 NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb
->Resource
) &&
3456 !ExIsResourceAcquiredExclusiveLite( &FatData
.Resource
));
3458 // Go synchronous for the rest of the lock operation. It may be
3459 // reasonable to try to revisit this in the future, but for now
3460 // the purge below expects to be able to wait.
3462 // We know it is OK to leave the flag up given how we're used at
3466 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3469 // If there are any open handles, this will fail.
3472 if (!FatIsHandleCountZero( IrpContext
, Vcb
)) {
3474 return STATUS_ACCESS_DENIED
;
3478 // Force Mm to get rid of its referenced file objects.
3481 FatFlushFat( IrpContext
, Vcb
);
3483 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
3485 FatCloseEaFile( IrpContext
, Vcb
, TRUE
);
3488 // Now back out of our synchronization and wait for the lazy writer
3489 // to finish off any lazy closes that could have been outstanding.
3491 // Since we flushed, we know that the lazy writer will issue all
3492 // possible lazy closes in the next tick - if we hadn't, an otherwise
3493 // unopened file with a large amount of dirty data could have hung
3494 // around for a while as the data trickled out to the disk.
3496 // This is even more important now since we send notification to
3497 // alert other folks that this style of check is about to happen so
3498 // that they can close their handles. We don't want to enter a fast
3499 // race with the lazy writer tearing down his references to the file.
3502 FatReleaseVcb( IrpContext
, Vcb
);
3504 Status
= CcWaitForCurrentLazyWriterActivity();
3506 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
3508 if (!NT_SUCCESS( Status
)) {
3514 // The act of closing and purging may have touched pages in various
3515 // parent DCBs. We handle this by purging a second time.
3518 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
3520 FatReleaseVcb( IrpContext
, Vcb
);
3522 Status
= CcWaitForCurrentLazyWriterActivity();
3524 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
3526 if (!NT_SUCCESS( Status
)) {
3532 // Now rundown the delayed closes one last time. We appear to be able
3533 // to have additional collisions.
3539 // Check if the Vcb is already locked, or if the open file count
3540 // is greater than 1 (which implies that someone else also is
3541 // currently using the volume, or a file on the volume), and that the
3542 // VPB reference count only includes our residual and the handle (as
3545 // We used to only check for the vpb refcount. This is unreliable since
3546 // the vpb refcount is dropped immediately before final close, meaning
3547 // that even though we had a good refcount, the close was inflight and
3548 // subsequent operations could get confused. Especially if the PNP path
3549 // was the lock caller, we delete the VCB with an outstanding opencount!
3552 IoAcquireVpbSpinLock( &SavedIrql
);
3554 if (!FlagOn(Vcb
->Vpb
->Flags
, VPB_LOCKED
) &&
3555 (Vcb
->Vpb
->ReferenceCount
<= 2 + RemainingUserReferences
) &&
3556 (Vcb
->OpenFileCount
== (CLONG
)( FileObject
? 1: 0 ))) {
3558 SetFlag(Vcb
->Vpb
->Flags
, (VPB_LOCKED
| VPB_DIRECT_WRITES_ALLOWED
));
3559 SetFlag(Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
);
3560 Vcb
->FileObjectWithVcbLocked
= FileObject
;
3564 Status
= STATUS_ACCESS_DENIED
;
3567 IoReleaseVpbSpinLock( SavedIrql
);
3570 // If we successully locked the volume, see if it is clean now.
3573 if (NT_SUCCESS( Status
) &&
3574 FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
) &&
3575 !FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
) &&
3576 !CcIsThereDirtyData(Vcb
->Vpb
)) {
3578 FatMarkVolume( IrpContext
, Vcb
, VolumeClean
);
3579 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
);
3582 NT_ASSERT( !NT_SUCCESS(Status
) || (Vcb
->OpenFileCount
== (CLONG
)( FileObject
? 1: 0 )));
3589 FatUnlockVolumeInternal (
3590 IN PIRP_CONTEXT IrpContext
,
3592 IN PFILE_OBJECT FileObject OPTIONAL
3597 Routine Description:
3599 This routine performs the actual unlock volume operation.
3601 The volume must be held exclusive by the caller.
3605 Vcb - The volume being locked.
3607 FileObject - File corresponding to the handle locking the volume. If this
3608 is not specified, a system lock is assumed.
3612 NTSTATUS - The return status for the operation
3614 Attempting to remove a system lock that did not exist is OK.
3620 NTSTATUS Status
= STATUS_NOT_LOCKED
;
3622 UNREFERENCED_PARAMETER( IrpContext
);
3624 IoAcquireVpbSpinLock( &SavedIrql
);
3626 if (FlagOn(Vcb
->Vpb
->Flags
, VPB_LOCKED
) && FileObject
== Vcb
->FileObjectWithVcbLocked
) {
3629 // This one locked it, unlock the volume
3632 ClearFlag( Vcb
->Vpb
->Flags
, (VPB_LOCKED
| VPB_DIRECT_WRITES_ALLOWED
) );
3633 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
);
3634 Vcb
->FileObjectWithVcbLocked
= NULL
;
3636 Status
= STATUS_SUCCESS
;
3639 IoReleaseVpbSpinLock( SavedIrql
);
3646 // Local Support Routine
3649 _Requires_lock_held_(_Global_critical_region_
)
3652 IN PIRP_CONTEXT IrpContext
,
3658 Routine Description:
3660 This routine performs the dismount volume operation. It is responsible for
3661 either completing of enqueuing the input Irp.
3665 Irp - Supplies the Irp to process
3669 NTSTATUS - The return status for the operation
3674 PIO_STACK_LOCATION IrpSp
;
3675 NTSTATUS Status
= STATUS_SUCCESS
;
3676 BOOLEAN VcbHeld
= FALSE
;
3683 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3685 DebugTrace(+1, Dbg
, "FatDismountVolume...\n", 0);
3688 // Decode the file object, the only type of opens we accept are
3689 // user volume opens on media that is not boot/paging and is not
3690 // already dismounted ... (but we need to check that stuff while
3694 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3696 Status
= STATUS_INVALID_PARAMETER
;
3700 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3702 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3704 DebugTrace(-1, Dbg
, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3705 return STATUS_INVALID_PARAMETER
;
3709 // Make some unsynchronized checks to see if this operation is possible.
3710 // We will repeat the appropriate ones inside synchronization, but it is
3711 // good to avoid bogus notifications.
3714 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
)) {
3716 Status
= STATUS_ACCESS_DENIED
;
3720 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
)) {
3722 Status
= STATUS_VOLUME_DISMOUNTED
;
3727 // A bit of historical comment is in order.
3729 // In all versions prior to NT5, we only permitted dismount if the volume had
3730 // previously been locked. Now we must permit a forced dismount, meaning that
3731 // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open
3732 // handles, etc. - to flush and invalidate the volume.
3734 // Previously, dismount assumed that lock had come along earlier and done some
3735 // of the work that we are now going to do - i.e., flush, tear down the eas. All
3736 // we had to do here is flush the device out and kill off as many of the orphan
3737 // fcbs as possible. This now changes.
3739 // In fact, everything is a forced dismount now. This changes one interesting
3740 // aspect, which is that it used to be the case that the handle used to dismount
3741 // could come back, read, and induce a verify/remount. This is just not possible
3742 // now. The point of forced dismount is that very shortly someone will come along
3743 // and be destructive to the possibility of using the media further - format, eject,
3744 // etc. By using this path, callers are expected to tolerate the consequences.
3746 // Note that the volume can still be successfully unlocked by this handle.
3750 // Send notification.
3753 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_DISMOUNT
);
3756 // Force ourselves to wait and grab everything.
3759 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3762 #pragma prefast( push )
3763 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
3764 #pragma prefast( disable: 28193, "this will always wait" )
3767 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
3770 #pragma prefast( pop )
3776 // Guess what? This can raise if a cleanup on the fileobject we
3777 // got races in ahead of us.
3780 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
3783 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
)) {
3785 try_return( Status
= STATUS_VOLUME_DISMOUNTED
);
3788 FatFlushAndCleanVolume( IrpContext
, Irp
, Vcb
, FlushAndInvalidate
);
3791 // We defer the physical dismount until this handle is closed, per symmetric
3792 // implemntation in the other FS. This permits a dismounter to issue IOCTL
3793 // through this handle and perform device manipulation without racing with
3794 // creates attempting to mount the volume again.
3796 // Raise a flag to tell the cleanup path to complete the dismount.
3799 SetFlag( Ccb
->Flags
, CCB_FLAG_COMPLETE_DISMOUNT
);
3802 // Indicate that the volume was dismounted so that we may return the
3803 // correct error code when operations are attempted via open handles.
3806 FatSetVcbCondition( Vcb
, VcbBad
);
3808 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
);
3811 // Set a flag in the VPB to let others know that direct volume access is allowed.
3814 IoAcquireVpbSpinLock( &SavedIrql
);
3815 SetFlag( Vcb
->Vpb
->Flags
, VPB_DIRECT_WRITES_ALLOWED
);
3816 IoReleaseVpbSpinLock( SavedIrql
);
3818 Status
= STATUS_SUCCESS
;
3824 #if (NTDDI_VERSION >= NTDDI_WIN8)
3826 FsRtlDismountComplete( Vcb
->TargetDeviceObject
, Status
);
3832 FatReleaseVolume( IrpContext
, Vcb
);
3835 FatReleaseGlobal( IrpContext
);
3838 // I do not believe it is possible to raise, but for completeness
3839 // notice and send notification of failure. We absolutely
3840 // cannot have raised in CheckForDismount.
3842 // We decline to call an attempt to dismount a dismounted volume
3843 // a failure to do so.
3846 if ((!NT_SUCCESS( Status
) && Status
!= STATUS_VOLUME_DISMOUNTED
)
3847 || _SEH2_AbnormalTermination()) {
3849 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_DISMOUNT_FAILED
);
3855 FatCompleteRequest( IrpContext
, Irp
, Status
);
3856 DebugTrace(-1, Dbg
, "FatDismountVolume -> %08lx\n", Status
);
3862 // Local Support Routine
3865 _Requires_lock_held_(_Global_critical_region_
)
3868 IN PIRP_CONTEXT IrpContext
,
3874 Routine Description:
3876 This routine marks the volume as dirty.
3880 Irp - Supplies the Irp to process
3884 NTSTATUS - The return status for the operation
3889 PIO_STACK_LOCATION IrpSp
;
3897 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3899 DebugTrace(+1, Dbg
, "FatDirtyVolume...\n", 0);
3902 // Decode the file object, the only type of opens we accept are
3903 // user volume opens.
3906 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3908 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3910 DebugTrace(-1, Dbg
, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3911 return STATUS_INVALID_PARAMETER
;
3914 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3916 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3918 DebugTrace(-1, Dbg
, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3919 return STATUS_INVALID_PARAMETER
;
3924 // Disable popups, we will just return any error.
3927 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
3930 // Verify the Vcb. We want to make sure we don't dirty some
3931 // random chunk of media that happens to be in the drive now.
3934 FatVerifyVcb( IrpContext
, Vcb
);
3936 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
);
3938 FatMarkVolume( IrpContext
, Vcb
, VolumeDirty
);
3940 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
3942 DebugTrace(-1, Dbg
, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
3944 return STATUS_SUCCESS
;
3949 // Local Support Routine
3954 IN PIRP_CONTEXT IrpContext
,
3960 Routine Description:
3962 This routine determines if a volume is currently dirty.
3966 Irp - Supplies the Irp to process
3970 NTSTATUS - The return status for the operation
3975 PIO_STACK_LOCATION IrpSp
;
3977 TYPE_OF_OPEN TypeOfOpen
;
3987 // Get the current stack location and extract the output
3988 // buffer information.
3991 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3994 // Get a pointer to the output buffer. Look at the system buffer field in the
3995 // irp first. Then the Irp Mdl.
3998 if (Irp
->AssociatedIrp
.SystemBuffer
!= NULL
) {
4000 VolumeState
= Irp
->AssociatedIrp
.SystemBuffer
;
4002 } else if (Irp
->MdlAddress
!= NULL
) {
4004 VolumeState
= MmGetSystemAddressForMdlSafe( Irp
->MdlAddress
, LowPagePriority
| MdlMappingNoExecute
);
4006 if (VolumeState
== NULL
) {
4008 FatCompleteRequest( IrpContext
, Irp
, STATUS_INSUFFICIENT_RESOURCES
);
4009 return STATUS_INSUFFICIENT_RESOURCES
;
4014 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_USER_BUFFER
);
4015 return STATUS_INVALID_USER_BUFFER
;
4019 // Make sure the output buffer is large enough and then initialize
4020 // the answer to be that the volume isn't dirty.
4023 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
)) {
4025 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4026 return STATUS_INVALID_PARAMETER
;
4032 // Decode the file object
4035 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4037 if (TypeOfOpen
!= UserVolumeOpen
) {
4039 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4040 return STATUS_INVALID_PARAMETER
;
4043 if (Vcb
->VcbCondition
!= VcbGood
) {
4045 FatCompleteRequest( IrpContext
, Irp
, STATUS_VOLUME_DISMOUNTED
);
4046 return STATUS_VOLUME_DISMOUNTED
;
4050 // Disable PopUps, we want to return any error.
4053 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
4056 // Verify the Vcb. We want to make double sure that this volume
4057 // is around so that we know our information is good.
4060 FatVerifyVcb( IrpContext
, Vcb
);
4063 // Now set the returned information. We can avoid probing the disk since
4064 // we know our internal state is in sync.
4067 if ( FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
) ) {
4069 SetFlag( *VolumeState
, VOLUME_IS_DIRTY
);
4072 Irp
->IoStatus
.Information
= sizeof( ULONG
);
4074 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4075 return STATUS_SUCCESS
;
4080 // Local Support Routine
4084 FatIsVolumeMounted (
4085 IN PIRP_CONTEXT IrpContext
,
4091 Routine Description:
4093 This routine determines if a volume is currently mounted.
4097 Irp - Supplies the Irp to process
4101 NTSTATUS - The return status for the operation
4108 PIO_STACK_LOCATION IrpSp
;
4116 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4118 Status
= STATUS_SUCCESS
;
4120 DebugTrace(+1, Dbg
, "FatIsVolumeMounted...\n", 0);
4123 // Decode the file object.
4126 (VOID
)FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4128 NT_ASSERT( Vcb
!= NULL
);
4129 _Analysis_assume_( Vcb
!= NULL
);
4132 // Disable PopUps, we want to return any error.
4135 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
4141 FatVerifyVcb( IrpContext
, Vcb
);
4143 FatCompleteRequest( IrpContext
, Irp
, Status
);
4145 DebugTrace(-1, Dbg
, "FatIsVolumeMounted -> %08lx\n", Status
);
4152 // Local Support Routine
4156 FatIsPathnameValid (
4157 IN PIRP_CONTEXT IrpContext
,
4163 Routine Description:
4165 This routine determines if a pathname is a-priori illegal by inspecting
4166 the the characters used. It is required to be correct on a FALSE return.
4168 N.B.: current implementation is intentioanlly a no-op. This may change
4169 in the future. A careful reader of the previous implementation of this
4170 FSCTL in FAT would discover that it violated the requirement stated above
4171 and could return FALSE for a valid (createable) pathname.
4175 Irp - Supplies the Irp to process
4179 NTSTATUS - The return status for the operation
4186 DebugTrace(+1, Dbg
, "FatIsPathnameValid...\n", 0);
4188 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4190 DebugTrace(-1, Dbg
, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS
);
4192 return STATUS_SUCCESS
;
4197 // Local Support Routine
4202 IN PIRP_CONTEXT IrpContext
,
4208 Routine Description:
4210 This routine simply returns the first 0x24 bytes of sector 0.
4214 Irp - Supplies the Irp to process
4218 NTSTATUS - The return status for the operation
4223 PIO_STACK_LOCATION IrpSp
;
4227 PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer
;
4231 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4233 DebugTrace(+1, Dbg
, "FatQueryBpb...\n", 0);
4236 // Get the Vcb. If we didn't keep the information needed for this call,
4237 // we had a reason ...
4240 Vcb
= &((PVOLUME_DEVICE_OBJECT
)IrpSp
->DeviceObject
)->Vcb
;
4242 if (Vcb
->First0x24BytesOfBootSector
== NULL
) {
4244 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
4245 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST
);
4246 return STATUS_INVALID_DEVICE_REQUEST
;
4250 // Extract the buffer
4253 BpbBuffer
= (PFSCTL_QUERY_FAT_BPB_BUFFER
)Irp
->AssociatedIrp
.SystemBuffer
;
4256 // Make sure the buffer is big enough.
4259 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< 0x24) {
4261 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
4262 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL
);
4263 return STATUS_BUFFER_TOO_SMALL
;
4267 // Fill in the output buffer
4270 RtlCopyMemory( BpbBuffer
->First0x24BytesOfBootSector
,
4271 Vcb
->First0x24BytesOfBootSector
,
4274 Irp
->IoStatus
.Information
= 0x24;
4276 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4277 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS
);
4278 return STATUS_SUCCESS
;
4283 // Local Support Routine
4286 _Requires_lock_held_(_Global_critical_region_
)
4288 FatInvalidateVolumes (
4294 Routine Description:
4296 This routine searches for all the volumes mounted on the same real device
4297 of the current DASD handle, and marks them all bad. The only operation
4298 that can be done on such handles is cleanup and close.
4302 Irp - Supplies the Irp to process
4306 NTSTATUS - The return status for the operation
4312 IRP_CONTEXT IrpContext
;
4313 PIO_STACK_LOCATION IrpSp
;
4315 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
4321 PFILE_OBJECT FileToMarkBad
;
4322 PDEVICE_OBJECT DeviceToMarkBad
;
4324 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4326 DebugTrace(+1, Dbg
, "FatInvalidateVolumes...\n", 0);
4329 // Check for the correct security access.
4330 // The caller must have the SeTcbPrivilege.
4333 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
)) {
4335 FatCompleteRequest( FatNull
, Irp
, STATUS_PRIVILEGE_NOT_HELD
);
4337 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD
);
4338 return STATUS_PRIVILEGE_NOT_HELD
;
4342 // Try to get a pointer to the device object from the handle passed in.
4345 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4346 if (IoIs32bitProcess( Irp
)) {
4348 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
)) {
4350 FatCompleteRequest( FatNull
, Irp
, STATUS_INVALID_PARAMETER
);
4352 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER
);
4353 return STATUS_INVALID_PARAMETER
;
4356 Handle
= (HANDLE
) LongToHandle( (*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
) );
4359 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
)) {
4361 FatCompleteRequest( FatNull
, Irp
, STATUS_INVALID_PARAMETER
);
4363 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER
);
4364 return STATUS_INVALID_PARAMETER
;
4367 Handle
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
4368 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4373 Status
= ObReferenceObjectByHandle( Handle
,
4380 (PVOID
*)&FileToMarkBad
,
4384 if (!NT_SUCCESS(Status
)) {
4386 FatCompleteRequest( FatNull
, Irp
, Status
);
4388 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", Status
);
4394 // We only needed the pointer, not a reference.
4397 ObDereferenceObject( FileToMarkBad
);
4400 // Grab the DeviceObject from the FileObject.
4403 DeviceToMarkBad
= FileToMarkBad
->DeviceObject
;
4406 RtlZeroMemory( &IrpContext
, sizeof(IRP_CONTEXT
) );
4408 SetFlag( IrpContext
.Flags
, IRP_CONTEXT_FLAG_WAIT
);
4409 IrpContext
.MajorFunction
= IrpSp
->MajorFunction
;
4410 IrpContext
.MinorFunction
= IrpSp
->MinorFunction
;
4413 #pragma prefast( push )
4414 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
4415 #pragma prefast( disable: 28193, "this will always wait" )
4418 FatAcquireExclusiveGlobal( &IrpContext
);
4421 #pragma prefast( pop )
4425 // First acquire the FatData resource shared, then walk through all the
4426 // mounted VCBs looking for candidates to mark BAD.
4428 // On volumes we mark bad, check for dismount possibility (which is
4429 // why we have to get the next link early).
4432 Links
= FatData
.VcbQueue
.Flink
;
4434 while (Links
!= &FatData
.VcbQueue
) {
4438 ExistingVcb
= CONTAINING_RECORD(Links
, VCB
, VcbLinks
);
4440 Links
= Links
->Flink
;
4443 // If we get a match, mark the volume Bad, and also check to
4444 // see if the volume should go away.
4447 if (ExistingVcb
->Vpb
->RealDevice
== DeviceToMarkBad
) {
4449 BOOLEAN VcbDeleted
= FALSE
;
4452 // Here we acquire the Vcb exclusive and try to purge
4453 // all the open files. The idea is to have as little as
4454 // possible stale data visible and to hasten the volume
4458 (VOID
)FatAcquireExclusiveVcb( &IrpContext
, ExistingVcb
);
4461 #pragma prefast( push )
4462 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
4465 if (ExistingVcb
->Vpb
== DeviceToMarkBad
->Vpb
) {
4469 IoAcquireVpbSpinLock( &OldIrql
);
4471 if (FlagOn( DeviceToMarkBad
->Vpb
->Flags
, VPB_MOUNTED
)) {
4475 NewVpb
= ExistingVcb
->SwapVpb
;
4476 ExistingVcb
->SwapVpb
= NULL
;
4477 SetFlag( ExistingVcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
4479 RtlZeroMemory( NewVpb
, sizeof( VPB
) );
4480 NewVpb
->Type
= IO_TYPE_VPB
;
4481 NewVpb
->Size
= sizeof( VPB
);
4482 NewVpb
->RealDevice
= DeviceToMarkBad
;
4483 NewVpb
->Flags
= FlagOn( DeviceToMarkBad
->Vpb
->Flags
, VPB_REMOVE_PENDING
);
4485 DeviceToMarkBad
->Vpb
= NewVpb
;
4488 NT_ASSERT( DeviceToMarkBad
->Vpb
->DeviceObject
== NULL
);
4491 #pragma prefast( pop )
4494 IoReleaseVpbSpinLock( OldIrql
);
4497 FatSetVcbCondition( ExistingVcb
, VcbBad
);
4500 // Process the root directory, if it is present.
4503 if (ExistingVcb
->RootDcb
!= NULL
) {
4506 // In order to safely mark all FCBs bad, we must acquire everything.
4509 FatAcquireExclusiveVolume(&IrpContext
, ExistingVcb
);
4510 FatMarkFcbCondition( &IrpContext
, ExistingVcb
->RootDcb
, FcbBad
, TRUE
);
4511 FatReleaseVolume(&IrpContext
, ExistingVcb
);
4514 // Purging the file objects on this volume could result in the memory manager
4515 // dereferencing it's file pointer which could be the last reference and
4516 // trigger object deletion and VCB deletion. Protect against that here by
4517 // temporarily biasing the file count, and later checking for dismount.
4520 ExistingVcb
->OpenFileCount
+= 1;
4522 FatPurgeReferencedFileObjects( &IrpContext
,
4523 ExistingVcb
->RootDcb
,
4526 ExistingVcb
->OpenFileCount
-= 1;
4528 VcbDeleted
= FatCheckForDismount( &IrpContext
, ExistingVcb
, FALSE
);
4532 // Only release the VCB if it did not go away.
4537 FatReleaseVcb( &IrpContext
, ExistingVcb
);
4542 FatReleaseGlobal( &IrpContext
);
4544 FatCompleteRequest( FatNull
, Irp
, STATUS_SUCCESS
);
4546 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
4548 return STATUS_SUCCESS
;
4553 // Local Support routine
4557 FatPerformVerifyDiskRead (
4558 IN PIRP_CONTEXT IrpContext
,
4562 IN ULONG NumberOfBytesToRead
,
4563 IN BOOLEAN ReturnOnError
4568 Routine Description:
4570 This routine is used to read in a range of bytes from the disk. It
4571 bypasses all of the caching and regular I/O logic, and builds and issues
4572 the requests itself. It does this operation overriding the verify
4573 volume flag in the device object.
4577 Vcb - Supplies the target device object for this operation.
4579 Buffer - Supplies the buffer that will recieve the results of this operation
4581 Lbo - Supplies the byte offset of where to start reading
4583 NumberOfBytesToRead - Supplies the number of bytes to read, this must
4584 be in multiple of bytes units acceptable to the disk driver.
4586 ReturnOnError - Indicates that we should return on an error, instead
4591 BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
4598 LARGE_INTEGER ByteOffset
;
4600 IO_STATUS_BLOCK Iosb
;
4604 DebugTrace(0, Dbg
, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo
);
4607 // Initialize the event we're going to use
4610 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
4613 // Build the irp for the operation and also set the overrride flag
4616 ByteOffset
.QuadPart
= Lbo
;
4618 Irp
= IoBuildSynchronousFsdRequest( IRP_MJ_READ
,
4619 Vcb
->TargetDeviceObject
,
4621 NumberOfBytesToRead
,
4626 if ( Irp
== NULL
) {
4628 FatRaiseStatus( IrpContext
, STATUS_INSUFFICIENT_RESOURCES
);
4631 SetFlag( IoGetNextIrpStackLocation( Irp
)->Flags
, SL_OVERRIDE_VERIFY_VOLUME
);
4634 // Call the device to do the read and wait for it to finish.
4637 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, Irp
);
4639 if (Status
== STATUS_PENDING
) {
4641 (VOID
)KeWaitForSingleObject( &Event
, Executive
, KernelMode
, FALSE
, (PLARGE_INTEGER
)NULL
);
4643 Status
= Iosb
.Status
;
4646 NT_ASSERT( Status
!= STATUS_VERIFY_REQUIRED
);
4649 // Special case this error code because this probably means we used
4650 // the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
4653 if (Status
== STATUS_INVALID_PARAMETER
) {
4659 // If it doesn't succeed then either return or raise the error.
4662 if (!NT_SUCCESS(Status
)) {
4664 if (ReturnOnError
) {
4670 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
4675 // And return to our caller
4683 // Local Support Routine
4686 _Requires_lock_held_(_Global_critical_region_
)
4688 FatQueryRetrievalPointers (
4689 IN PIRP_CONTEXT IrpContext
,
4695 Routine Description:
4697 This routine performs the query retrieval pointers operation.
4698 It returns the retrieval pointers for the specified input
4699 file from the start of the file to the request map size specified
4700 in the input buffer.
4704 Irp - Supplies the Irp to process
4708 NTSTATUS - The return status for the operation
4713 NTSTATUS Status
= STATUS_SUCCESS
;
4715 PIO_STACK_LOCATION IrpSp
;
4721 PLARGE_INTEGER RequestedMapSize
;
4722 PLARGE_INTEGER
*MappingPairs
;
4735 // Get the current stack location
4738 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4741 // Make this a synchronous IRP because we need access to the input buffer and
4742 // this Irp is marked METHOD_NEITHER.
4745 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
4748 // Decode the file object and ensure that it is the paging file
4750 // Only Kernel mode clients may query retrieval pointer information about
4751 // a file. Ensure that this is the case for this caller.
4754 (VOID
)FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4756 if (Irp
->RequestorMode
!= KernelMode
||
4758 !FlagOn(Fcb
->FcbState
, FCB_STATE_PAGING_FILE
) ) {
4760 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4761 return STATUS_INVALID_PARAMETER
;
4765 // Extract the input and output buffer information. The input contains
4766 // the requested size of the mappings in terms of VBO. The output
4767 // parameter will receive a pointer to nonpaged pool where the mapping
4768 // pairs are stored.
4771 NT_ASSERT( IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
== sizeof(LARGE_INTEGER
) );
4772 NT_ASSERT( IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
== sizeof(PVOID
) );
4774 RequestedMapSize
= IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
;
4775 MappingPairs
= Irp
->UserBuffer
;
4778 // Acquire exclusive access to the Fcb
4781 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
4786 // Verify the Fcb is still OK
4789 FatVerifyFcb( IrpContext
, Fcb
);
4792 // Check if the mapping the caller requested is too large
4795 if ((*RequestedMapSize
).QuadPart
> Fcb
->Header
.FileSize
.QuadPart
) {
4797 try_leave( Status
= STATUS_INVALID_PARAMETER
);
4801 // Now get the index for the mcb entry that will contain the
4802 // callers request and allocate enough pool to hold the
4803 // output mapping pairs. Mapping should always be present, but handle
4804 // the case where it isn't.
4807 Result
= FatLookupMcbEntry( Fcb
->Vcb
,
4809 RequestedMapSize
->LowPart
- 1,
4817 try_leave( Status
= STATUS_FILE_CORRUPT_ERROR
);
4820 *MappingPairs
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
4821 (Index
+ 2) * (2 * sizeof(LARGE_INTEGER
)),
4822 TAG_OUTPUT_MAPPINGPAIRS
);
4825 // Now copy over the mapping pairs from the mcb
4826 // to the output buffer. We store in [sector count, lbo]
4827 // mapping pairs and end with a zero sector count.
4830 MapSize
= RequestedMapSize
->LowPart
;
4832 for (i
= 0; i
<= Index
; i
+= 1) {
4834 (VOID
)FatGetNextMcbEntry( Fcb
->Vcb
, &Fcb
->Mcb
, i
, (PVBO
)&Vbo
, &Lbo
, &SectorCount
);
4836 if (SectorCount
> MapSize
) {
4837 SectorCount
= MapSize
;
4840 (*MappingPairs
)[ i
*2 + 0 ].QuadPart
= SectorCount
;
4841 (*MappingPairs
)[ i
*2 + 1 ].QuadPart
= Lbo
;
4843 MapSize
-= SectorCount
;
4846 (*MappingPairs
)[ i
*2 + 0 ].QuadPart
= 0;
4848 Status
= STATUS_SUCCESS
;
4852 DebugUnwind( FatQueryRetrievalPointers
);
4855 // Release all of our resources
4858 FatReleaseFcb( IrpContext
, Fcb
);
4861 // If this is an abnormal termination then undo our work, otherwise
4865 if (!_SEH2_AbnormalTermination()) {
4867 FatCompleteRequest( IrpContext
, Irp
, Status
);
4876 // Local Support Routine
4881 IN PIRP_CONTEXT IrpContext
,
4887 Routine Description:
4889 This routine returns the filesystem performance counters from the
4894 Irp - Supplies the Irp to process
4898 NTSTATUS - The return status for the operation
4903 PIO_STACK_LOCATION IrpSp
;
4907 PFILE_SYSTEM_STATISTICS Buffer
;
4914 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4916 DebugTrace(+1, Dbg
, "FatGetStatistics...\n", 0);
4919 // Extract the buffer
4922 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
4925 // Get a pointer to the output buffer.
4928 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
4931 // Make sure the buffer is big enough for at least the common part.
4934 if (BufferLength
< sizeof(FILESYSTEM_STATISTICS
)) {
4936 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
4938 DebugTrace(-1, Dbg
, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL
);
4940 return STATUS_BUFFER_TOO_SMALL
;
4944 // Now see how many bytes we can copy.
4947 StatsSize
= sizeof(FILE_SYSTEM_STATISTICS
) * FatData
.NumberProcessors
;
4949 if (BufferLength
< StatsSize
) {
4951 BytesToCopy
= BufferLength
;
4952 Status
= STATUS_BUFFER_OVERFLOW
;
4956 BytesToCopy
= StatsSize
;
4957 Status
= STATUS_SUCCESS
;
4964 Vcb
= &((PVOLUME_DEVICE_OBJECT
)IrpSp
->DeviceObject
)->Vcb
;
4967 // Fill in the output buffer
4970 RtlCopyMemory( Buffer
, Vcb
->Statistics
, BytesToCopy
);
4972 Irp
->IoStatus
.Information
= BytesToCopy
;
4974 FatCompleteRequest( IrpContext
, Irp
, Status
);
4976 DebugTrace(-1, Dbg
, "FatGetStatistics -> %08lx\n", Status
);
4982 // Local Support Routine
4985 _Requires_lock_held_(_Global_critical_region_
)
4988 IN PIRP_CONTEXT IrpContext
,
4994 Routine Description:
4996 This routine returns the volume allocation bitmap.
4998 Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
4999 through the input buffer.
5000 Output = the VOLUME_BITMAP_BUFFER data structure is returned through
5003 We return as much as the user buffer allows starting the specified input
5004 LCN (trucated to a byte). If there is no input buffer, we start at zero.
5008 Irp - Supplies the Irp being processed.
5012 NTSTATUS - The return status for the operation.
5017 PIO_STACK_LOCATION IrpSp
;
5024 ULONG TotalClusters
;
5025 ULONG DesiredClusters
;
5026 ULONG StartingCluster
;
5027 ULONG EndingCluster
;
5028 ULONG InputBufferLength
;
5029 ULONG OutputBufferLength
;
5030 LARGE_INTEGER StartingLcn
;
5031 PVOLUME_BITMAP_BUFFER OutputBuffer
;
5036 // Get the current Irp stack location and save some references.
5039 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5041 DebugTrace(+1, Dbg
, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
5042 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5045 // Make this a synchronous IRP because we need access to the input buffer and
5046 // this Irp is marked METHOD_NEITHER.
5049 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5052 // Extract and decode the file object and check for type of open.
5055 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
5057 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5058 return STATUS_INVALID_PARAMETER
;
5061 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5063 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5065 DebugTrace(-1, Dbg
, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER
);
5066 return STATUS_INVALID_PARAMETER
;
5069 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5070 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
5072 OutputBuffer
= (PVOLUME_BITMAP_BUFFER
)FatMapUserBuffer( IrpContext
, Irp
);
5075 // Check for a minimum length on the input and output buffers.
5078 if ((InputBufferLength
< sizeof(STARTING_LCN_INPUT_BUFFER
)) ||
5079 (OutputBufferLength
< sizeof(VOLUME_BITMAP_BUFFER
))) {
5081 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5082 return STATUS_BUFFER_TOO_SMALL
;
5086 // Check if a starting cluster was specified.
5089 TotalClusters
= Vcb
->AllocationSupport
.NumberOfClusters
;
5092 // Check for valid buffers
5097 if (Irp
->RequestorMode
!= KernelMode
) {
5099 ProbeForRead( IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5103 ProbeForWrite( OutputBuffer
, OutputBufferLength
, sizeof(UCHAR
) );
5106 StartingLcn
= ((PSTARTING_LCN_INPUT_BUFFER
)IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
)->StartingLcn
;
5108 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5110 Status
= _SEH2_GetExceptionCode();
5112 FatRaiseStatus( IrpContext
,
5113 FsRtlIsNtstatusExpected(Status
) ?
5114 Status
: STATUS_INVALID_USER_BUFFER
);
5117 if (StartingLcn
.HighPart
|| StartingLcn
.LowPart
>= TotalClusters
) {
5119 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5120 return STATUS_INVALID_PARAMETER
;
5124 StartingCluster
= StartingLcn
.LowPart
& ~7;
5127 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
5130 // Only return what will fit in the user buffer.
5133 OutputBufferLength
-= FIELD_OFFSET(VOLUME_BITMAP_BUFFER
, Buffer
);
5134 DesiredClusters
= TotalClusters
- StartingCluster
;
5136 if (OutputBufferLength
< (DesiredClusters
+ 7) / 8) {
5138 BytesToCopy
= OutputBufferLength
;
5139 Status
= STATUS_BUFFER_OVERFLOW
;
5143 BytesToCopy
= (DesiredClusters
+ 7) / 8;
5144 Status
= STATUS_SUCCESS
;
5148 // Use try/finally for cleanup.
5156 // Verify the Vcb is still OK
5159 FatQuickVerifyVcb( IrpContext
, Vcb
);
5162 // Fill in the fixed part of the output buffer
5165 OutputBuffer
->StartingLcn
.QuadPart
= StartingCluster
;
5166 OutputBuffer
->BitmapSize
.QuadPart
= DesiredClusters
;
5168 if (Vcb
->NumberOfWindows
== 1) {
5171 // Just copy the volume bitmap into the user buffer.
5174 NT_ASSERT( Vcb
->FreeClusterBitMap
.Buffer
!= NULL
);
5176 RtlCopyMemory( &OutputBuffer
->Buffer
[0],
5177 (PUCHAR
)Vcb
->FreeClusterBitMap
.Buffer
+ StartingCluster
/8,
5182 // Call out to analyze the FAT. We must bias by two to account for
5183 // the zero base of this API and FAT's physical reality of starting
5184 // the file heap at cluster 2.
5186 // Note that the end index is inclusive - we need to subtract one to
5189 // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster
5190 // of 2 and end cluster of 9, a run of eight clusters.
5193 EndingCluster
= StartingCluster
+ (BytesToCopy
* 8);
5196 // Make sure we do not read past the end of the entries.
5199 if (EndingCluster
> TotalClusters
) {
5201 EndingCluster
= TotalClusters
;
5204 FatExamineFatEntries( IrpContext
,
5206 StartingCluster
+ 2,
5207 EndingCluster
+ 2 - 1,
5210 (PULONG
)&OutputBuffer
->Buffer
[0] );
5213 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5215 Status
= _SEH2_GetExceptionCode();
5217 FatRaiseStatus( IrpContext
,
5218 FsRtlIsNtstatusExpected(Status
) ?
5219 Status
: STATUS_INVALID_USER_BUFFER
);
5224 FatReleaseVcb( IrpContext
, Vcb
);
5227 Irp
->IoStatus
.Information
= FIELD_OFFSET(VOLUME_BITMAP_BUFFER
, Buffer
) +
5230 FatCompleteRequest( IrpContext
, Irp
, Status
);
5232 DebugTrace(-1, Dbg
, "FatGetVolumeBitmap -> VOID\n", 0);
5239 // Local Support Routine
5242 _Requires_lock_held_(_Global_critical_region_
)
5244 FatGetRetrievalPointers (
5245 IN PIRP_CONTEXT IrpContext
,
5251 Routine Description:
5253 This routine scans the MCB and builds an extent list. The first run in
5254 the output extent list will start at the begining of the contiguous
5255 run specified by the input parameter.
5257 Input = STARTING_VCN_INPUT_BUFFER;
5258 Output = RETRIEVAL_POINTERS_BUFFER.
5262 Irp - Supplies the Irp being processed.
5266 NTSTATUS - The return status for the operation.
5270 NTSTATUS Status
= STATUS_SUCCESS
;
5271 PIO_STACK_LOCATION IrpSp
;
5276 PLARGE_MCB McbToUse
= NULL
;
5277 TYPE_OF_OPEN TypeOfOpen
;
5280 ULONG ClusterShift
= 0;
5282 LONGLONG AllocationSize
= 0;
5287 LARGE_INTEGER StartingVcn
;
5289 ULONG InputBufferLength
;
5290 ULONG OutputBufferLength
;
5296 PRETRIEVAL_POINTERS_BUFFER OutputBuffer
;
5301 // Get the current Irp stack location and save some references.
5304 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5306 DebugTrace(+1, Dbg
, "FatGetRetrievalPointers, FsControlCode = %08lx\n",
5307 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5310 // Make this a synchronous IRP because we need access to the input buffer and
5311 // this Irp is marked METHOD_NEITHER.
5314 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5317 // Extract and decode the file object and check for type of open.
5320 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
5322 if ((TypeOfOpen
!= UserFileOpen
) && (TypeOfOpen
!= UserDirectoryOpen
) && (TypeOfOpen
!= UserVolumeOpen
) ) {
5324 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5325 return STATUS_INVALID_PARAMETER
;
5329 // Get the input and output buffer lengths and pointers.
5330 // Initialize some variables.
5333 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5334 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
5336 OutputBuffer
= (PRETRIEVAL_POINTERS_BUFFER
)FatMapUserBuffer( IrpContext
, Irp
);
5339 // Check for a minimum length on the input and ouput buffers.
5342 if ((InputBufferLength
< sizeof(STARTING_VCN_INPUT_BUFFER
)) ||
5343 (OutputBufferLength
< sizeof(RETRIEVAL_POINTERS_BUFFER
))) {
5345 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5346 return STATUS_BUFFER_TOO_SMALL
;
5350 // Acquire the Fcb and enqueue the Irp if we didn't get access. Go for
5351 // shared on read-only media so we can allow prototype XIP to get
5352 // recursive, as well as recognizing this is safe anyway.
5354 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5356 if (FlagOn( FcbOrDcb
->Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
5358 (VOID
)FatAcquireSharedFcb( IrpContext
, FcbOrDcb
);
5362 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
5364 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5366 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5368 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
5370 DebugTrace(-1, Dbg
, "FatMoveFile -> 0x%x\n", STATUS_ACCESS_DENIED
);
5371 return STATUS_ACCESS_DENIED
;
5374 (VOID
)FatAcquireExclusiveVcb(IrpContext
, Vcb
);
5380 // Verify the Fcb is still OK, or if it is a volume handle, the VCB.
5383 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5385 FatVerifyFcb( IrpContext
, FcbOrDcb
);
5388 // If we haven't yet set the correct AllocationSize, do so.
5391 if (FcbOrDcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
5393 FatLookupFileAllocationSize( IrpContext
, FcbOrDcb
);
5396 // If this is a non-root directory, we have a bit more to
5397 // do since it has not gone through FatOpenDirectoryFile().
5400 if (NodeType(FcbOrDcb
) == FAT_NTC_DCB
||
5401 (NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& FatIsFat32(Vcb
))) {
5403 FcbOrDcb
->Header
.FileSize
.LowPart
=
5404 FcbOrDcb
->Header
.AllocationSize
.LowPart
;
5409 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5412 #pragma prefast( suppress:28931, "calculate it anyway, in case someone adds code that uses this in the future" )
5414 ClusterSize
= 1 << ClusterShift
;
5416 AllocationSize
= FcbOrDcb
->Header
.AllocationSize
.LowPart
;
5417 McbToUse
= &FcbOrDcb
->Mcb
;
5419 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5421 FatQuickVerifyVcb( IrpContext
, Vcb
);
5423 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_BAD_BLOCKS_POPULATED
)) {
5426 // If the bad cluster mcb isn't populated, something is wrong. (It should have been
5427 // populated during mount when we scanned the FAT.
5430 FatRaiseStatus(IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
5433 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5434 ClusterSize
= 1 << ClusterShift
;
5436 if (!FatLookupLastMcbEntry(Vcb
, &Vcb
->BadBlockMcb
, &LastVbo
, &LastLbo
, &LastIndex
)) {
5441 // Round the allocation size to a multiple of of the cluster size.
5444 AllocationSize
= (LastVbo
+ ((LONGLONG
)ClusterSize
-1)) & ~((LONGLONG
)ClusterSize
-1);
5447 McbToUse
= &Vcb
->BadBlockMcb
;
5452 // Check if a starting cluster was specified.
5457 if (Irp
->RequestorMode
!= KernelMode
) {
5459 ProbeForRead( IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5463 ProbeForWrite( OutputBuffer
, OutputBufferLength
, sizeof(UCHAR
) );
5466 StartingVcn
= ((PSTARTING_VCN_INPUT_BUFFER
)IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
)->StartingVcn
;
5468 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5470 Status
= _SEH2_GetExceptionCode();
5472 FatRaiseStatus( IrpContext
,
5473 FsRtlIsNtstatusExpected(Status
) ?
5474 Status
: STATUS_INVALID_USER_BUFFER
);
5477 if (StartingVcn
.HighPart
||
5478 StartingVcn
.LowPart
>= (AllocationSize
>> ClusterShift
)) {
5480 try_return( Status
= STATUS_END_OF_FILE
);
5485 // If we don't find the run, something is very wrong.
5490 if (!FatLookupMcbEntry( Vcb
, McbToUse
,
5491 StartingVcn
.LowPart
<< ClusterShift
,
5497 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5499 FatBugCheck( (ULONG_PTR
)FcbOrDcb
, (ULONG_PTR
)McbToUse
, StartingVcn
.LowPart
);
5504 // Now go fill in the ouput buffer with run information
5507 RunCount
= FsRtlNumberOfRunsInLargeMcb( McbToUse
);
5509 for (Index
= 0, Run
= StartingRun
; Run
< RunCount
; Index
++, Run
++) {
5516 // Check for an exhausted output buffer.
5519 if ((ULONG
)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
+1]) > OutputBufferLength
) {
5523 // We've run out of space, so we won't be storing as many runs to the
5524 // user's buffer as we had originally planned. We need to return the
5525 // number of runs that we did have room for.
5530 OutputBuffer
->ExtentCount
= Index
;
5532 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5534 Status
= _SEH2_GetExceptionCode();
5536 FatRaiseStatus( IrpContext
,
5537 FsRtlIsNtstatusExpected(Status
) ?
5538 Status
: STATUS_INVALID_USER_BUFFER
);
5541 Irp
->IoStatus
.Information
= FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
]);
5542 try_return( Status
= STATUS_BUFFER_OVERFLOW
);
5546 // Get the extent. If it's not there or malformed, something is very wrong.
5549 if (!FatGetNextMcbEntry(Vcb
, McbToUse
, Run
, (PVBO
)&Vcn
, &Lbo
, &ByteLength
)) {
5552 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5554 FatBugCheck( (ULONG_PTR
)FcbOrDcb
, (ULONG_PTR
)McbToUse
, Run
);
5558 // Fill in the next array element.
5563 OutputBuffer
->Extents
[Index
].NextVcn
.QuadPart
= ((LONGLONG
)Vcn
+ ByteLength
) >> ClusterShift
;
5564 OutputBuffer
->Extents
[Index
].Lcn
.QuadPart
= FatGetIndexFromLbo( Vcb
, Lbo
) - 2;
5567 // If this is the first run, fill in the starting Vcn
5571 OutputBuffer
->ExtentCount
= RunCount
- StartingRun
;
5572 OutputBuffer
->StartingVcn
.QuadPart
= Vcn
>> ClusterShift
;
5575 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5577 Status
= _SEH2_GetExceptionCode();
5579 FatRaiseStatus( IrpContext
,
5580 FsRtlIsNtstatusExpected(Status
) ?
5581 Status
: STATUS_INVALID_USER_BUFFER
);
5586 // We successfully retrieved extent info to the end of the allocation.
5589 Irp
->IoStatus
.Information
= FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
]);
5590 Status
= STATUS_SUCCESS
;
5596 DebugUnwind( FatGetRetrievalPointers
);
5599 // Release resources
5602 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5604 FatReleaseFcb( IrpContext
, FcbOrDcb
);
5605 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5607 FatReleaseVcb(IrpContext
, Vcb
);
5611 // If nothing raised then complete the irp.
5614 if (!_SEH2_AbnormalTermination()) {
5616 FatCompleteRequest( IrpContext
, Irp
, Status
);
5619 DebugTrace(-1, Dbg
, "FatGetRetrievalPointers -> VOID\n", 0);
5627 // Local Support Routine
5630 _Requires_lock_held_(_Global_critical_region_
)
5632 FatMoveFileNeedsWriteThrough (
5633 _In_ PIRP_CONTEXT IrpContext
,
5635 _In_ ULONG OldWriteThroughFlags
5640 if (NodeType(FcbOrDcb
) == FAT_NTC_FCB
) {
5643 if (FcbOrDcb
->Header
.ValidDataLength
.QuadPart
== 0) {
5646 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WRITE_THROUGH
);
5647 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
);
5651 IrpContext
->Flags
&= ~(IRP_CONTEXT_FLAG_WRITE_THROUGH
|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
);
5652 IrpContext
->Flags
|= OldWriteThroughFlags
;
5658 _Requires_lock_held_(_Global_critical_region_
)
5661 IN PIRP_CONTEXT IrpContext
,
5667 Routine Description:
5669 Routine moves a file to the requested Starting Lcn from Starting Vcn for the length
5670 of cluster count. These values are passed in through the the input buffer as a
5671 MOVE_DATA structure.
5673 The call must be made with a DASD handle. The file to move is passed in as a
5678 Irp - Supplies the Irp being processed.
5682 NTSTATUS - The return status for the operation.
5688 PIO_STACK_LOCATION IrpSp
;
5690 PFILE_OBJECT FileObject
;
5691 TYPE_OF_OPEN TypeOfOpen
;
5696 ULONG InputBufferLength
;
5697 PMOVE_FILE_DATA InputBuffer
;
5705 ULONG TargetCluster
;
5706 LARGE_INTEGER LargeSourceLbo
;
5707 LARGE_INTEGER LargeTargetLbo
;
5711 ULONG BytesToReallocate
;
5713 ULONG FirstSpliceSourceCluster
;
5714 ULONG FirstSpliceTargetCluster
;
5715 ULONG SecondSpliceSourceCluster
;
5716 ULONG SecondSpliceTargetCluster
;
5718 LARGE_MCB SourceMcb
;
5719 LARGE_MCB TargetMcb
;
5723 PVOID Buffer
= NULL
;
5726 BOOLEAN SourceMcbInitialized
= FALSE
;
5727 BOOLEAN TargetMcbInitialized
= FALSE
;
5729 BOOLEAN FcbAcquired
= FALSE
;
5730 BOOLEAN EventArmed
= FALSE
;
5731 BOOLEAN DiskSpaceAllocated
= FALSE
;
5734 PBCB DirentBcb
= NULL
;
5736 ULONG OldWriteThroughFlags
= (IrpContext
->Flags
& (IRP_CONTEXT_FLAG_WRITE_THROUGH
|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
));
5738 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5739 MOVE_FILE_DATA LocalMoveFileData
;
5740 PMOVE_FILE_DATA32 MoveFileData32
;
5743 ULONG LocalAbnormalTermination
= 0;
5748 // Get the current Irp stack location and save some references.
5751 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5753 DebugTrace(+1, Dbg
, "FatMoveFile, FsControlCode = %08lx\n",
5754 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5757 // Force WAIT to true. We have a handle in the input buffer which can only
5758 // be referenced within the originating process.
5761 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5764 // Extract and decode the file object and check for type of open.
5767 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
) != UserVolumeOpen
) {
5769 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5771 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5772 return STATUS_INVALID_PARAMETER
;
5775 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5777 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5779 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5780 return STATUS_INVALID_PARAMETER
;
5783 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5784 InputBuffer
= (PMOVE_FILE_DATA
)Irp
->AssociatedIrp
.SystemBuffer
;
5787 // Do a quick check on the input buffer.
5790 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5791 if (IoIs32bitProcess( Irp
)) {
5793 if (InputBuffer
== NULL
|| InputBufferLength
< sizeof(MOVE_FILE_DATA32
)) {
5795 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5796 return STATUS_BUFFER_TOO_SMALL
;
5799 MoveFileData32
= (PMOVE_FILE_DATA32
) InputBuffer
;
5801 LocalMoveFileData
.FileHandle
= (HANDLE
) LongToHandle( MoveFileData32
->FileHandle
);
5802 LocalMoveFileData
.StartingVcn
= MoveFileData32
->StartingVcn
;
5803 LocalMoveFileData
.StartingLcn
= MoveFileData32
->StartingLcn
;
5804 LocalMoveFileData
.ClusterCount
= MoveFileData32
->ClusterCount
;
5806 InputBuffer
= &LocalMoveFileData
;
5810 if (InputBuffer
== NULL
|| InputBufferLength
< sizeof(MOVE_FILE_DATA
)) {
5812 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5813 return STATUS_BUFFER_TOO_SMALL
;
5815 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5819 MaxClusters
= Vcb
->AllocationSupport
.NumberOfClusters
;
5820 TargetCluster
= InputBuffer
->StartingLcn
.LowPart
+ 2;
5822 if (InputBuffer
->StartingVcn
.HighPart
||
5823 InputBuffer
->StartingLcn
.HighPart
||
5824 (TargetCluster
< 2) ||
5825 (TargetCluster
+ InputBuffer
->ClusterCount
< TargetCluster
) ||
5826 (TargetCluster
+ InputBuffer
->ClusterCount
> MaxClusters
+ 2) ||
5827 (InputBuffer
->StartingVcn
.LowPart
>= MaxClusters
) ||
5828 InputBuffer
->ClusterCount
== 0
5831 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5833 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5834 return STATUS_INVALID_PARAMETER
;
5838 // Try to get a pointer to the file object from the handle passed in.
5841 Status
= ObReferenceObjectByHandle( InputBuffer
->FileHandle
,
5848 (PVOID
*)&FileObject
,
5852 if (!NT_SUCCESS(Status
)) {
5854 FatCompleteRequest( IrpContext
, Irp
, Status
);
5856 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", Status
);
5861 // There are three basic ways this could be an invalid attempt, so
5864 // - check that this file object is opened on the same volume as the
5865 // DASD handle used to call this routine.
5867 // - extract and decode the file object and check for type of open.
5869 // - if this is a directory, verify that it's not the root and that
5870 // we are not trying to move the first cluster. We cannot move the
5871 // first cluster because sub-directories have this cluster number
5872 // in them and there is no safe way to simultaneously update them
5875 // We'll allow movefile on the root dir if its fat32, since the root dir
5876 // is a real chained file there.
5879 if (FileObject
->Vpb
!= Vcb
->Vpb
) {
5881 ObDereferenceObject( FileObject
);
5882 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5884 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5885 return STATUS_INVALID_PARAMETER
;
5888 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
5890 if ((TypeOfOpen
!= UserFileOpen
&&
5891 TypeOfOpen
!= UserDirectoryOpen
) ||
5893 ((TypeOfOpen
== UserDirectoryOpen
) &&
5894 ((NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& !FatIsFat32(Vcb
)) ||
5895 (InputBuffer
->StartingVcn
.QuadPart
== 0)))) {
5897 ObDereferenceObject( FileObject
);
5898 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5900 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5901 return STATUS_INVALID_PARAMETER
;
5905 // If the VDL of the file is zero, it has no valid data in it anyway.
5906 // So it should be safe to avoid flushing the FAT entries and let them be
5907 // lazily written out.
5909 // This is done so that bitlocker's cover file doesn't cause
5910 // unnecessary FAT table I/O when it's moved around.
5911 // (See Win8 bug 106505)
5915 // If this is a file, and the VDL is zero, clear write through.
5918 FatMoveFileNeedsWriteThrough(IrpContext
, FcbOrDcb
, OldWriteThroughFlags
);
5922 // Indicate we're getting to parents of this fcb by their child, and that
5923 // this is a sufficient assertion of our ability to by synchronized
5924 // with respect to the parent directory going away.
5926 // The defrag path is an example of one which arrives at an Fcb by
5927 // a means which would be unreasonable to duplicate in the assertion
5928 // code. See FatOpenDirectoryFile.
5931 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_PARENT_BY_CHILD
);
5933 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5938 // Initialize our state variables and the event.
5941 FileOffset
= InputBuffer
->StartingVcn
.LowPart
<< ClusterShift
;
5943 ByteCount
= InputBuffer
->ClusterCount
<< ClusterShift
;
5945 TargetLbo
= FatGetLboFromIndex( Vcb
, TargetCluster
);
5946 LargeTargetLbo
.QuadPart
= TargetLbo
;
5951 // Do a quick check on parameters here
5954 if (FileOffset
+ ByteCount
< FileOffset
) {
5956 try_return( Status
= STATUS_INVALID_PARAMETER
);
5959 KeInitializeEvent( &StackEvent
, NotificationEvent
, FALSE
);
5962 // Initialize two MCBs we will be using
5965 FsRtlInitializeLargeMcb( &SourceMcb
, PagedPool
);
5966 SourceMcbInitialized
= TRUE
;
5968 FsRtlInitializeLargeMcb( &TargetMcb
, PagedPool
);
5969 TargetMcbInitialized
= TRUE
;
5972 // Ok, now if this is a directory open we need to switch to the internal
5973 // stream fileobject since it is set up for caching. The top-level
5974 // fileobject has no section object pointers in order prevent folks from
5978 if (TypeOfOpen
== UserDirectoryOpen
) {
5980 PFILE_OBJECT DirStreamFileObject
;
5983 // Open the stream fileobject if neccesary. We must acquire the Fcb
5984 // now to synchronize with other operations (such as dismount ripping
5985 // apart the allocator).
5988 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
5991 FatVerifyFcb( IrpContext
, FcbOrDcb
);
5993 FatOpenDirectoryFile( IrpContext
, FcbOrDcb
);
5994 DirStreamFileObject
= FcbOrDcb
->Specific
.Dcb
.DirectoryFile
;
5997 // Transfer our reference to the internal stream and proceed. Note that
5998 // if we dereferenced first, the user could sneak a teardown through since
5999 // we'd have no references.
6002 ObReferenceObject( DirStreamFileObject
);
6003 ObDereferenceObject( FileObject
);
6004 FileObject
= DirStreamFileObject
;
6007 // We've referenced the DirStreamFileObject, so it should be ok to drop
6011 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6012 FcbAcquired
= FALSE
;
6016 // Determine the size of the buffer we will use to move data.
6019 BufferSize
= FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES
;
6021 if (BufferSize
< (ULONG
)(1 << ClusterShift
)) {
6023 BufferSize
= (1 << ClusterShift
);
6030 ULONG TempByteCount
;
6033 // We must throttle our writes.
6036 CcCanIWrite( FileObject
,
6042 // Aqcuire file resource exclusive to freeze FileSize and block
6043 // user non-cached I/O. Verify the integrity of the fcb - the
6044 // media may have changed (or been dismounted) on us.
6047 if (FcbAcquired
== FALSE
) {
6049 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
6052 FatVerifyFcb( IrpContext
, FcbOrDcb
);
6056 // Check if the handle indicates we're allowed to move the file.
6058 // FCB_STATE_DENY_DEFRAG indicates that someone blocked move file on this FCB.
6059 // CCB_FLAG_DENY_DEFRAG indicates that this handle was the one that blocked move file, and hence
6060 // it still gets to move the file around.
6063 if ((FcbOrDcb
->FcbState
& FCB_STATE_DENY_DEFRAG
) && !(Ccb
->Flags
& CCB_FLAG_DENY_DEFRAG
)) {
6064 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_ACCESS_DENIED
);
6065 try_return( Status
= STATUS_ACCESS_DENIED
);
6069 // Allocate our buffer, if we need to.
6072 if (Buffer
== NULL
) {
6074 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
6076 TAG_DEFRAG_BUFFER
);
6080 // Analyzes the range of file allocation we are moving
6081 // and determines the actual amount of allocation to be
6082 // moved and how much needs to be written. In addition
6083 // it guarantees that the Mcb in the file is large enough
6084 // so that later MCB operations cannot fail.
6087 FatComputeMoveFileParameter( IrpContext
,
6097 // If ByteCount comes back zero, break here.
6100 if (ByteCount
== 0) {
6105 // At this point (before actually doing anything with the disk
6106 // meta data), calculate the FAT splice clusters and build an
6107 // MCB describing the space to be deallocated.
6110 FatComputeMoveFileSplicePoints( IrpContext
,
6115 &FirstSpliceSourceCluster
,
6116 &FirstSpliceTargetCluster
,
6117 &SecondSpliceSourceCluster
,
6118 &SecondSpliceTargetCluster
,
6122 // Now attempt to allocate the new disk storage using the
6123 // Target Lcn as a hint.
6126 TempByteCount
= BytesToReallocate
;
6127 FatAllocateDiskSpace( IrpContext
,
6134 DiskSpaceAllocated
= TRUE
;
6137 // If we didn't get EXACTLY what we wanted, return immediately.
6140 if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb
) != 1) ||
6141 !FatGetNextMcbEntry( Vcb
, &TargetMcb
, 0, &TempVbo
, &TempLbo
, &TempByteCount
) ||
6142 (FatGetIndexFromLbo( Vcb
, TempLbo
) != TargetCluster
) ||
6143 (TempByteCount
!= BytesToReallocate
)) {
6146 // It would be nice if we could be more specific, but such is life.
6148 try_return( Status
= STATUS_INVALID_PARAMETER
);
6153 // We are going to attempt a move, note it.
6156 if (FatMoveFileDebug
) {
6157 DbgPrint("0x%p: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n",
6158 PsGetCurrentThread(),
6159 FileOffset
>> ClusterShift
,
6161 BytesToReallocate
>> ClusterShift
);
6166 // Now attempt to commit the new allocation to disk. If this
6167 // raises, the allocation will be deallocated.
6169 // If the VDL of the file is zero, it has no valid data in it anyway.
6170 // So it should be safe to avoid flushing the FAT entries and let them be
6171 // lazily written out.
6173 // This is done so that bitlocker's cover file doesn't cause
6174 // unnecessary FAT table I/O when it's moved around.
6175 // (See Win8 bug 106505)
6178 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6180 FatFlushFatEntries( IrpContext
,
6183 BytesToReallocate
>> ClusterShift
);
6187 // Aqcuire both resources exclusive now, guaranteeing that NOBODY
6188 // is in either the read or write paths.
6191 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6194 // This is the first part of some tricky synchronization.
6196 // Set the Event pointer in the FCB. Any paging I/O will block on
6197 // this event (if set in FCB) after acquiring the PagingIo resource.
6199 // This is how I keep ALL I/O out of this path without holding the
6200 // PagingIo resource exclusive for an extended time.
6203 FcbOrDcb
->MoveFileEvent
= &StackEvent
;
6206 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6209 // Now write out the data, but only if we have to. We don't have
6210 // to copy any file data if the range being reallocated is wholly
6211 // beyond valid data length.
6218 IO_STATUS_BLOCK Iosb
;
6220 KeInitializeEvent( &IoEvent
,
6224 NT_ASSERT( LargeTargetLbo
.QuadPart
>= Vcb
->AllocationSupport
.FileAreaLbo
);
6227 // Read in the data that is being moved.
6230 IoIrp
= IoBuildSynchronousFsdRequest( IRP_MJ_READ
,
6231 Vcb
->TargetDeviceObject
,
6238 if (IoIrp
== NULL
) {
6240 FatRaiseStatus( IrpContext
,
6241 STATUS_INSUFFICIENT_RESOURCES
);
6244 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, IoIrp
);
6246 if (Status
== STATUS_PENDING
) {
6248 (VOID
)KeWaitForSingleObject( &IoEvent
,
6252 (PLARGE_INTEGER
)NULL
);
6254 Status
= Iosb
.Status
;
6257 if (!NT_SUCCESS( Status
)) {
6259 FatNormalizeAndRaiseStatus( IrpContext
,
6264 // Write the data to its new location.
6267 KeClearEvent( &IoEvent
);
6269 IoIrp
= IoBuildSynchronousFsdRequest( IRP_MJ_WRITE
,
6270 Vcb
->TargetDeviceObject
,
6277 if (IoIrp
== NULL
) {
6279 FatRaiseStatus( IrpContext
,
6280 STATUS_INSUFFICIENT_RESOURCES
);
6284 // Set a flag indicating that we want to write through any
6285 // cache on the controller. This eliminates the need for
6286 // an explicit flush-device after the write.
6289 SetFlag( IoGetNextIrpStackLocation(IoIrp
)->Flags
, SL_WRITE_THROUGH
);
6291 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, IoIrp
);
6293 if (Status
== STATUS_PENDING
) {
6295 (VOID
)KeWaitForSingleObject( &IoEvent
,
6299 (PLARGE_INTEGER
)NULL
);
6301 Status
= Iosb
.Status
;
6304 if (!NT_SUCCESS( Status
)) {
6306 FatNormalizeAndRaiseStatus( IrpContext
,
6312 // Now that the file data has been moved successfully, we'll go
6313 // to fix up the links in the FAT table and perhaps change the
6314 // entry in the parent directory.
6316 // First we'll do the second splice and commit it. At that point,
6317 // while the volume is in an inconsistent state, the file is
6321 FatSetFatEntry( IrpContext
,
6323 SecondSpliceSourceCluster
,
6324 (FAT_ENTRY
)SecondSpliceTargetCluster
);
6326 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6328 FatFlushFatEntries( IrpContext
, Vcb
, SecondSpliceSourceCluster
, 1 );
6332 // Now do the first splice OR update the dirent in the parent
6333 // and flush the respective object. After this flush the file
6334 // now points to the new allocation.
6337 if (FirstSpliceSourceCluster
== 0) {
6339 NT_ASSERT( NodeType(FcbOrDcb
) == FAT_NTC_FCB
);
6342 // We are moving the first cluster of the file, so we need
6343 // to update our parent directory.
6346 FatGetDirentFromFcbOrDcb( IrpContext
,
6352 Dirent
->FirstClusterOfFile
= (USHORT
)FirstSpliceTargetCluster
;
6354 if (FatIsFat32(Vcb
)) {
6356 Dirent
->FirstClusterOfFileHi
=
6357 (USHORT
)(FirstSpliceTargetCluster
>> 16);
6361 FatSetDirtyBcb( IrpContext
, DirentBcb
, Vcb
, TRUE
);
6363 FatUnpinBcb( IrpContext
, DirentBcb
);
6366 FatFlushDirentForFile( IrpContext
, FcbOrDcb
);
6368 FcbOrDcb
->FirstClusterOfFile
= FirstSpliceTargetCluster
;
6372 FatSetFatEntry( IrpContext
,
6374 FirstSpliceSourceCluster
,
6375 (FAT_ENTRY
)FirstSpliceTargetCluster
);
6377 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6379 FatFlushFatEntries( IrpContext
, Vcb
, FirstSpliceSourceCluster
, 1 );
6384 // This was successfully committed. We no longer want to free
6385 // this allocation on error.
6388 DiskSpaceAllocated
= FALSE
;
6391 // Check if we need to turn off write through for this file.
6394 FatMoveFileNeedsWriteThrough(IrpContext
, FcbOrDcb
, OldWriteThroughFlags
);
6397 // Now we just have to free the orphaned space. We don't have
6398 // to commit this right now as the integrity of the file doesn't
6402 FatDeallocateDiskSpace( IrpContext
, Vcb
, &SourceMcb
, FALSE
);
6404 FatUnpinRepinnedBcbs( IrpContext
);
6406 Status
= FatHijackIrpAndFlushDevice( IrpContext
,
6408 Vcb
->TargetDeviceObject
);
6410 if (!NT_SUCCESS(Status
)) {
6411 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
6415 // Finally we must replace the old MCB extent information with
6416 // the new. If this fails from pool allocation, we fix it in
6417 // the finally clause by resetting the file's Mcb.
6420 FatRemoveMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6422 BytesToReallocate
);
6424 FatAddMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6427 BytesToReallocate
);
6430 // Now this is the second part of the tricky synchronization.
6432 // We drop the paging I/O here and signal the notification
6433 // event which allows all waiters (present or future) to proceed.
6434 // Then we block again on the PagingIo exclusive. When
6435 // we have it, we again know that there can be nobody in the
6436 // read/write path and thus nobody touching the event, so we
6437 // NULL the pointer to it and then drop the PagingIo resource.
6439 // This combined with our synchronization before the write above
6440 // guarantees that while we were moving the allocation, there
6441 // was no other I/O to this file and because we do not hold
6442 // the paging resource across a flush, we are not exposed to
6446 KeSetEvent( &StackEvent
, 0, FALSE
);
6448 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6450 FcbOrDcb
->MoveFileEvent
= NULL
;
6453 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6456 // Release the resources and let anyone else access the file before
6460 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6461 FcbAcquired
= FALSE
;
6464 // Advance the state variables.
6467 TargetCluster
+= BytesToReallocate
>> ClusterShift
;
6469 FileOffset
+= BytesToReallocate
;
6470 TargetLbo
+= BytesToReallocate
;
6471 ByteCount
-= BytesToReallocate
;
6473 LargeTargetLbo
.QuadPart
+= BytesToReallocate
;
6476 // Clear the two Mcbs
6479 FatRemoveMcbEntry( Vcb
, &SourceMcb
, 0, 0xFFFFFFFF );
6480 FatRemoveMcbEntry( Vcb
, &TargetMcb
, 0, 0xFFFFFFFF );
6483 // Make the event blockable again.
6486 KeClearEvent( &StackEvent
);
6489 Status
= STATUS_SUCCESS
;
6495 DebugUnwind( FatMoveFile
);
6497 LocalAbnormalTermination
|= _SEH2_AbnormalTermination();
6499 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_PARENT_BY_CHILD
);
6502 // Free the data buffer, if it was allocated.
6505 if (Buffer
!= NULL
) {
6507 ExFreePool( Buffer
);
6511 // Use a nested try-finally for cleanup if our unpinrepinned
6512 // encounters write-through errors. This may even be a re-raise.
6518 // If we have some new allocation hanging around, remove it. The
6519 // pages needed to do this are guaranteed to be resident because
6520 // we have already repinned them.
6523 if (DiskSpaceAllocated
) {
6524 FatDeallocateDiskSpace( IrpContext
, Vcb
, &TargetMcb
, FALSE
);
6525 FatUnpinRepinnedBcbs( IrpContext
);
6530 LocalAbnormalTermination
|= _SEH2_AbnormalTermination();
6533 // Check on the directory Bcb
6536 if (DirentBcb
!= NULL
) {
6537 FatUnpinBcb( IrpContext
, DirentBcb
);
6541 // Uninitialize our MCBs
6544 if (SourceMcbInitialized
) {
6545 FsRtlUninitializeLargeMcb( &SourceMcb
);
6548 if (TargetMcbInitialized
) {
6549 FsRtlUninitializeLargeMcb( &TargetMcb
);
6553 // If this is an abnormal termination then presumably something
6554 // bad happened. Set the Allocation size to unknown and clear
6555 // the Mcb, but only if we still own the Fcb.
6557 // It is important to make sure we use a 64bit form of -1. This is
6558 // what will convince the fastIO path that it cannot extend the file
6559 // in the cache until we have picked up the mapping pairs again.
6561 // Also, we have to do this while owning PagingIo or we can tear the
6562 // Mcb down in the midst of the noncached IO path looking up extents
6563 // (after we drop it and let them all in).
6566 if (LocalAbnormalTermination
&& FcbAcquired
) {
6568 if (FcbOrDcb
->FirstClusterOfFile
== 0) {
6570 FcbOrDcb
->Header
.AllocationSize
.QuadPart
= 0;
6574 FcbOrDcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
6577 FatRemoveMcbEntry( Vcb
, &FcbOrDcb
->Mcb
, 0, 0xFFFFFFFF );
6581 // If we broke out of the loop with the Event armed, defuse it
6582 // in the same way we do it after a write.
6586 KeSetEvent( &StackEvent
, 0, FALSE
);
6587 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6588 FcbOrDcb
->MoveFileEvent
= NULL
;
6589 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6593 // Finally release the main file resource.
6598 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6602 // Now dereference the fileobject. If the user was a wacko they could have
6603 // tried to nail us by closing the handle right after they threw this move
6604 // down, so we had to keep the fileobject referenced across the entire
6608 ObDereferenceObject( FileObject
);
6614 // Complete the irp if we terminated normally.
6617 FatCompleteRequest( IrpContext
, Irp
, Status
);
6624 // Local Support Routine
6627 _Requires_lock_held_(_Global_critical_region_
)
6629 FatComputeMoveFileParameter (
6630 IN PIRP_CONTEXT IrpContext
,
6632 IN ULONG BufferSize
,
6633 IN ULONG FileOffset
,
6634 IN OUT PULONG ByteCount
,
6635 OUT PULONG BytesToReallocate
,
6636 OUT PULONG BytesToWrite
,
6637 OUT PLARGE_INTEGER SourceLbo
6642 Routine Description:
6644 This is a helper routine for FatMoveFile that analyses the range of
6645 file allocation we are moving and determines the actual amount
6646 of allocation to be moved and how much needs to be written.
6650 FcbOrDcb - Supplies the file and its various sizes.
6652 BufferSize - Supplies the size of the buffer we are using to store the data
6655 FileOffset - Supplies the beginning Vbo of the reallocation zone.
6657 ByteCount - Supplies the request length to reallocate. This will
6658 be bounded by allocation size on return.
6660 BytesToReallocate - Receives ByteCount bounded by the file allocation size
6663 BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength.
6665 SourceLbo - Receives the logical byte offset of the source data on the volume.
6676 ULONG AllocationSize
;
6677 ULONG ValidDataLength
;
6678 ULONG ClusterAlignedVDL
;
6682 BOOLEAN RunAllocated
;
6683 BOOLEAN RunEndOnMax
;
6688 // If we haven't yet set the correct AllocationSize, do so.
6691 if (FcbOrDcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
6693 FatLookupFileAllocationSize( IrpContext
, FcbOrDcb
);
6696 // If this is a non-root directory, we have a bit more to
6697 // do since it has not gone through FatOpenDirectoryFile().
6700 if (NodeType(FcbOrDcb
) == FAT_NTC_DCB
||
6701 (NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& FatIsFat32(FcbOrDcb
->Vcb
))) {
6703 FcbOrDcb
->Header
.FileSize
.LowPart
=
6704 FcbOrDcb
->Header
.AllocationSize
.LowPart
;
6709 // Get the number of bytes left to write and ensure that it does
6710 // not extend beyond allocation size. We return here if FileOffset
6711 // is beyond AllocationSize which can happn on a truncation.
6714 AllocationSize
= FcbOrDcb
->Header
.AllocationSize
.LowPart
;
6715 ValidDataLength
= FcbOrDcb
->Header
.ValidDataLength
.LowPart
;
6717 if (FileOffset
+ *ByteCount
> AllocationSize
) {
6719 if (FileOffset
>= AllocationSize
) {
6721 *BytesToReallocate
= 0;
6727 *ByteCount
= AllocationSize
- FileOffset
;
6731 // If there is more than our max, then reduce the byte count for this
6732 // pass to our maximum. We must also align the file offset to a
6733 // buffer size byte boundary.
6736 if ((FileOffset
& (BufferSize
- 1)) + *ByteCount
> BufferSize
) {
6738 *BytesToReallocate
= BufferSize
- (FileOffset
& (BufferSize
- 1));
6742 *BytesToReallocate
= *ByteCount
;
6746 // Find where this data exists on the volume.
6749 FatLookupFileAllocation( IrpContext
,
6758 NT_ASSERT( RunAllocated
);
6761 // Limit this run to the contiguous length.
6764 if (RunByteCount
< *BytesToReallocate
) {
6766 *BytesToReallocate
= RunByteCount
;
6770 // Set the starting offset of the source.
6773 SourceLbo
->QuadPart
= RunLbo
;
6776 // We may be able to skip some (or all) of the write
6777 // if allocation size is significantly greater than valid data length.
6780 ClusterSize
= 1 << FcbOrDcb
->Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
6782 NT_ASSERT( ClusterSize
<= BufferSize
);
6784 ClusterAlignedVDL
= (ValidDataLength
+ (ClusterSize
- 1)) & ~(ClusterSize
- 1);
6786 if ((NodeType(FcbOrDcb
) == FAT_NTC_FCB
) &&
6787 (FileOffset
+ *BytesToReallocate
> ClusterAlignedVDL
)) {
6789 if (FileOffset
> ClusterAlignedVDL
) {
6795 *BytesToWrite
= ClusterAlignedVDL
- FileOffset
;
6800 *BytesToWrite
= *BytesToReallocate
;
6806 // Local Support Routine
6810 FatComputeMoveFileSplicePoints (
6811 IN PIRP_CONTEXT IrpContext
,
6813 IN ULONG FileOffset
,
6814 IN ULONG TargetCluster
,
6815 IN ULONG BytesToReallocate
,
6816 OUT PULONG FirstSpliceSourceCluster
,
6817 OUT PULONG FirstSpliceTargetCluster
,
6818 OUT PULONG SecondSpliceSourceCluster
,
6819 OUT PULONG SecondSpliceTargetCluster
,
6820 IN OUT PLARGE_MCB SourceMcb
6825 Routine Description:
6827 This is a helper routine for FatMoveFile that analyzes the range of
6828 file allocation we are moving and generates the splice points in the
6833 FcbOrDcb - Supplies the file and thus Mcb.
6835 FileOffset - Supplies the beginning Vbo of the reallocation zone.
6837 TargetCluster - Supplies the beginning cluster of the reallocation target.
6839 BytesToReallocate - Suppies the length of the reallocation zone.
6841 FirstSpliceSourceCluster - Receives the last cluster in previous allocation
6842 or zero if we are reallocating from VBO 0.
6844 FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation)
6846 SecondSpliceSourceCluster - Receives the final target cluster.
6848 SecondSpliceTargetCluster - Receives the first cluster of the remaining
6849 source allocation or FAT_CLUSTER_LAST if the reallocation zone
6850 extends to the end of the file.
6852 SourceMcb - This supplies an MCB that will be filled in with run
6853 information describing the file allocation being replaced. The Mcb
6854 must be initialized by the caller.
6866 ULONG SourceBytesInRun
;
6867 ULONG SourceBytesRemaining
;
6869 ULONG SourceMcbVbo
= 0;
6870 ULONG SourceMcbBytesInRun
= 0;
6877 Vcb
= FcbOrDcb
->Vcb
;
6880 // Get information on the final cluster in the previous allocation and
6881 // prepare to enumerate it in the follow loop.
6884 if (FileOffset
== 0) {
6887 *FirstSpliceSourceCluster
= 0;
6888 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6892 &SourceBytesInRun
);
6896 Result
= FatLookupMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6902 *FirstSpliceSourceCluster
= FatGetIndexFromLbo( Vcb
, SourceLbo
);
6904 if ((Result
) && (SourceBytesInRun
== 1)) {
6907 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6915 SourceVbo
= FileOffset
;
6917 SourceBytesInRun
-= 1;
6922 // Run should always be present, but don't bugcheck in the case where it's not.
6928 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
6932 // At this point the variables:
6934 // - SourceIndex - SourceLbo - SourceBytesInRun -
6936 // all correctly decribe the allocation to be removed. In the loop
6937 // below we will start here and continue enumerating the Mcb runs
6938 // until we are finished with the allocation to be relocated.
6941 *FirstSpliceTargetCluster
= TargetCluster
;
6943 *SecondSpliceSourceCluster
=
6944 *FirstSpliceTargetCluster
+
6945 (BytesToReallocate
>> Vcb
->AllocationSupport
.LogOfBytesPerCluster
) - 1;
6947 for (SourceBytesRemaining
= BytesToReallocate
, SourceMcbVbo
= 0;
6949 SourceBytesRemaining
> 0;
6952 SourceBytesRemaining
-= SourceMcbBytesInRun
,
6953 SourceMcbVbo
+= SourceMcbBytesInRun
) {
6955 if (SourceMcbVbo
!= 0) {
6957 #pragma prefast( suppress:28931, "needed for debug build" )
6959 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6963 &SourceBytesInRun
);
6967 NT_ASSERT( SourceVbo
== SourceMcbVbo
+ FileOffset
);
6969 SourceMcbBytesInRun
=
6970 SourceBytesInRun
< SourceBytesRemaining
?
6971 SourceBytesInRun
: SourceBytesRemaining
;
6973 FatAddMcbEntry( Vcb
, SourceMcb
,
6976 SourceMcbBytesInRun
);
6980 // Now compute the cluster of the target of the second
6981 // splice. If the final run in the above loop was
6982 // more than we needed, then we can just do arithmetic,
6983 // otherwise we have to look up the next run.
6986 if (SourceMcbBytesInRun
< SourceBytesInRun
) {
6988 *SecondSpliceTargetCluster
=
6989 FatGetIndexFromLbo( Vcb
, SourceLbo
+ SourceMcbBytesInRun
);
6993 if (FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6997 &SourceBytesInRun
)) {
6999 *SecondSpliceTargetCluster
= FatGetIndexFromLbo( Vcb
, SourceLbo
);
7003 *SecondSpliceTargetCluster
= FAT_CLUSTER_LAST
;
7010 FatAllowExtendedDasdIo(
7011 IN PIRP_CONTEXT IrpContext
,
7016 Routine Description:
7018 This routine marks the CCB to indicate that the handle
7019 may be used to read past the end of the volume file. The
7020 handle must be a dasd handle.
7024 Irp - Supplies the Irp being processed.
7028 NTSTATUS - The return status for the operation.
7032 PIO_STACK_LOCATION IrpSp
;
7040 // Get the current Irp stack location and save some references.
7043 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7046 // Extract and decode the file object and check for type of open.
7049 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7051 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7052 return STATUS_INVALID_PARAMETER
;
7055 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7057 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7059 DebugTrace(-1, Dbg
, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER
);
7060 return STATUS_INVALID_PARAMETER
;
7063 SetFlag( Ccb
->Flags
, CCB_FLAG_ALLOW_EXTENDED_DASD_IO
);
7065 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7066 return STATUS_SUCCESS
;
7069 #if (NTDDI_VERSION >= NTDDI_WIN7)
7071 _Requires_lock_held_(_Global_critical_region_
)
7073 FatGetRetrievalPointerBase (
7074 _In_ PIRP_CONTEXT IrpContext
,
7079 Routine Description:
7081 This routine retrieves the sector offset to the first allocation unit.
7085 IrpContext - Supplies the Irp Context.
7086 Irp - Supplies the Irp being processed.
7090 NTSTATUS - The return status for the operation.
7094 PIO_STACK_LOCATION IrpSp
= NULL
;
7098 ULONG BufferLength
= 0;
7099 PRETRIEVAL_POINTER_BASE RetrievalPointerBase
= NULL
;
7103 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7106 // Force WAIT to true.
7109 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7112 // Extract and decode the file object and check for type of open.
7115 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7117 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7118 return STATUS_INVALID_PARAMETER
;
7122 // Extract the buffer
7125 RetrievalPointerBase
= Irp
->AssociatedIrp
.SystemBuffer
;
7126 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
7129 // Verify the handle has manage volume access.
7132 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7134 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7135 return STATUS_INVALID_PARAMETER
;
7139 // Validate the output buffer is the right size.
7141 // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7144 if (BufferLength
< sizeof(RETRIEVAL_POINTER_BASE
)) {
7146 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7147 return STATUS_BUFFER_TOO_SMALL
;
7151 // Fill out the offset to the file area.
7154 RtlZeroMemory( RetrievalPointerBase
, BufferLength
);
7158 FatAcquireSharedVcb(IrpContext
, Vcb
);
7159 FatQuickVerifyVcb(IrpContext
, Vcb
);
7161 RetrievalPointerBase
->FileAreaOffset
.QuadPart
= Vcb
->AllocationSupport
.FileAreaLbo
>> Vcb
->AllocationSupport
.LogOfBytesPerSector
;
7162 Irp
->IoStatus
.Information
= sizeof( RETRIEVAL_POINTER_BASE
);
7166 FatReleaseVcb(IrpContext
, Vcb
);
7170 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7172 return STATUS_SUCCESS
;
7177 _Requires_lock_held_(_Global_critical_region_
)
7179 FatGetBootAreaInfo (
7180 _In_ PIRP_CONTEXT IrpContext
,
7185 Routine Description:
7187 This routine retrieves information about the boot areas of the filesystem.
7191 IrpContext - Supplies the Irp Context.
7192 Irp - Supplies the Irp being processed.
7196 NTSTATUS - The return status for the operation.
7200 PIO_STACK_LOCATION IrpSp
= NULL
;
7204 ULONG BufferLength
= 0;
7205 PBOOT_AREA_INFO BootAreaInfo
= NULL
;
7209 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7212 // Force WAIT to true.
7215 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7218 // Extract and decode the file object and check for type of open.
7221 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7223 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7224 return STATUS_INVALID_PARAMETER
;
7228 // Extract the buffer
7231 BootAreaInfo
= Irp
->AssociatedIrp
.SystemBuffer
;
7232 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
7235 // Verify the handle has manage volume access.
7238 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7240 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7241 return STATUS_INVALID_PARAMETER
;
7245 // Validate the output buffer is the right size.
7247 // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7250 if (BufferLength
< sizeof(BOOT_AREA_INFO
)) {
7252 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7253 return STATUS_BUFFER_TOO_SMALL
;
7257 // Fill out our boot areas.
7260 RtlZeroMemory( BootAreaInfo
, BufferLength
);
7264 FatAcquireSharedVcb(IrpContext
, Vcb
);
7265 FatQuickVerifyVcb(IrpContext
, Vcb
);
7267 if (FatIsFat32( Vcb
)) {
7269 BootAreaInfo
->BootSectorCount
= 2;
7270 BootAreaInfo
->BootSectors
[0].Offset
.QuadPart
= 0;
7271 BootAreaInfo
->BootSectors
[1].Offset
.QuadPart
= 6;
7274 BootAreaInfo
->BootSectorCount
= 1;
7275 BootAreaInfo
->BootSectors
[0].Offset
.QuadPart
= 0;
7278 Irp
->IoStatus
.Information
= sizeof( BOOT_AREA_INFO
);
7282 FatReleaseVcb(IrpContext
, Vcb
);
7285 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7286 return STATUS_SUCCESS
;
7292 _Requires_lock_held_(_Global_critical_region_
)
7295 _In_ PIRP_CONTEXT IrpContext
,
7300 Routine Description:
7302 This routine is used to attach special properties to a user handle.
7306 IrpContext - Supplies the Irp Context.
7307 Irp - Supplies the Irp being processed.
7311 NTSTATUS - The return status for the operation.
7315 NTSTATUS Status
= STATUS_SUCCESS
;
7316 PIO_STACK_LOCATION IrpSp
= NULL
;
7320 PFCB DasdFcb
= NULL
;
7321 PCCB DasdCcb
= NULL
;
7322 TYPE_OF_OPEN TypeOfOpen
;
7323 PMARK_HANDLE_INFO HandleInfo
= NULL
;
7324 PFILE_OBJECT DasdFileObject
= NULL
;
7325 BOOLEAN ReleaseFcb
= FALSE
;
7327 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7328 MARK_HANDLE_INFO LocalMarkHandleInfo
= {0};
7333 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7336 // Always make this a synchronous IRP.
7339 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7342 // Extract and decode the file object and check for type of open.
7345 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) ;
7348 // We currently support this call for files and directories only.
7351 if ((TypeOfOpen
!= UserFileOpen
) &&
7352 (TypeOfOpen
!= UserDirectoryOpen
)) {
7354 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7355 return STATUS_INVALID_PARAMETER
;
7358 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7361 // Win32/64 thunking code
7364 if (IoIs32bitProcess( Irp
)) {
7366 PMARK_HANDLE_INFO32 MarkHandle32
;
7368 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( MARK_HANDLE_INFO32
)) {
7370 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7371 return STATUS_BUFFER_TOO_SMALL
;
7374 MarkHandle32
= (PMARK_HANDLE_INFO32
) Irp
->AssociatedIrp
.SystemBuffer
;
7375 LocalMarkHandleInfo
.HandleInfo
= MarkHandle32
->HandleInfo
;
7376 LocalMarkHandleInfo
.UsnSourceInfo
= MarkHandle32
->UsnSourceInfo
;
7377 LocalMarkHandleInfo
.VolumeHandle
= (HANDLE
)(ULONG_PTR
)(LONG
) MarkHandle32
->VolumeHandle
;
7379 HandleInfo
= &LocalMarkHandleInfo
;
7386 // Get the input buffer pointer and check its length.
7389 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( MARK_HANDLE_INFO
)) {
7391 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7392 return STATUS_BUFFER_TOO_SMALL
;
7395 HandleInfo
= (PMARK_HANDLE_INFO
) Irp
->AssociatedIrp
.SystemBuffer
;
7397 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7402 // Check that only legal bits are being set.
7403 // We currently only support two bits: protect clusters and unprotect clusters.
7405 // Note that we don't actually support the USN journal, but we must ignore the flags in order
7406 // to preserve compatibility.
7409 if (FlagOn( HandleInfo
->HandleInfo
,
7410 ~(MARK_HANDLE_PROTECT_CLUSTERS
)) ||
7411 (FlagOn( HandleInfo
->HandleInfo
,
7413 (IrpSp
->MinorFunction
!= IRP_MN_KERNEL_CALL
)) ||
7414 FlagOn(HandleInfo
->UsnSourceInfo
,
7415 ~(USN_SOURCE_DATA_MANAGEMENT
|
7416 USN_SOURCE_AUXILIARY_DATA
|
7417 USN_SOURCE_REPLICATION_MANAGEMENT
) ) ) {
7419 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7420 return STATUS_INVALID_PARAMETER
;
7424 // Check that the user has a valid volume handle or the manage volume
7425 // privilege or is a kernel mode caller
7427 // NOTE: the kernel mode check is only valid because the rdr doesn't support this
7431 if ((Irp
->RequestorMode
!= KernelMode
) &&
7432 (IrpSp
->MinorFunction
!= IRP_MN_KERNEL_CALL
) &&
7433 !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
) &&
7434 ( FlagOn( HandleInfo
->HandleInfo
, MARK_HANDLE_PROTECT_CLUSTERS
) || (HandleInfo
->UsnSourceInfo
!= 0) )) {
7436 if (HandleInfo
->VolumeHandle
== 0) {
7437 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
7438 return STATUS_ACCESS_DENIED
;
7441 Status
= ObReferenceObjectByHandle( HandleInfo
->VolumeHandle
,
7448 (PVOID
*)&DasdFileObject
,
7452 if (!NT_SUCCESS(Status
)) {
7454 FatCompleteRequest( IrpContext
, Irp
, Status
);
7459 // Check that this file object is opened on the same volume as the
7460 // handle used to call this routine.
7463 if (DasdFileObject
->Vpb
!= Vcb
->Vpb
) {
7465 ObDereferenceObject( DasdFileObject
);
7467 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7468 return STATUS_INVALID_PARAMETER
;
7472 // Now decode this FileObject and verify it is a volume handle.
7473 // We don't care to raise on dismounts here because
7474 // we check for that further down anyway. So send FALSE.
7478 #pragma prefast( suppress:28931, "convenient for debugging" )
7480 TypeOfOpen
= FatDecodeFileObject( DasdFileObject
, &Vcb
, &DasdFcb
, &DasdCcb
) ;
7482 ObDereferenceObject( DasdFileObject
);
7484 if ((DasdCcb
== NULL
) || !FlagOn( DasdCcb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7486 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
7487 return STATUS_ACCESS_DENIED
;
7494 FatAcquireExclusiveFcb(IrpContext
, Fcb
);
7497 FatVerifyFcb( IrpContext
, Fcb
);
7499 if (HandleInfo
->HandleInfo
& MARK_HANDLE_PROTECT_CLUSTERS
) {
7501 if (Fcb
->FcbState
& FCB_STATE_DENY_DEFRAG
) {
7504 // It's already set, bail out.
7507 try_return( Status
= STATUS_ACCESS_DENIED
);
7510 Ccb
->Flags
|= CCB_FLAG_DENY_DEFRAG
;
7511 Fcb
->FcbState
|= FCB_STATE_DENY_DEFRAG
;
7521 FatReleaseFcb(IrpContext
, Fcb
);
7526 FatCompleteRequest( IrpContext
, Irp
, Status
);
7531 _Requires_lock_held_(_Global_critical_region_
)
7533 FatFlushAndCleanVolume(
7534 IN PIRP_CONTEXT IrpContext
,
7537 IN FAT_FLUSH_TYPE FlushType
7541 Routine Description:
7543 This routine flushes and otherwise preparse a volume to be eligible
7544 for deletion. The dismount and PNP paths share the need for this
7547 The Vcb will always be valid on return from this function. It is the
7548 caller's responsibility to attempt the dismount/deletion, and to setup
7549 allocation support again if the volume will be brought back from the
7554 Irp - Irp for the overlying request
7556 Vcb - the volume being operated on
7558 FlushType - specifies the kind of flushing desired
7562 NTSTATUS - The return status for the operation.
7569 // The volume must be held exclusive.
7572 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext
, Vcb
));
7575 // There is no fail, flush everything. If invalidating, it is important
7576 // that we invalidate as we flush (eventually, w/ paging io held) so that we
7577 // error out the maximum number of late writes.
7580 if (FlushType
!= NoFlush
) {
7582 (VOID
) FatFlushVolume( IrpContext
, Vcb
, FlushType
);
7585 FatCloseEaFile( IrpContext
, Vcb
, FALSE
);
7588 // Now, tell the device to flush its buffers.
7591 if (FlushType
!= NoFlush
) {
7593 (VOID
)FatHijackIrpAndFlushDevice( IrpContext
, Irp
, Vcb
->TargetDeviceObject
);
7597 // Now purge everything in sight. We're trying to provoke as many closes as
7598 // soon as possible, this volume may be on its way out.
7601 if (FlushType
!= FlushWithoutPurge
) {
7603 CcPurgeCacheSection( &Vcb
->SectionObjectPointers
,
7608 (VOID
) FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, NoFlush
);
7612 // If the volume was dirty and we were allowed to flush, do the processing that
7613 // the delayed callback would have done.
7616 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
)) {
7619 // Cancel any pending clean volumes.
7622 (VOID
)KeCancelTimer( &Vcb
->CleanVolumeTimer
);
7623 (VOID
)KeRemoveQueueDpc( &Vcb
->CleanVolumeDpc
);
7626 if (FlushType
!= NoFlush
) {
7629 // The volume is now clean, note it.
7632 if (!FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
)) {
7634 FatMarkVolume( IrpContext
, Vcb
, VolumeClean
);
7635 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
);
7639 // Unlock the volume if it is removable.
7642 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
) &&
7643 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
)) {
7645 FatToggleMediaEjectDisable( IrpContext
, Vcb
, FALSE
);
7652 #if (NTDDI_VERSION >= NTDDI_WIN8)
7655 _Requires_lock_held_(_Global_critical_region_
)
7657 FatSetPurgeFailureMode (
7658 _In_ PIRP_CONTEXT IrpContext
,
7663 This routine is used to enable or disable the purge failure mode
7664 on a file. When in this mode the file system will propagate purge
7665 failures encountered during coherency purges. Normally these are
7666 ignored for application compatibilty purposes. Since the normal
7667 behavior can lead to cache incoherency there needs to be a way to
7668 force error propagation, particulary when a filter has mapped a
7669 section for the purposes of scanning the file in the background.
7671 The purge failure mode is a reference count because it is set
7672 per mapped section and there may be multiple sections backed by
7677 IrpContext - Supplies the Irp Context.
7678 Irp - Supplies the Irp being processed.
7682 NTSTATUS - The return status for the operation.
7686 NTSTATUS Status
= STATUS_SUCCESS
;
7687 PIO_STACK_LOCATION IrpSp
;
7688 TYPE_OF_OPEN TypeOfOpen
;
7692 PSET_PURGE_FAILURE_MODE_INPUT SetPurgeInput
;
7693 BOOLEAN FcbAcquired
= FALSE
;
7697 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7700 // Force WAIT to true.
7703 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7706 // This has to be a kernel only call. Can't let a user request
7707 // change the purge failure mode count
7710 if (Irp
->RequestorMode
!= KernelMode
) {
7712 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7713 return STATUS_INVALID_PARAMETER
;
7717 // Extract and decode the file object and check for type of open.
7720 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
7722 if (TypeOfOpen
== UserDirectoryOpen
) {
7724 FatCompleteRequest( IrpContext
, Irp
, STATUS_FILE_IS_A_DIRECTORY
);
7725 return STATUS_FILE_IS_A_DIRECTORY
;
7728 if (TypeOfOpen
!= UserFileOpen
) {
7730 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7731 return STATUS_INVALID_PARAMETER
;
7735 // Get the input buffer pointer and check its length.
7738 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( SET_PURGE_FAILURE_MODE_INPUT
)) {
7740 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7741 return STATUS_BUFFER_TOO_SMALL
;
7744 SetPurgeInput
= (PSET_PURGE_FAILURE_MODE_INPUT
) Irp
->AssociatedIrp
.SystemBuffer
;
7746 if (!FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_ENABLED
| SET_PURGE_FAILURE_MODE_DISABLED
)) {
7748 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7749 return STATUS_INVALID_PARAMETER
;
7755 // Acquire the FCB exclusively to synchronize with coherency flush
7759 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
7762 FatVerifyFcb( IrpContext
, Fcb
);
7764 if (FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_ENABLED
)) {
7766 if (Fcb
->PurgeFailureModeEnableCount
== MAXULONG
) {
7768 try_return( Status
= STATUS_INVALID_PARAMETER
);
7771 Fcb
->PurgeFailureModeEnableCount
+= 1;
7775 ASSERT( FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_DISABLED
));
7777 if (Fcb
->PurgeFailureModeEnableCount
== 0) {
7779 try_return( Status
= STATUS_INVALID_PARAMETER
);
7782 Fcb
->PurgeFailureModeEnableCount
-= 1;
7790 FatReleaseFcb( IrpContext
, Fcb
);
7795 // Complete the irp if we terminated normally.
7798 FatCompleteRequest( IrpContext
, Irp
, Status
);
7807 FatSearchBufferForLabel(
7808 IN PIRP_CONTEXT IrpContext
,
7812 OUT PBOOLEAN LabelFound
7816 Routine Description:
7818 Search a buffer (taken from the root directory) for a volume label
7819 matching the label in the
7823 IrpContext - Supplies our irp context
7824 Vpb - Vpb supplying the volume label
7825 Buffer - Supplies the buffer we'll search
7826 Size - The size of the buffer in bytes.
7827 LabelFound - Returns whether a label was found.
7831 There are four interesting cases:
7833 1) Some random error occurred - that error returned as status, LabelFound
7836 2) No label was found - STATUS_SUCCESS returned, LabelFound is FALSE.
7838 3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE.
7840 4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound
7847 WCHAR UnicodeBuffer
[11];
7850 PDIRENT TerminationDirent
;
7851 ULONG VolumeLabelLength
;
7852 OEM_STRING OemString
;
7853 UNICODE_STRING UnicodeString
;
7857 UNREFERENCED_PARAMETER( IrpContext
);
7861 TerminationDirent
= Dirent
+ Size
/ sizeof(DIRENT
);
7863 while ( Dirent
< TerminationDirent
) {
7865 if ( Dirent
->FileName
[0] == FAT_DIRENT_NEVER_USED
) {
7867 Dirent
= TerminationDirent
;
7872 // If the entry is the non-deleted volume label break from the loop.
7874 // Note that all out parameters are already correctly set.
7877 if (((Dirent
->Attributes
& ~FAT_DIRENT_ATTR_ARCHIVE
) ==
7878 FAT_DIRENT_ATTR_VOLUME_ID
) &&
7879 (Dirent
->FileName
[0] != FAT_DIRENT_DELETED
)) {
7887 if (Dirent
>= TerminationDirent
) {
7890 // We've run out of buffer.
7893 *LabelFound
= FALSE
;
7894 return STATUS_SUCCESS
;
7899 // Compute the length of the volume name
7902 OemString
.Buffer
= (PCHAR
)&Dirent
->FileName
[0];
7903 OemString
.MaximumLength
= 11;
7905 for ( OemString
.Length
= 11;
7906 OemString
.Length
> 0;
7907 OemString
.Length
-= 1) {
7909 if ( (Dirent
->FileName
[OemString
.Length
-1] != 0x00) &&
7910 (Dirent
->FileName
[OemString
.Length
-1] != 0x20) ) { break; }
7913 UnicodeString
.MaximumLength
= sizeof( UnicodeBuffer
);
7914 UnicodeString
.Buffer
= &UnicodeBuffer
[0];
7916 Status
= RtlOemStringToCountedUnicodeString( &UnicodeString
,
7920 if ( !NT_SUCCESS( Status
) ) {
7925 VolumeLabelLength
= UnicodeString
.Length
;
7927 if ( (VolumeLabelLength
!= (ULONG
)Vpb
->VolumeLabelLength
) ||
7928 (!RtlEqualMemory(&UnicodeBuffer
[0],
7929 &Vpb
->VolumeLabel
[0],
7930 VolumeLabelLength
)) ) {
7932 return STATUS_WRONG_VOLUME
;
7936 // We found a matching label.
7940 return STATUS_SUCCESS
;
7945 FatVerifyLookupFatEntry (
7946 IN PIRP_CONTEXT IrpContext
,
7949 IN OUT PULONG FatEntry
7952 ULONG PageEntryOffset
;
7953 ULONG OffsetIntoVolumeFile
;
7958 NT_ASSERT(Vcb
->AllocationSupport
.FatIndexBitSize
== 32);
7960 FatVerifyIndexIsValid( IrpContext
, Vcb
, FatIndex
);
7962 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned
,
7964 TAG_ENTRY_LOOKUP_BUFFER
);
7966 OffsetIntoVolumeFile
= FatReservedBytes(&Vcb
->Bpb
) + FatIndex
* sizeof(ULONG
);
7967 PageEntryOffset
= (OffsetIntoVolumeFile
% PAGE_SIZE
) / sizeof(ULONG
);
7971 FatPerformVerifyDiskRead( IrpContext
,
7974 OffsetIntoVolumeFile
& ~(PAGE_SIZE
- 1),
7978 *FatEntry
= ((PULONG
)(Buffer
))[PageEntryOffset
];
7982 ExFreePool( Buffer
);
7987 // Local support routine
7990 _Requires_lock_held_(_Global_critical_region_
)
7992 FatScanForDismountedVcb (
7993 IN PIRP_CONTEXT IrpContext
7998 Routine Description:
8000 This routine walks through the list of Vcb's looking for any which may
8001 now be deleted. They may have been left on the list because there were
8002 outstanding references.
8021 // Walk through all of the Vcb's attached to the global data.
8024 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
8027 #pragma prefast( push )
8028 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
8029 #pragma prefast( disable: 28193, "this will always wait" )
8032 FatAcquireExclusiveGlobal( IrpContext
);
8035 #pragma prefast( pop )
8038 Links
= FatData
.VcbQueue
.Flink
;
8040 while (Links
!= &FatData
.VcbQueue
) {
8042 Vcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
8045 // Move to the next link now since the current Vcb may be deleted.
8048 Links
= Links
->Flink
;
8051 // Try to acquire the VCB for exclusive access. If we cannot, just skip
8056 #pragma prefast( push )
8057 #pragma prefast( disable:28103,"prefast cannot work out that Vcb->Resource will be released below." )
8058 #pragma prefast( disable:28109,"prefast cannot work out the Vcb is not already held" );
8061 if (!ExAcquireResourceExclusiveLite( &(Vcb
->Resource
), FALSE
)) {
8067 #pragma prefast( pop )
8070 // Check if this Vcb can go away.
8073 VcbDeleted
= FatCheckForDismount( IrpContext
,
8078 // If the VCB was not deleted, release it.
8083 ExReleaseResourceLite( &(Vcb
->Resource
) );
8087 FatReleaseGlobal( IrpContext
);
8092 #if (NTDDI_VERSION >= NTDDI_WIN7)
8093 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8095 // FatSetZeroOnDeallocate is used when we need to stomp over the contents with zeros when a file is deleted.
8099 FatSetZeroOnDeallocate (
8100 __in PIRP_CONTEXT IrpContext
,
8104 NTSTATUS Status
= STATUS_SUCCESS
;
8110 TYPE_OF_OPEN TypeOfOpen
;
8112 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
8114 BOOLEAN ReleaseFcb
= FALSE
;
8119 // This call should always be synchronous.
8122 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
8124 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
8126 if ((TypeOfOpen
!= UserFileOpen
) ||
8127 (!IrpSp
->FileObject
->WriteAccess
) ) {
8129 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
8130 return STATUS_ACCESS_DENIED
;
8134 // Readonly mount should be just that: read only.
8137 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
8139 FatCompleteRequest( IrpContext
, Irp
, STATUS_MEDIA_WRITE_PROTECTED
);
8140 return STATUS_MEDIA_WRITE_PROTECTED
;
8144 // Acquire main then paging to exclude everyone from this FCB.
8147 FatAcquireExclusiveFcb(IrpContext
, FcbOrDcb
);
8152 SetFlag( FcbOrDcb
->FcbState
, FCB_STATE_ZERO_ON_DEALLOCATION
);
8157 FatReleaseFcb(IrpContext
, FcbOrDcb
);
8162 FatCompleteRequest( IrpContext
, Irp
, Status
);