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
;
920 IO_STATUS_BLOCK Iosb
= {0};
922 IO_STATUS_BLOCK Iosb
= {{0}};
924 ULONG ChangeCount
= 0;
926 DISK_GEOMETRY Geometry
;
928 PARTITION_INFORMATION_EX PartitionInformation
;
929 NTSTATUS StatusPartInfo
;
931 #if (NTDDI_VERSION > NTDDI_WIN8)
932 GUID VolumeGuid
= {0};
938 DebugTrace(+1, Dbg
, "FatMountVolume\n", 0);
939 DebugTrace( 0, Dbg
, "TargetDeviceObject = %p\n", TargetDeviceObject
);
940 DebugTrace( 0, Dbg
, "Vpb = %p\n", Vpb
);
942 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
943 NT_ASSERT( FatDeviceIsFatFsdo( FsDeviceObject
));
946 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
949 if (FlagOn(TargetDeviceObject
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
952 // Verify that there is a disk here and pick up the change count.
955 Status
= FatPerformDevIoCtrl( IrpContext
,
956 IOCTL_DISK_CHECK_VERIFY
,
966 if (!NT_SUCCESS( Status
)) {
969 // If we will allow a raw mount then avoid sending the popup.
971 // Only send this on "true" disk devices to handle the accidental
972 // legacy of FAT. No other FS will throw a harderror on empty
975 // Cmd should really handle this per 9x.
978 if (!FlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
) &&
979 Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_DISK
) {
981 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
989 if (Iosb
.Information
!= sizeof(ULONG
)) {
992 // Be safe about the count in case the driver didn't fill it in
999 // If this is a CD class device, then check to see if there is a
1000 // 'data track' or not. This is to avoid issuing paging reads which will
1001 // fail later in the mount process (e.g. CD-DA or blank CD media)
1004 if ((TargetDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) &&
1005 !FatScanForDataTrack( IrpContext
, TargetDeviceObject
)) {
1007 return STATUS_UNRECOGNIZED_VOLUME
;
1011 // Ping the volume with a partition query and pick up the partition
1012 // type. We'll check this later to avoid some scurrilous volumes.
1015 StatusPartInfo
= FatPerformDevIoCtrl( IrpContext
,
1016 IOCTL_DISK_GET_PARTITION_INFO_EX
,
1020 &PartitionInformation
,
1021 sizeof(PARTITION_INFORMATION_EX
),
1027 // Make sure we can wait.
1030 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
1033 // Do a quick check to see if there any Vcb's which can be removed.
1036 FatScanForDismountedVcb( IrpContext
);
1039 // Initialize the Bcbs and our final state so that the termination
1040 // handlers will know what to free or unpin
1048 MountNewVolume
= FALSE
;
1053 // Synchronize with FatCheckForDismount(), which modifies the vpb.
1057 #pragma prefast( push )
1058 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1059 #pragma prefast( disable: 28193, "this will always wait" )
1062 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
1065 #pragma prefast( pop )
1069 // Create a new volume device object. This will have the Vcb
1070 // hanging off of its end, and set its alignment requirement
1071 // from the device we talk to.
1074 if (!NT_SUCCESS(Status
= IoCreateDevice( FatData
.DriverObject
,
1075 sizeof(VOLUME_DEVICE_OBJECT
) - sizeof(DEVICE_OBJECT
),
1077 FILE_DEVICE_DISK_FILE_SYSTEM
,
1080 (PDEVICE_OBJECT
*)&VolDo
))) {
1082 try_return( Status
);
1086 // Our alignment requirement is the larger of the processor alignment requirement
1087 // already in the volume device object and that in the TargetDeviceObject
1090 if (TargetDeviceObject
->AlignmentRequirement
> VolDo
->DeviceObject
.AlignmentRequirement
) {
1092 VolDo
->DeviceObject
.AlignmentRequirement
= TargetDeviceObject
->AlignmentRequirement
;
1096 // Initialize the overflow queue for the volume
1099 VolDo
->OverflowQueueCount
= 0;
1100 InitializeListHead( &VolDo
->OverflowQueue
);
1102 VolDo
->PostedRequestCount
= 0;
1103 KeInitializeSpinLock( &VolDo
->OverflowQueueSpinLock
);
1106 // We must initialize the stack size in our device object before
1107 // the following reads, because the I/O system has not done it yet.
1108 // This must be done before we clear the device initializing flag
1109 // otherwise a filter could attach and copy the wrong stack size into
1110 // it's device object.
1113 VolDo
->DeviceObject
.StackSize
= (CCHAR
)(TargetDeviceObject
->StackSize
+ 1);
1116 // We must also set the sector size correctly in our device object
1117 // before clearing the device initializing flag.
1120 Status
= FatPerformDevIoCtrl( IrpContext
,
1121 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1126 sizeof( DISK_GEOMETRY
),
1131 if (!NT_SUCCESS( Status
)) {
1133 try_return( Status
);
1137 #pragma prefast( suppress: 28175, "this is a filesystem driver, touching SectorSize is fine" )
1139 VolDo
->DeviceObject
.SectorSize
= (USHORT
)Geometry
.BytesPerSector
;
1142 // Indicate that this device object is now completely initialized
1145 ClearFlag(VolDo
->DeviceObject
.Flags
, DO_DEVICE_INITIALIZING
);
1148 // Now Before we can initialize the Vcb we need to set up the device
1149 // object field in the Vpb to point to our new volume device object.
1150 // This is needed when we create the virtual volume file's file object
1151 // in initialize vcb.
1154 Vpb
->DeviceObject
= (PDEVICE_OBJECT
)VolDo
;
1157 // If the real device needs verification, temporarily clear the
1161 RealDevice
= Vpb
->RealDevice
;
1163 if ( FlagOn(RealDevice
->Flags
, DO_VERIFY_VOLUME
) ) {
1165 ClearFlag(RealDevice
->Flags
, DO_VERIFY_VOLUME
);
1167 WeClearedVerifyRequiredBit
= TRUE
;
1171 // Initialize the new vcb
1174 FatInitializeVcb( IrpContext
,
1180 // Get a reference to the Vcb hanging off the end of the device object
1186 // Read in the boot sector, and have the read be the minumum size
1187 // needed. We know we can wait.
1191 // We need to commute errors on CD so that CDFS will get its crack. Audio
1192 // and even data media may not be universally readable on sector zero.
1197 FatReadVolumeFile( IrpContext
,
1200 sizeof(PACKED_BOOT_SECTOR
),
1202 (PVOID
*)&BootSector
);
1204 } _SEH2_EXCEPT( Vpb
->RealDevice
->DeviceType
== FILE_DEVICE_CD_ROM
?
1205 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
1211 // Call a routine to check the boot sector to see if it is fat
1214 if (BootBcb
== NULL
|| !FatIsBootSectorFat( BootSector
)) {
1216 DebugTrace(0, Dbg
, "Not a Fat Volume\n", 0);
1219 // Complete the request and return to our caller
1222 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
1225 #if (NTDDI_VERSION > NTDDI_WIN8)
1227 // Initialize the volume guid.
1230 if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb
->TargetDeviceObject
, &VolumeGuid
))) {
1234 // Stash a copy away in the VCB.
1237 RtlCopyMemory( &Vcb
->VolumeGuid
, &VolumeGuid
, sizeof(GUID
));
1243 // Stash away a copy of the volume GUID path in our VCB.
1246 if (Vcb
->VolumeGuidPath
.Buffer
) {
1247 ExFreePool( Vcb
->VolumeGuidPath
.Buffer
);
1248 Vcb
->VolumeGuidPath
.Buffer
= NULL
;
1249 Vcb
->VolumeGuidPath
.Length
= Vcb
->VolumeGuidPath
.MaximumLength
= 0;
1252 IoVolumeDeviceToGuidPath( Vcb
->TargetDeviceObject
, &Vcb
->VolumeGuidPath
);
1256 // Unpack the BPB. We used to do some sanity checking of the FATs at
1257 // this point, but authoring errors on third-party devices prevent
1258 // us from continuing to safeguard ourselves. We can only hope the
1259 // boot sector check is good enough.
1261 // (read: digital cameras)
1263 // Win9x does the same.
1266 FatUnpackBios( &Vcb
->Bpb
, &BootSector
->PackedBpb
);
1269 // Check if we have an OS/2 Boot Manager partition and treat it as an
1270 // unknown file system. We'll check the partition type in from the
1271 // partition table and we ensure that it has less than 0x80 sectors,
1272 // which is just a heuristic that will capture all real OS/2 BM partitions
1273 // and avoid the chance we'll discover partitions which erroneously
1274 // (but to this point, harmlessly) put down the OS/2 BM type.
1276 // Note that this is only conceivable on good old MBR media.
1278 // The OS/2 Boot Manager boot format mimics a FAT16 partition in sector
1279 // zero but does is not a real FAT16 file system. For example, the boot
1280 // sector indicates it has 2 FATs but only really has one, with the boot
1281 // manager code overlaying the second FAT. If we then set clean bits in
1282 // FAT[0] we'll corrupt that code.
1285 if (NT_SUCCESS( StatusPartInfo
) &&
1286 (PartitionInformation
.PartitionStyle
== PARTITION_STYLE_MBR
&&
1287 PartitionInformation
.Mbr
.PartitionType
== PARTITION_OS2BOOTMGR
) &&
1288 (Vcb
->Bpb
.Sectors
!= 0 &&
1289 Vcb
->Bpb
.Sectors
< 0x80)) {
1291 DebugTrace( 0, Dbg
, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 );
1294 // Complete the request and return to our caller
1297 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
1301 // Verify that the sector size recorded in the Bpb matches what the
1302 // device currently reports it's sector size to be.
1305 if ( !NT_SUCCESS( Status
) ||
1306 (Geometry
.BytesPerSector
!= Vcb
->Bpb
.BytesPerSector
)) {
1308 try_return( Status
= STATUS_UNRECOGNIZED_VOLUME
);
1312 // This is a fat volume, so extract the bpb, serial number. The
1313 // label we'll get later after we've created the root dcb.
1315 // Note that the way data caching is done, we set neither the
1316 // direct I/O or Buffered I/O bit in the device object flags.
1319 if (Vcb
->Bpb
.Sectors
!= 0) { Vcb
->Bpb
.LargeSectors
= 0; }
1321 if (IsBpbFat32(&BootSector
->PackedBpb
)) {
1323 CopyUchar4( &Vpb
->SerialNumber
, ((PPACKED_BOOT_SECTOR_EX
)BootSector
)->Id
);
1327 CopyUchar4( &Vpb
->SerialNumber
, BootSector
->Id
);
1330 // Allocate space for the stashed boot sector chunk. This only has meaning on
1331 // FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and
1332 // its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24
1336 Vcb
->First0x24BytesOfBootSector
=
1337 FsRtlAllocatePoolWithTag( PagedPool
,
1342 // Stash a copy of the first 0x24 bytes
1345 RtlCopyMemory( Vcb
->First0x24BytesOfBootSector
,
1351 // Now unpin the boot sector, so when we set up allocation eveything
1355 FatUnpinBcb( IrpContext
, BootBcb
);
1358 // Compute a number of fields for Vcb.AllocationSupport
1361 FatSetupAllocationSupport( IrpContext
, Vcb
);
1364 // Sanity check the FsInfo information for FAT32 volumes. Silently deal
1365 // with messed up information by effectively disabling FsInfo updates.
1368 if (FatIsFat32( Vcb
)) {
1370 if (Vcb
->Bpb
.FsInfoSector
>= Vcb
->Bpb
.ReservedSectors
) {
1372 Vcb
->Bpb
.FsInfoSector
= 0;
1378 // Create a root Dcb so we can read in the volume label. If this is FAT32, we can
1379 // discover corruption in the FAT chain.
1381 // NOTE: this exception handler presumes that this is the only spot where we can
1382 // discover corruption in the mount process. If this ever changes, this handler
1383 // MUST be expanded. The reason we have this guy here is because we have to rip
1384 // the structures down now (in the finally below) and can't wait for the outer
1385 // exception handling to do it for us, at which point everything will have vanished.
1390 FatCreateRootDcb( IrpContext
, Vcb
);
1392 } _SEH2_EXCEPT (_SEH2_GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR
? EXCEPTION_EXECUTE_HANDLER
:
1393 EXCEPTION_CONTINUE_SEARCH
) {
1396 // The volume needs to be dirtied, do it now. Note that at this point we have built
1397 // enough of the Vcb to pull this off.
1400 FatCheckDirtyBit( IrpContext
,
1404 // Set the dirty bit if it is not set already
1407 if ( !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
)) {
1409 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNT_IN_PROGRESS
);
1410 FatMarkVolume( IrpContext
, Vcb
, VolumeDirty
);
1411 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNT_IN_PROGRESS
);
1415 // Now keep bailing out ...
1418 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1421 FatLocateVolumeLabel( IrpContext
,
1425 (PVBO
)&ByteOffset
);
1427 if (Dirent
!= NULL
) {
1429 OEM_STRING OemString
;
1430 UNICODE_STRING UnicodeString
;
1433 // Compute the length of the volume name
1436 OemString
.Buffer
= (PCHAR
)&Dirent
->FileName
[0];
1437 OemString
.MaximumLength
= 11;
1439 for ( OemString
.Length
= 11;
1440 OemString
.Length
> 0;
1441 OemString
.Length
-= 1) {
1443 if ( (Dirent
->FileName
[OemString
.Length
-1] != 0x00) &&
1444 (Dirent
->FileName
[OemString
.Length
-1] != 0x20) ) { break; }
1447 UnicodeString
.MaximumLength
= MAXIMUM_VOLUME_LABEL_LENGTH
;
1448 UnicodeString
.Buffer
= &Vcb
->Vpb
->VolumeLabel
[0];
1450 Status
= RtlOemStringToCountedUnicodeString( &UnicodeString
,
1454 if ( !NT_SUCCESS( Status
) ) {
1456 try_return( Status
);
1459 Vpb
->VolumeLabelLength
= UnicodeString
.Length
;
1463 Vpb
->VolumeLabelLength
= 0;
1467 // Use the change count we noted initially *before* doing any work.
1468 // If something came along in the midst of this operation, we'll
1469 // verify and discover the problem.
1472 Vcb
->ChangeCount
= ChangeCount
;
1475 // Now scan the list of previously mounted volumes and compare
1476 // serial numbers and volume labels off not currently mounted
1477 // volumes to see if we have a match.
1480 for (Links
= FatData
.VcbQueue
.Flink
;
1481 Links
!= &FatData
.VcbQueue
;
1482 Links
= Links
->Flink
) {
1484 OldVcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
1485 OldVpb
= OldVcb
->Vpb
;
1488 // Skip over ourselves since we're already in the VcbQueue
1491 if (OldVpb
== Vpb
) { continue; }
1494 // Check for a match:
1496 // Serial Number, VolumeLabel and Bpb must all be the same.
1497 // Also the volume must have failed a verify before (ie.
1498 // VolumeNotMounted), and it must be in the same physical
1499 // drive than it was mounted in before.
1502 if ( (OldVpb
->SerialNumber
== Vpb
->SerialNumber
) &&
1503 (OldVcb
->VcbCondition
== VcbNotMounted
) &&
1504 (OldVpb
->RealDevice
== RealDevice
) &&
1505 (OldVpb
->VolumeLabelLength
== Vpb
->VolumeLabelLength
) &&
1506 (RtlEqualMemory(&OldVpb
->VolumeLabel
[0],
1507 &Vpb
->VolumeLabel
[0],
1508 Vpb
->VolumeLabelLength
)) &&
1509 (RtlEqualMemory(&OldVcb
->Bpb
,
1511 IsBpbFat32(&Vcb
->Bpb
) ?
1512 sizeof(BIOS_PARAMETER_BLOCK
) :
1513 FIELD_OFFSET(BIOS_PARAMETER_BLOCK
,
1514 LargeSectorsPerFat
) ))) {
1526 DebugTrace(0, Dbg
, "Doing a remount\n", 0);
1527 DebugTrace(0, Dbg
, "Vcb = %p\n", Vcb
);
1528 DebugTrace(0, Dbg
, "Vpb = %p\n", Vpb
);
1529 DebugTrace(0, Dbg
, "OldVcb = %p\n", OldVcb
);
1530 DebugTrace(0, Dbg
, "OldVpb = %p\n", OldVpb
);
1533 // Swap target device objects between the VCBs. That way
1534 // the old VCB will start using the new target device object,
1535 // and the new VCB will be torn down and deference the old
1536 // target device object.
1539 Vcb
->TargetDeviceObject
= OldVcb
->TargetDeviceObject
;
1540 OldVcb
->TargetDeviceObject
= TargetDeviceObject
;
1543 // This is a remount, so link the old vpb in place
1547 NT_ASSERT( !FlagOn( OldVcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
) );
1549 FatSetVcbCondition( OldVcb
, VcbGood
);
1550 OldVpb
->RealDevice
= Vpb
->RealDevice
;
1551 ClearFlag( OldVcb
->VcbState
, VCB_STATE_VPB_NOT_ON_DEVICE
);
1554 #pragma prefast( suppress: 28175, "touching Vpb is ok for a filesystem" )
1556 OldVpb
->RealDevice
->Vpb
= OldVpb
;
1559 // Use the new changecount.
1562 OldVcb
->ChangeCount
= Vcb
->ChangeCount
;
1565 // If the new VPB is the VPB referenced in the original Irp, set
1566 // that reference back to the old VPB.
1569 IrpVpb
= &IoGetCurrentIrpStackLocation(IrpContext
->OriginatingIrp
)->Parameters
.MountVolume
.Vpb
;
1571 if (*IrpVpb
== Vpb
) {
1577 // We do not want to touch this VPB again. It will get cleaned up when
1578 // the new VCB is cleaned up.
1581 NT_ASSERT( Vcb
->Vpb
== Vpb
);
1584 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
1585 FatSetVcbCondition( Vcb
, VcbBad
);
1588 // Reinitialize the volume file cache and allocation support.
1592 CC_FILE_SIZES FileSizes
;
1594 FileSizes
.AllocationSize
.QuadPart
=
1595 FileSizes
.FileSize
.QuadPart
= ( 0x40000 + 0x1000 );
1596 FileSizes
.ValidDataLength
= FatMaxLarge
;
1598 DebugTrace(0, Dbg
, "Truncate and reinitialize the volume file\n", 0);
1600 FatInitializeCacheMap( OldVcb
->VirtualVolumeFile
,
1603 &FatData
.CacheManagerNoOpCallbacks
,
1607 // Redo the allocation support
1610 FatSetupAllocationSupport( IrpContext
, OldVcb
);
1613 // Get the state of the dirty bit.
1616 FatCheckDirtyBit( IrpContext
, OldVcb
);
1619 // Check for write protected media.
1622 if (FatIsMediaWriteProtected(IrpContext
, TargetDeviceObject
)) {
1624 SetFlag( OldVcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1628 ClearFlag( OldVcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1633 // Complete the request and return to our caller
1636 try_return( Status
= STATUS_SUCCESS
);
1639 DebugTrace(0, Dbg
, "Mount a new volume\n", 0);
1642 // This is a new mount
1644 // Create a blank ea data file fcb, just not for Fat32.
1647 if (!FatIsFat32(Vcb
)) {
1652 RtlZeroMemory( &TempDirent
, sizeof(DIRENT
) );
1653 RtlCopyMemory( &TempDirent
.FileName
[0], "EA DATA SF", 11 );
1655 EaFcb
= FatCreateFcb( IrpContext
,
1667 // Deny anybody who trys to open the file.
1670 SetFlag( EaFcb
->FcbState
, FCB_STATE_SYSTEM_FILE
);
1676 // Get the state of the dirty bit.
1679 FatCheckDirtyBit( IrpContext
, Vcb
);
1683 // Check for write protected media.
1686 if (FatIsMediaWriteProtected(IrpContext
, TargetDeviceObject
)) {
1688 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1692 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
1697 // Lock volume in drive if we just mounted the boot drive.
1700 if (FlagOn(RealDevice
->Flags
, DO_SYSTEM_BOOT_PARTITION
)) {
1702 SetFlag(Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
);
1704 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
)) {
1706 FatToggleMediaEjectDisable( IrpContext
, Vcb
, TRUE
);
1712 // Indicate to our termination handler that we have mounted
1716 MountNewVolume
= TRUE
;
1719 // Complete the request
1722 Status
= STATUS_SUCCESS
;
1725 // Ref the root dir stream object so we can send mount notification.
1728 RootDirectoryFile
= Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
;
1729 ObReferenceObject( RootDirectoryFile
);
1732 // Remove the extra reference to this target DO made on behalf of us
1733 // by the IO system. In the remount case, we permit regular Vcb
1734 // deletion to do this work.
1737 ObDereferenceObject( TargetDeviceObject
);
1744 DebugUnwind( FatMountVolume
);
1746 FatUnpinBcb( IrpContext
, BootBcb
);
1747 FatUnpinBcb( IrpContext
, DirentBcb
);
1750 // Check if a volume was mounted. If not then we need to
1751 // mark the Vpb not mounted again.
1754 if ( !MountNewVolume
) {
1756 if ( Vcb
!= NULL
) {
1759 // A VCB was created and initialized. We need to try to tear it down.
1762 FatCheckForDismount( IrpContext
,
1766 IrpContext
->Vcb
= NULL
;
1768 } else if (VolDo
!= NULL
) {
1771 // The VCB was never initialized, so we need to delete the
1772 // device right here.
1775 IoDeleteDevice( &VolDo
->DeviceObject
);
1779 // See if a remount failed.
1782 if (DoARemount
&& _SEH2_AbnormalTermination()) {
1785 // The remount failed. Try to tear down the old VCB as well.
1788 FatCheckForDismount( IrpContext
,
1794 if ( WeClearedVerifyRequiredBit
== TRUE
) {
1796 SetFlag(RealDevice
->Flags
, DO_VERIFY_VOLUME
);
1799 FatReleaseGlobal( IrpContext
);
1801 DebugTrace(-1, Dbg
, "FatMountVolume -> %08lx\n", Status
);
1805 // Now send mount notification. Note that since this is outside of any
1806 // synchronization since the synchronous delivery of this may go to
1807 // folks that provoke re-entrance to the FS.
1810 if (RootDirectoryFile
!= NULL
) {
1812 #if (NTDDI_VERSION >= NTDDI_WIN8)
1813 if (FatDiskAccountingEnabled
) {
1815 CcSetAdditionalCacheAttributesEx( RootDirectoryFile
, CC_ENABLE_DISK_IO_ACCOUNTING
);
1819 FsRtlNotifyVolumeEvent( RootDirectoryFile
, FSRTL_VOLUME_MOUNT
);
1820 ObDereferenceObject( RootDirectoryFile
);
1828 // Local Support Routine
1831 _Requires_lock_held_(_Global_critical_region_
)
1834 IN PIRP_CONTEXT IrpContext
,
1840 Routine Description:
1842 This routine performs the verify volume operation by checking the volume
1843 label and serial number physically on the media with the the Vcb
1844 currently claiming to have the volume mounted. It is responsible for
1845 either completing or enqueuing the input Irp.
1847 Regardless of whether the verify operation succeeds, the following
1848 operations are performed:
1850 - Set Vcb->VirtualEaFile back to its initial state.
1851 - Purge all cached data (flushing first if verify succeeds)
1852 - Mark all Fcbs as needing verification
1854 If the volumes verifies correctly we also must:
1856 - Check the volume dirty bit.
1857 - Reinitialize the allocation support
1858 - Flush any dirty data
1860 If the volume verify fails, it may never be mounted again. If it is
1861 mounted again, it will happen as a remount operation. In preparation
1862 for that, and to leave the volume in a state that can be "lazy deleted"
1863 the following operations are performed:
1865 - Set the Vcb condition to VcbNotMounted
1866 - Uninitialize the volume file cachemap
1867 - Tear down the allocation support
1869 In the case of an abnormal termination we haven't determined the state
1870 of the volume, so we set the Device Object as needing verification again.
1874 Irp - Supplies the Irp to process
1878 NTSTATUS - If the verify operation completes, it will return either
1879 STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or
1880 other error is encountered, that status will be returned.
1885 NTSTATUS Status
= STATUS_SUCCESS
;
1887 PIO_STACK_LOCATION IrpSp
;
1889 PDIRENT RootDirectory
= NULL
;
1890 PPACKED_BOOT_SECTOR BootSector
= NULL
;
1892 BIOS_PARAMETER_BLOCK Bpb
;
1894 PVOLUME_DEVICE_OBJECT VolDo
;
1899 BOOLEAN ClearVerify
= FALSE
;
1900 BOOLEAN ReleaseEntireVolume
= FALSE
;
1901 BOOLEAN VerifyAlreadyDone
= FALSE
;
1903 DISK_GEOMETRY DiskGeometry
;
1905 LBO RootDirectoryLbo
;
1906 ULONG RootDirectorySize
;
1909 ULONG ChangeCount
= 0;
1911 IO_STATUS_BLOCK Iosb
= {0};
1913 IO_STATUS_BLOCK Iosb
= {{0}};
1919 // Get the current Irp stack location
1922 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
1924 DebugTrace(+1, Dbg
, "FatVerifyVolume\n", 0);
1925 DebugTrace( 0, Dbg
, "DeviceObject = %p\n", IrpSp
->Parameters
.VerifyVolume
.DeviceObject
);
1926 DebugTrace( 0, Dbg
, "Vpb = %p\n", IrpSp
->Parameters
.VerifyVolume
.Vpb
);
1929 // Save some references to make our life a little easier. Note the Vcb for the purposes
1930 // of exception handling.
1933 VolDo
= (PVOLUME_DEVICE_OBJECT
)IrpSp
->Parameters
.VerifyVolume
.DeviceObject
;
1935 Vpb
= IrpSp
->Parameters
.VerifyVolume
.Vpb
;
1936 IrpContext
->Vcb
= Vcb
= &VolDo
->Vcb
;
1939 // If we cannot wait then enqueue the irp to the fsp and
1940 // return the status to our caller.
1943 if (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
)) {
1945 DebugTrace(0, Dbg
, "Cannot wait for verify.\n", 0);
1947 Status
= FatFsdPostRequest( IrpContext
, Irp
);
1949 DebugTrace(-1, Dbg
, "FatVerifyVolume -> %08lx\n", Status
);
1954 // We are serialized at this point allowing only one thread to
1955 // actually perform the verify operation. Any others will just
1956 // wait and then no-op when checking if the volume still needs
1961 #pragma prefast( push )
1962 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1963 #pragma prefast( disable: 28193, "this will always wait" )
1966 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
1969 #pragma prefast( pop )
1972 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
1976 BOOLEAN AllowRawMount
= BooleanFlagOn( IrpSp
->Flags
, SL_ALLOW_RAW_MOUNT
);
1979 // Mark ourselves as verifying this volume so that recursive I/Os
1980 // will be able to complete.
1983 NT_ASSERT( Vcb
->VerifyThread
== NULL
);
1984 Vcb
->VerifyThread
= KeGetCurrentThread();
1987 // Check if the real device still needs to be verified. If it doesn't
1988 // then obviously someone beat us here and already did the work
1989 // so complete the verify irp with success. Otherwise reenable
1990 // the real device and get to work.
1993 if (!FlagOn(Vpb
->RealDevice
->Flags
, DO_VERIFY_VOLUME
)) {
1995 DebugTrace(0, Dbg
, "RealDevice has already been verified\n", 0);
1997 VerifyAlreadyDone
= TRUE
;
1998 try_return( Status
= STATUS_SUCCESS
);
2002 // Ping the volume with a partition query to make Jeff happy.
2006 PARTITION_INFORMATION_EX PartitionInformation
;
2008 (VOID
) FatPerformDevIoCtrl( IrpContext
,
2009 IOCTL_DISK_GET_PARTITION_INFO_EX
,
2010 Vcb
->TargetDeviceObject
,
2013 &PartitionInformation
,
2014 sizeof(PARTITION_INFORMATION_EX
),
2021 // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
2024 if (FlagOn(Vcb
->TargetDeviceObject
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
2027 // Verify that there is a disk here and pick up the change count.
2030 Status
= FatPerformDevIoCtrl( IrpContext
,
2031 IOCTL_DISK_CHECK_VERIFY
,
2032 Vcb
->TargetDeviceObject
,
2041 if (!NT_SUCCESS( Status
)) {
2044 // If we will allow a raw mount then return WRONG_VOLUME to
2045 // allow the volume to be mounted by raw.
2048 if (AllowRawMount
) {
2050 try_return( Status
= STATUS_WRONG_VOLUME
);
2053 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
2058 if (Iosb
.Information
!= sizeof(ULONG
)) {
2061 // Be safe about the count in case the driver didn't fill it in
2068 // Whatever happens we will have verified this volume at this change
2069 // count, so record that fact.
2072 Vcb
->ChangeCount
= ChangeCount
;
2075 // If this is a CD class device, then check to see if there is a
2076 // 'data track' or not. This is to avoid issuing paging reads which will
2077 // fail later in the mount process (e.g. CD-DA or blank CD media)
2080 if ((Vcb
->TargetDeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) &&
2081 !FatScanForDataTrack( IrpContext
, Vcb
->TargetDeviceObject
)) {
2083 try_return( Status
= STATUS_WRONG_VOLUME
);
2087 // Some devices can change sector sizes on the fly. Obviously, it
2088 // isn't the same volume if that happens.
2091 Status
= FatPerformDevIoCtrl( IrpContext
,
2092 IOCTL_DISK_GET_DRIVE_GEOMETRY
,
2093 Vcb
->TargetDeviceObject
,
2097 sizeof( DISK_GEOMETRY
),
2102 if (!NT_SUCCESS( Status
)) {
2105 // If we will allow a raw mount then return WRONG_VOLUME to
2106 // allow the volume to be mounted by raw.
2109 if (AllowRawMount
) {
2111 try_return( Status
= STATUS_WRONG_VOLUME
);
2114 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
2118 // Read in the boot sector
2121 SectorSize
= (ULONG
)Vcb
->Bpb
.BytesPerSector
;
2123 if (SectorSize
!= DiskGeometry
.BytesPerSector
) {
2125 try_return( Status
= STATUS_WRONG_VOLUME
);
2129 BootSector
= FsRtlAllocatePoolWithTag(NonPagedPoolNxCacheAligned
,
2131 BootSector
= FsRtlAllocatePoolWithTag(NonPagedPoolCacheAligned
,
2133 (ULONG
) ROUND_TO_PAGES( SectorSize
),
2134 TAG_VERIFY_BOOTSECTOR
);
2137 // If this verify is on behalf of a DASD open, allow a RAW mount.
2140 if (!FatPerformVerifyDiskRead( IrpContext
,
2147 try_return( Status
= STATUS_WRONG_VOLUME
);
2151 // Call a routine to check the boot sector to see if it is fat.
2152 // If it is not fat then mark the vcb as not mounted tell our
2153 // caller its the wrong volume
2156 if (!FatIsBootSectorFat( BootSector
)) {
2158 DebugTrace(0, Dbg
, "Not a Fat Volume\n", 0);
2160 try_return( Status
= STATUS_WRONG_VOLUME
);
2164 // This is a fat volume, so extract serial number and see if it is
2171 if (IsBpbFat32(&BootSector
->PackedBpb
)) {
2172 CopyUchar4( &SerialNumber
, ((PPACKED_BOOT_SECTOR_EX
)BootSector
)->Id
);
2174 CopyUchar4( &SerialNumber
, BootSector
->Id
);
2177 if (SerialNumber
!= Vpb
->SerialNumber
) {
2179 DebugTrace(0, Dbg
, "Not our serial number\n", 0);
2181 try_return( Status
= STATUS_WRONG_VOLUME
);
2186 // Make sure the Bpbs are not different. We have to zero out our
2187 // stack version of the Bpb since unpacking leaves holes.
2190 RtlZeroMemory( &Bpb
, sizeof(BIOS_PARAMETER_BLOCK
) );
2192 FatUnpackBios( &Bpb
, &BootSector
->PackedBpb
);
2193 if (Bpb
.Sectors
!= 0) { Bpb
.LargeSectors
= 0; }
2195 if ( !RtlEqualMemory( &Bpb
,
2198 sizeof(BIOS_PARAMETER_BLOCK
) :
2199 FIELD_OFFSET(BIOS_PARAMETER_BLOCK
,
2200 LargeSectorsPerFat
) )) {
2202 DebugTrace(0, Dbg
, "Bpb is different\n", 0);
2204 try_return( Status
= STATUS_WRONG_VOLUME
);
2208 // Check the volume label. We do this by trying to locate the
2209 // volume label, making two strings one for the saved volume label
2210 // and the other for the new volume label and then we compare the
2214 if (FatRootDirectorySize(&Bpb
) > 0) {
2216 RootDirectorySize
= FatRootDirectorySize(&Bpb
);
2220 RootDirectorySize
= FatBytesPerCluster(&Bpb
);
2224 RootDirectory
= FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned
,
2226 RootDirectory
= FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned
,
2228 (ULONG
) ROUND_TO_PAGES( RootDirectorySize
),
2229 TAG_VERIFY_ROOTDIR
);
2231 if (!IsBpbFat32(&BootSector
->PackedBpb
)) {
2234 // The Fat12/16 case is simple -- read the root directory in and
2238 RootDirectoryLbo
= FatRootDirectoryLbo(&Bpb
);
2240 if (!FatPerformVerifyDiskRead( IrpContext
,
2247 try_return( Status
= STATUS_WRONG_VOLUME
);
2250 Status
= FatSearchBufferForLabel(IrpContext
, Vpb
,
2251 RootDirectory
, RootDirectorySize
,
2254 if (!NT_SUCCESS(Status
)) {
2256 try_return( Status
);
2259 if (!LabelFound
&& Vpb
->VolumeLabelLength
> 0) {
2261 try_return( Status
= STATUS_WRONG_VOLUME
);
2266 ULONG RootDirectoryCluster
;
2268 RootDirectoryCluster
= Bpb
.RootDirFirstCluster
;
2270 while (RootDirectoryCluster
!= FAT_CLUSTER_LAST
) {
2272 RootDirectoryLbo
= FatGetLboFromIndex(Vcb
, RootDirectoryCluster
);
2274 if (!FatPerformVerifyDiskRead( IrpContext
,
2281 try_return( Status
= STATUS_WRONG_VOLUME
);
2284 Status
= FatSearchBufferForLabel(IrpContext
, Vpb
,
2285 RootDirectory
, RootDirectorySize
,
2288 if (!NT_SUCCESS(Status
)) {
2290 try_return( Status
);
2296 // Found a matching label.
2303 // Set ourselves up for the next loop iteration.
2306 FatVerifyLookupFatEntry( IrpContext
, Vcb
,
2307 RootDirectoryCluster
,
2308 &RootDirectoryCluster
);
2310 switch (FatInterpretClusterType(Vcb
, RootDirectoryCluster
)) {
2312 case FatClusterAvailable
:
2313 case FatClusterReserved
:
2317 // Bail all the way out if we have a bad root.
2320 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
2330 if (RootDirectoryCluster
== FAT_CLUSTER_LAST
&&
2331 Vpb
->VolumeLabelLength
> 0) {
2334 // Should have found a label, didn't find any.
2337 try_return( Status
= STATUS_WRONG_VOLUME
);
2345 // Note that we have previously acquired the Vcb to serialize
2346 // the EA file stuff the marking all the Fcbs as NeedToBeVerified.
2348 // Put the Ea file back in a initial state.
2351 FatCloseEaFile( IrpContext
, Vcb
, (BOOLEAN
)(Status
== STATUS_SUCCESS
) );
2354 // Mark all Fcbs as needing verification, but only if we really have
2358 if (!VerifyAlreadyDone
) {
2360 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2361 ReleaseEntireVolume
= TRUE
;
2363 FatMarkFcbCondition( IrpContext
, Vcb
->RootDcb
, FcbNeedsToBeVerified
, TRUE
);
2367 // If the verify didn't succeed, get the volume ready for a
2368 // remount or eventual deletion.
2371 if (Vcb
->VcbCondition
== VcbNotMounted
) {
2374 // If the volume was already in an unmounted state, just bail
2375 // and make sure we return STATUS_WRONG_VOLUME.
2378 Status
= STATUS_WRONG_VOLUME
;
2380 } else if ( Status
== STATUS_WRONG_VOLUME
) {
2383 // Grab everything so we can safely transition the volume state without
2384 // having a thread stumble into the torn-down allocation engine.
2387 if (!ReleaseEntireVolume
) {
2388 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2389 ReleaseEntireVolume
= TRUE
;
2393 // Get rid of any cached data, without flushing
2396 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, NoFlush
);
2399 // Uninitialize the volume file cache map. Note that we cannot
2400 // do a "FatSyncUninit" because of deadlock problems. However,
2401 // since this FileObject is referenced by us, and thus included
2402 // in the Vpb residual count, it is OK to do a normal CcUninit.
2405 CcUninitializeCacheMap( Vcb
->VirtualVolumeFile
,
2409 FatTearDownAllocationSupport( IrpContext
, Vcb
);
2411 FatSetVcbCondition( Vcb
, VcbNotMounted
);
2415 } else if (!VerifyAlreadyDone
) {
2418 // Grab everything so we can safely transition the volume state without
2419 // having a thread stumble into the torn-down allocation engine.
2422 if (!ReleaseEntireVolume
) {
2423 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
2424 ReleaseEntireVolume
= TRUE
;
2428 // Get rid of any cached data, flushing first.
2430 // Future work (and for bonus points, around the other flush points)
2431 // could address the possibility that the dirent filesize hasn't been
2432 // updated yet, causing us to fail the re-verification of a file in
2433 // DetermineAndMark. This is pretty subtle and very very uncommon.
2436 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
2439 // Flush and Purge the volume file.
2442 (VOID
)FatFlushFat( IrpContext
, Vcb
);
2443 CcPurgeCacheSection( &Vcb
->SectionObjectPointers
, NULL
, 0, FALSE
);
2446 // Redo the allocation support with newly paged stuff.
2449 FatTearDownAllocationSupport( IrpContext
, Vcb
);
2450 FatSetupAllocationSupport( IrpContext
, Vcb
);
2452 FatCheckDirtyBit( IrpContext
, Vcb
);
2455 // Check for write protected media.
2458 if (FatIsMediaWriteProtected(IrpContext
, Vcb
->TargetDeviceObject
)) {
2460 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
2464 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
);
2473 // Mark the device as no longer needing verification.
2476 ClearFlag( Vpb
->RealDevice
->Flags
, DO_VERIFY_VOLUME
);
2481 DebugUnwind( FatVerifyVolume
);
2484 // Free any buffer we may have allocated
2487 if ( BootSector
!= NULL
) { ExFreePool( BootSector
); }
2488 if ( RootDirectory
!= NULL
) { ExFreePool( RootDirectory
); }
2491 // Show that we are done with this volume.
2494 NT_ASSERT( Vcb
->VerifyThread
== KeGetCurrentThread() );
2495 Vcb
->VerifyThread
= NULL
;
2497 if (ReleaseEntireVolume
) {
2499 FatReleaseVolume( IrpContext
, Vcb
);
2502 FatReleaseVcb( IrpContext
, Vcb
);
2503 FatReleaseGlobal( IrpContext
);
2506 // If this was not an abnormal termination, complete the irp.
2509 if (!_SEH2_AbnormalTermination()) {
2511 FatCompleteRequest( IrpContext
, Irp
, Status
);
2514 DebugTrace(-1, Dbg
, "FatVerifyVolume -> %08lx\n", Status
);
2522 // Local Support Routine
2526 FatIsBootSectorFat (
2527 IN PPACKED_BOOT_SECTOR BootSector
2532 Routine Description:
2534 This routine checks if the boot sector is for a fat file volume.
2538 BootSector - Supplies the packed boot sector to check
2542 BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
2548 BIOS_PARAMETER_BLOCK Bpb
= {0};
2550 DebugTrace(+1, Dbg
, "FatIsBootSectorFat, BootSector = %p\n", BootSector
);
2553 // The result is true unless we decide that it should be false
2559 // Unpack the bios and then test everything
2562 FatUnpackBios( &Bpb
, &BootSector
->PackedBpb
);
2563 if (Bpb
.Sectors
!= 0) { Bpb
.LargeSectors
= 0; }
2565 if ((BootSector
->Jump
[0] != 0xe9) &&
2566 (BootSector
->Jump
[0] != 0xeb) &&
2567 (BootSector
->Jump
[0] != 0x49)) {
2572 // Enforce some sanity on the sector size (easy check)
2575 } else if ((Bpb
.BytesPerSector
!= 128) &&
2576 (Bpb
.BytesPerSector
!= 256) &&
2577 (Bpb
.BytesPerSector
!= 512) &&
2578 (Bpb
.BytesPerSector
!= 1024) &&
2579 (Bpb
.BytesPerSector
!= 2048) &&
2580 (Bpb
.BytesPerSector
!= 4096)) {
2585 // Likewise on the clustering.
2588 } else if ((Bpb
.SectorsPerCluster
!= 1) &&
2589 (Bpb
.SectorsPerCluster
!= 2) &&
2590 (Bpb
.SectorsPerCluster
!= 4) &&
2591 (Bpb
.SectorsPerCluster
!= 8) &&
2592 (Bpb
.SectorsPerCluster
!= 16) &&
2593 (Bpb
.SectorsPerCluster
!= 32) &&
2594 (Bpb
.SectorsPerCluster
!= 64) &&
2595 (Bpb
.SectorsPerCluster
!= 128)) {
2600 // Likewise on the reserved sectors (must reflect at least the boot sector!)
2603 } else if (Bpb
.ReservedSectors
== 0) {
2608 // No FATs? Wrong ...
2611 } else if (Bpb
.Fats
== 0) {
2616 // Prior to DOS 3.2 might contains value in both of Sectors and
2620 } else if ((Bpb
.Sectors
== 0) && (Bpb
.LargeSectors
== 0)) {
2625 // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and
2626 // is of a version we recognize, currently Version 0.0.
2629 } else if (Bpb
.SectorsPerFat
== 0 && ( Bpb
.LargeSectorsPerFat
== 0 ||
2630 Bpb
.FsVersion
!= 0 )) {
2634 } else if ((Bpb
.Media
!= 0xf0) &&
2635 (Bpb
.Media
!= 0xf8) &&
2636 (Bpb
.Media
!= 0xf9) &&
2637 (Bpb
.Media
!= 0xfb) &&
2638 (Bpb
.Media
!= 0xfc) &&
2639 (Bpb
.Media
!= 0xfd) &&
2640 (Bpb
.Media
!= 0xfe) &&
2641 (Bpb
.Media
!= 0xff) &&
2642 (!FatData
.FujitsuFMR
|| ((Bpb
.Media
!= 0x00) &&
2643 (Bpb
.Media
!= 0x01) &&
2644 (Bpb
.Media
!= 0xfa)))) {
2649 // If this isn't FAT32, then there better be a claimed root directory
2653 } else if (Bpb
.SectorsPerFat
!= 0 && Bpb
.RootEntries
== 0) {
2658 // If this is FAT32 (i.e., extended BPB), look for and refuse to mount
2659 // mirror-disabled volumes. If we did, we would need to only write to
2660 // the FAT# indicated in the ActiveFat field. The only user of this is
2661 // the FAT->FAT32 converter after the first pass of protected mode work
2662 // (booting into realmode) and NT should absolutely not be attempting
2663 // to mount such an in-transition volume.
2666 } else if (Bpb
.SectorsPerFat
== 0 && Bpb
.MirrorDisabled
) {
2671 DebugTrace(-1, Dbg
, "FatIsBootSectorFat -> %08lx\n", Result
);
2678 // Local Support Routine
2682 FatIsMediaWriteProtected (
2683 IN PIRP_CONTEXT IrpContext
,
2684 IN PDEVICE_OBJECT TargetDeviceObject
2689 Routine Description:
2691 This routine determines if the target media is write protected.
2695 TargetDeviceObject - The target of the query
2699 NTSTATUS - The return status for the operation
2707 IO_STATUS_BLOCK Iosb
;
2710 UNREFERENCED_PARAMETER( IrpContext
);
2713 // Query the partition table
2716 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
2719 // See if the media is write protected. On success or any kind
2720 // of error (possibly illegal device function), assume it is
2721 // writeable, and only complain if he tells us he is write protected.
2724 Irp
= IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE
,
2735 // Just return FALSE in the unlikely event we couldn't allocate an Irp.
2738 if ( Irp
== NULL
) {
2743 SetFlag( IoGetNextIrpStackLocation( Irp
)->Flags
, SL_OVERRIDE_VERIFY_VOLUME
);
2745 Status
= IoCallDriver( TargetDeviceObject
, Irp
);
2747 if ( Status
== STATUS_PENDING
) {
2749 (VOID
) KeWaitForSingleObject( &Event
,
2753 (PLARGE_INTEGER
)NULL
);
2755 Status
= Iosb
.Status
;
2758 return (BOOLEAN
)(Status
== STATUS_MEDIA_WRITE_PROTECTED
);
2763 // Local Support Routine
2766 _Requires_lock_held_(_Global_critical_region_
)
2769 IN PIRP_CONTEXT IrpContext
,
2775 Routine Description:
2777 This is the common routine for implementing the user's requests made
2778 through NtFsControlFile.
2782 Irp - Supplies the Irp being processed
2786 NTSTATUS - The return status for the operation
2792 ULONG FsControlCode
;
2794 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2799 // Save some references to make our life a little easier
2802 FsControlCode
= IrpSp
->Parameters
.FileSystemControl
.FsControlCode
;
2804 DebugTrace(+1, Dbg
,"FatUserFsCtrl...\n", 0);
2805 DebugTrace( 0, Dbg
,"FsControlCode = %08lx\n", FsControlCode
);
2808 // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode
2809 // of the caller was userspace and this is a METHOD_NEITHER, we have the choice
2810 // of realy buffering the request through so we can possibly post, or making the
2811 // request synchronous. Since the former was not done by design, do the latter.
2814 if (Irp
->RequestorMode
!= KernelMode
&& (FsControlCode
& 3) == METHOD_NEITHER
) {
2816 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
2820 // Case on the control code.
2823 switch ( FsControlCode
) {
2825 case FSCTL_REQUEST_OPLOCK_LEVEL_1
:
2826 case FSCTL_REQUEST_OPLOCK_LEVEL_2
:
2827 case FSCTL_REQUEST_BATCH_OPLOCK
:
2828 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
:
2829 case FSCTL_OPBATCH_ACK_CLOSE_PENDING
:
2830 case FSCTL_OPLOCK_BREAK_NOTIFY
:
2831 case FSCTL_OPLOCK_BREAK_ACK_NO_2
:
2832 case FSCTL_REQUEST_FILTER_OPLOCK
:
2833 #if (NTDDI_VERSION >= NTDDI_WIN7)
2834 case FSCTL_REQUEST_OPLOCK
:
2836 Status
= FatOplockRequest( IrpContext
, Irp
);
2839 case FSCTL_LOCK_VOLUME
:
2841 Status
= FatLockVolume( IrpContext
, Irp
);
2844 case FSCTL_UNLOCK_VOLUME
:
2846 Status
= FatUnlockVolume( IrpContext
, Irp
);
2849 case FSCTL_DISMOUNT_VOLUME
:
2851 Status
= FatDismountVolume( IrpContext
, Irp
);
2854 case FSCTL_MARK_VOLUME_DIRTY
:
2856 Status
= FatDirtyVolume( IrpContext
, Irp
);
2859 case FSCTL_IS_VOLUME_DIRTY
:
2861 Status
= FatIsVolumeDirty( IrpContext
, Irp
);
2864 case FSCTL_IS_VOLUME_MOUNTED
:
2866 Status
= FatIsVolumeMounted( IrpContext
, Irp
);
2869 case FSCTL_IS_PATHNAME_VALID
:
2870 Status
= FatIsPathnameValid( IrpContext
, Irp
);
2873 case FSCTL_QUERY_RETRIEVAL_POINTERS
:
2874 Status
= FatQueryRetrievalPointers( IrpContext
, Irp
);
2877 case FSCTL_QUERY_FAT_BPB
:
2878 Status
= FatQueryBpb( IrpContext
, Irp
);
2881 case FSCTL_FILESYSTEM_GET_STATISTICS
:
2882 Status
= FatGetStatistics( IrpContext
, Irp
);
2885 #if (NTDDI_VERSION >= NTDDI_WIN7)
2886 case FSCTL_GET_RETRIEVAL_POINTER_BASE
:
2887 Status
= FatGetRetrievalPointerBase( IrpContext
, Irp
);
2890 case FSCTL_GET_BOOT_AREA_INFO
:
2891 Status
= FatGetBootAreaInfo( IrpContext
, Irp
);
2895 case FSCTL_GET_VOLUME_BITMAP
:
2896 Status
= FatGetVolumeBitmap( IrpContext
, Irp
);
2899 case FSCTL_GET_RETRIEVAL_POINTERS
:
2900 Status
= FatGetRetrievalPointers( IrpContext
, Irp
);
2903 case FSCTL_MOVE_FILE
:
2904 Status
= FatMoveFile( IrpContext
, Irp
);
2907 case FSCTL_ALLOW_EXTENDED_DASD_IO
:
2908 Status
= FatAllowExtendedDasdIo( IrpContext
, Irp
);
2911 case FSCTL_MARK_HANDLE
:
2912 Status
= FatMarkHandle( IrpContext
, Irp
);
2915 #if (NTDDI_VERSION >= NTDDI_WIN8)
2917 case FSCTL_SET_PURGE_FAILURE_MODE
:
2918 Status
= FatSetPurgeFailureMode( IrpContext
, Irp
);
2924 #if (NTDDI_VERSION >= NTDDI_WIN7)
2925 case FSCTL_SET_ZERO_ON_DEALLOCATION
:
2926 Status
= FatSetZeroOnDeallocate( IrpContext
, Irp
);
2932 DebugTrace(0, Dbg
, "Invalid control code -> %08lx\n", FsControlCode
);
2934 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
2935 Status
= STATUS_INVALID_DEVICE_REQUEST
;
2939 DebugTrace(-1, Dbg
, "FatUserFsCtrl -> %08lx\n", Status
);
2946 // Local support routine
2949 _Requires_lock_held_(_Global_critical_region_
)
2952 _In_ PIRP_CONTEXT IrpContext
,
2958 Routine Description:
2960 This is the common routine to handle oplock requests made via the
2961 NtFsControlFile call.
2965 Irp - Supplies the Irp being processed
2969 NTSTATUS - The return status for the operation
2974 NTSTATUS Status
= STATUS_SUCCESS
;
2975 ULONG FsControlCode
;
2980 ULONG OplockCount
= 0;
2982 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2984 BOOLEAN AcquiredVcb
= FALSE
;
2985 BOOLEAN AcquiredFcb
= FALSE
;
2987 #if (NTDDI_VERSION >= NTDDI_WIN7)
2988 PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer
= NULL
;
2989 ULONG InputBufferLength
;
2990 ULONG OutputBufferLength
;
2993 TYPE_OF_OPEN TypeOfOpen
;
2998 // Save some references to make our life a little easier
3001 FsControlCode
= IrpSp
->Parameters
.FileSystemControl
.FsControlCode
;
3003 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
3005 DebugTrace(+1, Dbg
, "FatOplockRequest...\n", 0);
3006 DebugTrace( 0, Dbg
, "FsControlCode = %08lx\n", FsControlCode
);
3009 // We permit oplock requests on files and directories.
3012 if ((TypeOfOpen
!= UserFileOpen
)
3013 #if (NTDDI_VERSION >= NTDDI_WIN8)
3015 (TypeOfOpen
!= UserDirectoryOpen
)
3019 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3020 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3021 return STATUS_INVALID_PARAMETER
;
3024 #if (NTDDI_VERSION >= NTDDI_WIN7)
3027 // Get the input & output buffer lengths and pointers.
3030 if (FsControlCode
== FSCTL_REQUEST_OPLOCK
) {
3032 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
3033 InputBuffer
= (PREQUEST_OPLOCK_INPUT_BUFFER
) Irp
->AssociatedIrp
.SystemBuffer
;
3035 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
3038 // Check for a minimum length on the input and ouput buffers.
3041 if ((InputBufferLength
< sizeof( REQUEST_OPLOCK_INPUT_BUFFER
)) ||
3042 (OutputBufferLength
< sizeof( REQUEST_OPLOCK_OUTPUT_BUFFER
))) {
3044 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
3045 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_BUFFER_TOO_SMALL\n", 0);
3046 return STATUS_BUFFER_TOO_SMALL
;
3051 // If the oplock request is on a directory it must be for a Read or Read-Handle
3055 if ((TypeOfOpen
== UserDirectoryOpen
) &&
3056 ((FsControlCode
!= FSCTL_REQUEST_OPLOCK
) ||
3057 !FsRtlOplockIsSharedRequest( Irp
))) {
3059 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3060 DebugTrace(-1, Dbg
, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3061 return STATUS_INVALID_PARAMETER
;
3067 // Make this a waitable Irpcontext so we don't fail to acquire
3071 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3074 // Use a try finally to free the Fcb/Vcb
3080 // We grab the Fcb exclusively for oplock requests, shared for oplock
3081 // break acknowledgement.
3084 if ((FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_1
) ||
3085 (FsControlCode
== FSCTL_REQUEST_BATCH_OPLOCK
) ||
3086 (FsControlCode
== FSCTL_REQUEST_FILTER_OPLOCK
) ||
3087 (FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_2
)
3088 #if (NTDDI_VERSION >= NTDDI_WIN7)
3090 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->Flags
, REQUEST_OPLOCK_INPUT_FLAG_REQUEST
))
3094 FatAcquireSharedVcb( IrpContext
, Fcb
->Vcb
);
3096 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
3099 #if (NTDDI_VERSION >= NTDDI_WIN7)
3100 if (FsRtlOplockIsSharedRequest( Irp
)) {
3102 if (FsControlCode
== FSCTL_REQUEST_OPLOCK_LEVEL_2
) {
3106 // Byte-range locks are only valid on files.
3109 if (TypeOfOpen
== UserFileOpen
) {
3112 // Set OplockCount to nonzero if FsRtl denies access
3113 // based on current byte-range lock state.
3116 #if (NTDDI_VERSION >= NTDDI_WIN8)
3117 OplockCount
= (ULONG
) !FsRtlCheckLockForOplockRequest( &Fcb
->Specific
.Fcb
.FileLock
, &Fcb
->Header
.AllocationSize
);
3118 #elif (NTDDI_VERSION >= NTDDI_WIN7)
3119 OplockCount
= (ULONG
) FsRtlAreThereCurrentOrInProgressFileLocks( &Fcb
->Specific
.Fcb
.FileLock
);
3121 OplockCount
= (ULONG
) FsRtlAreThereCurrentFileLocks( &Fcb
->Specific
.Fcb
.FileLock
);
3128 OplockCount
= Fcb
->UncleanCount
;
3131 } else if ((FsControlCode
== FSCTL_OPLOCK_BREAK_ACKNOWLEDGE
) ||
3132 (FsControlCode
== FSCTL_OPBATCH_ACK_CLOSE_PENDING
) ||
3133 (FsControlCode
== FSCTL_OPLOCK_BREAK_NOTIFY
) ||
3134 (FsControlCode
== FSCTL_OPLOCK_BREAK_ACK_NO_2
)
3135 #if (NTDDI_VERSION >= NTDDI_WIN7)
3137 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->Flags
, REQUEST_OPLOCK_INPUT_FLAG_ACK
))
3141 FatAcquireSharedFcb( IrpContext
, Fcb
);
3143 #if (NTDDI_VERSION >= NTDDI_WIN7)
3144 } else if (FsControlCode
== FSCTL_REQUEST_OPLOCK
) {
3147 // The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or
3148 // REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer.
3151 try_leave( Status
= STATUS_INVALID_PARAMETER
);
3159 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
3161 FatBugCheck( FsControlCode
, 0, 0 );
3165 // Fail batch, filter, and handle oplock requests if the file is marked
3169 if (((FsControlCode
== FSCTL_REQUEST_FILTER_OPLOCK
) ||
3170 (FsControlCode
== FSCTL_REQUEST_BATCH_OPLOCK
)
3171 #if (NTDDI_VERSION >= NTDDI_WIN7)
3173 ((FsControlCode
== FSCTL_REQUEST_OPLOCK
) && FlagOn( InputBuffer
->RequestedOplockLevel
, OPLOCK_LEVEL_CACHE_HANDLE
))
3176 FlagOn( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
)) {
3178 try_leave( Status
= STATUS_DELETE_PENDING
);
3182 // Call the FsRtl routine to grant/acknowledge oplock.
3185 Status
= FsRtlOplockFsctrl( FatGetFcbOplock(Fcb
),
3190 // Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should not complete it.
3196 // Set the flag indicating if Fast I/O is possible
3199 Fcb
->Header
.IsFastIoPossible
= FatIsFastIoPossible( Fcb
);
3203 DebugUnwind( FatOplockRequest
);
3206 // Release all of our resources
3211 FatReleaseVcb( IrpContext
, Fcb
->Vcb
);
3216 FatReleaseFcb( IrpContext
, Fcb
);
3219 DebugTrace(-1, Dbg
, "FatOplockRequest -> %08lx\n", Status
);
3222 FatCompleteRequest( IrpContext
, Irp
, Status
);
3229 // Local Support Routine
3232 _Requires_lock_held_(_Global_critical_region_
)
3235 IN PIRP_CONTEXT IrpContext
,
3241 Routine Description:
3243 This routine performs the lock volume operation. It is responsible for
3244 either completing of enqueuing the input Irp.
3248 Irp - Supplies the Irp to process
3252 NTSTATUS - The return status for the operation
3257 NTSTATUS Status
= STATUS_SUCCESS
;
3259 PIO_STACK_LOCATION IrpSp
;
3267 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3269 DebugTrace(+1, Dbg
, "FatLockVolume...\n", 0);
3272 // Decode the file object, the only type of opens we accept are
3273 // user volume opens.
3276 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3278 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3280 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3281 return STATUS_INVALID_PARAMETER
;
3284 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3286 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3288 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3289 return STATUS_INVALID_PARAMETER
;
3293 // Send our notification so that folks that like to hold handles on
3294 // volumes can get out of the way.
3297 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK
);
3300 // Acquire exclusive access to the Vcb and enqueue the Irp if we
3301 // didn't get access.
3304 if (!FatAcquireExclusiveVcb( IrpContext
, Vcb
)) {
3306 DebugTrace( 0, Dbg
, "Cannot acquire Vcb\n", 0);
3308 Status
= FatFsdPostRequest( IrpContext
, Irp
);
3310 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", Status
);
3316 Status
= FatLockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
3321 // Since we drop and release the vcb while trying to punch the volume
3322 // down, it may be the case that we decide the operation should not
3323 // continue if the user raced a CloeseHandle() with us (and it finished
3324 // the cleanup) while we were waiting for our closes to finish.
3326 // In this case, we will have been raised out of the acquire logic with
3327 // STATUS_FILE_CLOSED, and the volume will not be held.
3330 if (!_SEH2_AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb
->Resource
)) {
3332 FatReleaseVcb( IrpContext
, Vcb
);
3335 if (!NT_SUCCESS( Status
) || _SEH2_AbnormalTermination()) {
3338 // The volume lock will be failing.
3341 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_LOCK_FAILED
);
3345 FatCompleteRequest( IrpContext
, Irp
, Status
);
3347 DebugTrace(-1, Dbg
, "FatLockVolume -> %08lx\n", Status
);
3354 // Local Support Routine
3359 IN PIRP_CONTEXT IrpContext
,
3365 Routine Description:
3367 This routine performs the unlock volume operation. It is responsible for
3368 either completing of enqueuing the input Irp.
3372 Irp - Supplies the Irp to process
3376 NTSTATUS - The return status for the operation
3383 PIO_STACK_LOCATION IrpSp
;
3391 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3393 DebugTrace(+1, Dbg
, "FatUnlockVolume...\n", 0);
3396 // Decode the file object, the only type of opens we accept are
3397 // user volume opens.
3400 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3402 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3404 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3405 return STATUS_INVALID_PARAMETER
;
3408 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3410 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3412 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3413 return STATUS_INVALID_PARAMETER
;
3416 Status
= FatUnlockVolumeInternal( IrpContext
, Vcb
, IrpSp
->FileObject
);
3419 // Send notification that the volume is avaliable.
3422 if (NT_SUCCESS( Status
)) {
3424 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_UNLOCK
);
3427 FatCompleteRequest( IrpContext
, Irp
, Status
);
3429 DebugTrace(-1, Dbg
, "FatUnlockVolume -> %08lx\n", Status
);
3435 _Requires_lock_held_(_Global_critical_region_
)
3437 FatLockVolumeInternal (
3438 IN PIRP_CONTEXT IrpContext
,
3440 IN PFILE_OBJECT FileObject OPTIONAL
3445 Routine Description:
3447 This routine performs the actual lock volume operation. It will be called
3448 by anyone wishing to try to protect the volume for a long duration. PNP
3449 operations are such a user.
3451 The volume must be held exclusive by the caller.
3455 Vcb - The volume being locked.
3457 FileObject - File corresponding to the handle locking the volume. If this
3458 is not specified, a system lock is assumed.
3462 NTSTATUS - The return status for the operation
3467 NTSTATUS Status
= STATUS_SUCCESS
;
3469 ULONG RemainingUserReferences
= (FileObject
? 1: 0);
3471 NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb
->Resource
) &&
3472 !ExIsResourceAcquiredExclusiveLite( &FatData
.Resource
));
3474 // Go synchronous for the rest of the lock operation. It may be
3475 // reasonable to try to revisit this in the future, but for now
3476 // the purge below expects to be able to wait.
3478 // We know it is OK to leave the flag up given how we're used at
3482 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3485 // If there are any open handles, this will fail.
3488 if (!FatIsHandleCountZero( IrpContext
, Vcb
)) {
3490 return STATUS_ACCESS_DENIED
;
3494 // Force Mm to get rid of its referenced file objects.
3497 FatFlushFat( IrpContext
, Vcb
);
3499 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
3501 FatCloseEaFile( IrpContext
, Vcb
, TRUE
);
3504 // Now back out of our synchronization and wait for the lazy writer
3505 // to finish off any lazy closes that could have been outstanding.
3507 // Since we flushed, we know that the lazy writer will issue all
3508 // possible lazy closes in the next tick - if we hadn't, an otherwise
3509 // unopened file with a large amount of dirty data could have hung
3510 // around for a while as the data trickled out to the disk.
3512 // This is even more important now since we send notification to
3513 // alert other folks that this style of check is about to happen so
3514 // that they can close their handles. We don't want to enter a fast
3515 // race with the lazy writer tearing down his references to the file.
3518 FatReleaseVcb( IrpContext
, Vcb
);
3520 Status
= CcWaitForCurrentLazyWriterActivity();
3522 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
3524 if (!NT_SUCCESS( Status
)) {
3530 // The act of closing and purging may have touched pages in various
3531 // parent DCBs. We handle this by purging a second time.
3534 FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, Flush
);
3536 FatReleaseVcb( IrpContext
, Vcb
);
3538 Status
= CcWaitForCurrentLazyWriterActivity();
3540 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
3542 if (!NT_SUCCESS( Status
)) {
3548 // Now rundown the delayed closes one last time. We appear to be able
3549 // to have additional collisions.
3555 // Check if the Vcb is already locked, or if the open file count
3556 // is greater than 1 (which implies that someone else also is
3557 // currently using the volume, or a file on the volume), and that the
3558 // VPB reference count only includes our residual and the handle (as
3561 // We used to only check for the vpb refcount. This is unreliable since
3562 // the vpb refcount is dropped immediately before final close, meaning
3563 // that even though we had a good refcount, the close was inflight and
3564 // subsequent operations could get confused. Especially if the PNP path
3565 // was the lock caller, we delete the VCB with an outstanding opencount!
3568 IoAcquireVpbSpinLock( &SavedIrql
);
3570 if (!FlagOn(Vcb
->Vpb
->Flags
, VPB_LOCKED
) &&
3571 (Vcb
->Vpb
->ReferenceCount
<= 2 + RemainingUserReferences
) &&
3572 (Vcb
->OpenFileCount
== (CLONG
)( FileObject
? 1: 0 ))) {
3574 SetFlag(Vcb
->Vpb
->Flags
, (VPB_LOCKED
| VPB_DIRECT_WRITES_ALLOWED
));
3575 SetFlag(Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
);
3576 Vcb
->FileObjectWithVcbLocked
= FileObject
;
3580 Status
= STATUS_ACCESS_DENIED
;
3583 IoReleaseVpbSpinLock( SavedIrql
);
3586 // If we successully locked the volume, see if it is clean now.
3589 if (NT_SUCCESS( Status
) &&
3590 FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
) &&
3591 !FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
) &&
3592 !CcIsThereDirtyData(Vcb
->Vpb
)) {
3594 FatMarkVolume( IrpContext
, Vcb
, VolumeClean
);
3595 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
);
3598 NT_ASSERT( !NT_SUCCESS(Status
) || (Vcb
->OpenFileCount
== (CLONG
)( FileObject
? 1: 0 )));
3605 FatUnlockVolumeInternal (
3606 IN PIRP_CONTEXT IrpContext
,
3608 IN PFILE_OBJECT FileObject OPTIONAL
3613 Routine Description:
3615 This routine performs the actual unlock volume operation.
3617 The volume must be held exclusive by the caller.
3621 Vcb - The volume being locked.
3623 FileObject - File corresponding to the handle locking the volume. If this
3624 is not specified, a system lock is assumed.
3628 NTSTATUS - The return status for the operation
3630 Attempting to remove a system lock that did not exist is OK.
3636 NTSTATUS Status
= STATUS_NOT_LOCKED
;
3638 UNREFERENCED_PARAMETER( IrpContext
);
3640 IoAcquireVpbSpinLock( &SavedIrql
);
3642 if (FlagOn(Vcb
->Vpb
->Flags
, VPB_LOCKED
) && FileObject
== Vcb
->FileObjectWithVcbLocked
) {
3645 // This one locked it, unlock the volume
3648 ClearFlag( Vcb
->Vpb
->Flags
, (VPB_LOCKED
| VPB_DIRECT_WRITES_ALLOWED
) );
3649 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
);
3650 Vcb
->FileObjectWithVcbLocked
= NULL
;
3652 Status
= STATUS_SUCCESS
;
3655 IoReleaseVpbSpinLock( SavedIrql
);
3662 // Local Support Routine
3665 _Requires_lock_held_(_Global_critical_region_
)
3668 IN PIRP_CONTEXT IrpContext
,
3674 Routine Description:
3676 This routine performs the dismount volume operation. It is responsible for
3677 either completing of enqueuing the input Irp.
3681 Irp - Supplies the Irp to process
3685 NTSTATUS - The return status for the operation
3690 PIO_STACK_LOCATION IrpSp
;
3691 NTSTATUS Status
= STATUS_SUCCESS
;
3692 BOOLEAN VcbHeld
= FALSE
;
3699 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3701 DebugTrace(+1, Dbg
, "FatDismountVolume...\n", 0);
3704 // Decode the file object, the only type of opens we accept are
3705 // user volume opens on media that is not boot/paging and is not
3706 // already dismounted ... (but we need to check that stuff while
3710 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3712 Status
= STATUS_INVALID_PARAMETER
;
3716 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3718 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3720 DebugTrace(-1, Dbg
, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3721 return STATUS_INVALID_PARAMETER
;
3725 // Make some unsynchronized checks to see if this operation is possible.
3726 // We will repeat the appropriate ones inside synchronization, but it is
3727 // good to avoid bogus notifications.
3730 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
)) {
3732 Status
= STATUS_ACCESS_DENIED
;
3736 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
)) {
3738 Status
= STATUS_VOLUME_DISMOUNTED
;
3743 // A bit of historical comment is in order.
3745 // In all versions prior to NT5, we only permitted dismount if the volume had
3746 // previously been locked. Now we must permit a forced dismount, meaning that
3747 // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open
3748 // handles, etc. - to flush and invalidate the volume.
3750 // Previously, dismount assumed that lock had come along earlier and done some
3751 // of the work that we are now going to do - i.e., flush, tear down the eas. All
3752 // we had to do here is flush the device out and kill off as many of the orphan
3753 // fcbs as possible. This now changes.
3755 // In fact, everything is a forced dismount now. This changes one interesting
3756 // aspect, which is that it used to be the case that the handle used to dismount
3757 // could come back, read, and induce a verify/remount. This is just not possible
3758 // now. The point of forced dismount is that very shortly someone will come along
3759 // and be destructive to the possibility of using the media further - format, eject,
3760 // etc. By using this path, callers are expected to tolerate the consequences.
3762 // Note that the volume can still be successfully unlocked by this handle.
3766 // Send notification.
3769 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_DISMOUNT
);
3772 // Force ourselves to wait and grab everything.
3775 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
3778 #pragma prefast( push )
3779 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
3780 #pragma prefast( disable: 28193, "this will always wait" )
3783 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
3786 #pragma prefast( pop )
3792 // Guess what? This can raise if a cleanup on the fileobject we
3793 // got races in ahead of us.
3796 FatAcquireExclusiveVolume( IrpContext
, Vcb
);
3799 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
)) {
3801 try_return( Status
= STATUS_VOLUME_DISMOUNTED
);
3804 FatFlushAndCleanVolume( IrpContext
, Irp
, Vcb
, FlushAndInvalidate
);
3807 // We defer the physical dismount until this handle is closed, per symmetric
3808 // implemntation in the other FS. This permits a dismounter to issue IOCTL
3809 // through this handle and perform device manipulation without racing with
3810 // creates attempting to mount the volume again.
3812 // Raise a flag to tell the cleanup path to complete the dismount.
3815 SetFlag( Ccb
->Flags
, CCB_FLAG_COMPLETE_DISMOUNT
);
3818 // Indicate that the volume was dismounted so that we may return the
3819 // correct error code when operations are attempted via open handles.
3822 FatSetVcbCondition( Vcb
, VcbBad
);
3824 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DISMOUNTED
);
3827 // Set a flag in the VPB to let others know that direct volume access is allowed.
3830 IoAcquireVpbSpinLock( &SavedIrql
);
3831 SetFlag( Vcb
->Vpb
->Flags
, VPB_DIRECT_WRITES_ALLOWED
);
3832 IoReleaseVpbSpinLock( SavedIrql
);
3834 Status
= STATUS_SUCCESS
;
3840 #if (NTDDI_VERSION >= NTDDI_WIN8)
3842 FsRtlDismountComplete( Vcb
->TargetDeviceObject
, Status
);
3848 FatReleaseVolume( IrpContext
, Vcb
);
3851 FatReleaseGlobal( IrpContext
);
3854 // I do not believe it is possible to raise, but for completeness
3855 // notice and send notification of failure. We absolutely
3856 // cannot have raised in CheckForDismount.
3858 // We decline to call an attempt to dismount a dismounted volume
3859 // a failure to do so.
3862 if ((!NT_SUCCESS( Status
) && Status
!= STATUS_VOLUME_DISMOUNTED
)
3863 || _SEH2_AbnormalTermination()) {
3865 FsRtlNotifyVolumeEvent( IrpSp
->FileObject
, FSRTL_VOLUME_DISMOUNT_FAILED
);
3871 FatCompleteRequest( IrpContext
, Irp
, Status
);
3872 DebugTrace(-1, Dbg
, "FatDismountVolume -> %08lx\n", Status
);
3878 // Local Support Routine
3881 _Requires_lock_held_(_Global_critical_region_
)
3884 IN PIRP_CONTEXT IrpContext
,
3890 Routine Description:
3892 This routine marks the volume as dirty.
3896 Irp - Supplies the Irp to process
3900 NTSTATUS - The return status for the operation
3905 PIO_STACK_LOCATION IrpSp
;
3913 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3915 DebugTrace(+1, Dbg
, "FatDirtyVolume...\n", 0);
3918 // Decode the file object, the only type of opens we accept are
3919 // user volume opens.
3922 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
3924 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3926 DebugTrace(-1, Dbg
, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3927 return STATUS_INVALID_PARAMETER
;
3930 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
3932 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
3934 DebugTrace(-1, Dbg
, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER
);
3935 return STATUS_INVALID_PARAMETER
;
3940 // Disable popups, we will just return any error.
3943 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
3946 // Verify the Vcb. We want to make sure we don't dirty some
3947 // random chunk of media that happens to be in the drive now.
3950 FatVerifyVcb( IrpContext
, Vcb
);
3952 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
);
3954 FatMarkVolume( IrpContext
, Vcb
, VolumeDirty
);
3956 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
3958 DebugTrace(-1, Dbg
, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
3960 return STATUS_SUCCESS
;
3965 // Local Support Routine
3970 IN PIRP_CONTEXT IrpContext
,
3976 Routine Description:
3978 This routine determines if a volume is currently dirty.
3982 Irp - Supplies the Irp to process
3986 NTSTATUS - The return status for the operation
3991 PIO_STACK_LOCATION IrpSp
;
3993 TYPE_OF_OPEN TypeOfOpen
;
4003 // Get the current stack location and extract the output
4004 // buffer information.
4007 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4010 // Get a pointer to the output buffer. Look at the system buffer field in the
4011 // irp first. Then the Irp Mdl.
4014 if (Irp
->AssociatedIrp
.SystemBuffer
!= NULL
) {
4016 VolumeState
= Irp
->AssociatedIrp
.SystemBuffer
;
4018 } else if (Irp
->MdlAddress
!= NULL
) {
4021 VolumeState
= MmGetSystemAddressForMdlSafe( Irp
->MdlAddress
, LowPagePriority
| MdlMappingNoExecute
);
4023 VolumeState
= MmGetSystemAddressForMdlSafe( Irp
->MdlAddress
, LowPagePriority
);
4026 if (VolumeState
== NULL
) {
4028 FatCompleteRequest( IrpContext
, Irp
, STATUS_INSUFFICIENT_RESOURCES
);
4029 return STATUS_INSUFFICIENT_RESOURCES
;
4034 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_USER_BUFFER
);
4035 return STATUS_INVALID_USER_BUFFER
;
4039 // Make sure the output buffer is large enough and then initialize
4040 // the answer to be that the volume isn't dirty.
4043 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< sizeof(ULONG
)) {
4045 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4046 return STATUS_INVALID_PARAMETER
;
4052 // Decode the file object
4055 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4057 if (TypeOfOpen
!= UserVolumeOpen
) {
4059 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4060 return STATUS_INVALID_PARAMETER
;
4063 if (Vcb
->VcbCondition
!= VcbGood
) {
4065 FatCompleteRequest( IrpContext
, Irp
, STATUS_VOLUME_DISMOUNTED
);
4066 return STATUS_VOLUME_DISMOUNTED
;
4070 // Disable PopUps, we want to return any error.
4073 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
4076 // Verify the Vcb. We want to make double sure that this volume
4077 // is around so that we know our information is good.
4080 FatVerifyVcb( IrpContext
, Vcb
);
4083 // Now set the returned information. We can avoid probing the disk since
4084 // we know our internal state is in sync.
4087 if ( FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
) ) {
4089 SetFlag( *VolumeState
, VOLUME_IS_DIRTY
);
4092 Irp
->IoStatus
.Information
= sizeof( ULONG
);
4094 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4095 return STATUS_SUCCESS
;
4100 // Local Support Routine
4104 FatIsVolumeMounted (
4105 IN PIRP_CONTEXT IrpContext
,
4111 Routine Description:
4113 This routine determines if a volume is currently mounted.
4117 Irp - Supplies the Irp to process
4121 NTSTATUS - The return status for the operation
4128 PIO_STACK_LOCATION IrpSp
;
4136 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4138 Status
= STATUS_SUCCESS
;
4140 DebugTrace(+1, Dbg
, "FatIsVolumeMounted...\n", 0);
4143 // Decode the file object.
4146 (VOID
)FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4148 NT_ASSERT( Vcb
!= NULL
);
4149 _Analysis_assume_( Vcb
!= NULL
);
4152 // Disable PopUps, we want to return any error.
4155 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_POPUPS
);
4161 FatVerifyVcb( IrpContext
, Vcb
);
4163 FatCompleteRequest( IrpContext
, Irp
, Status
);
4165 DebugTrace(-1, Dbg
, "FatIsVolumeMounted -> %08lx\n", Status
);
4172 // Local Support Routine
4176 FatIsPathnameValid (
4177 IN PIRP_CONTEXT IrpContext
,
4183 Routine Description:
4185 This routine determines if a pathname is a-priori illegal by inspecting
4186 the the characters used. It is required to be correct on a FALSE return.
4188 N.B.: current implementation is intentioanlly a no-op. This may change
4189 in the future. A careful reader of the previous implementation of this
4190 FSCTL in FAT would discover that it violated the requirement stated above
4191 and could return FALSE for a valid (createable) pathname.
4195 Irp - Supplies the Irp to process
4199 NTSTATUS - The return status for the operation
4206 DebugTrace(+1, Dbg
, "FatIsPathnameValid...\n", 0);
4208 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4210 DebugTrace(-1, Dbg
, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS
);
4212 return STATUS_SUCCESS
;
4217 // Local Support Routine
4222 IN PIRP_CONTEXT IrpContext
,
4228 Routine Description:
4230 This routine simply returns the first 0x24 bytes of sector 0.
4234 Irp - Supplies the Irp to process
4238 NTSTATUS - The return status for the operation
4243 PIO_STACK_LOCATION IrpSp
;
4247 PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer
;
4251 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4253 DebugTrace(+1, Dbg
, "FatQueryBpb...\n", 0);
4256 // Get the Vcb. If we didn't keep the information needed for this call,
4257 // we had a reason ...
4260 Vcb
= &((PVOLUME_DEVICE_OBJECT
)IrpSp
->DeviceObject
)->Vcb
;
4262 if (Vcb
->First0x24BytesOfBootSector
== NULL
) {
4264 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
4265 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST
);
4266 return STATUS_INVALID_DEVICE_REQUEST
;
4270 // Extract the buffer
4273 BpbBuffer
= (PFSCTL_QUERY_FAT_BPB_BUFFER
)Irp
->AssociatedIrp
.SystemBuffer
;
4276 // Make sure the buffer is big enough.
4279 if (IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
< 0x24) {
4281 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
4282 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL
);
4283 return STATUS_BUFFER_TOO_SMALL
;
4287 // Fill in the output buffer
4290 RtlCopyMemory( BpbBuffer
->First0x24BytesOfBootSector
,
4291 Vcb
->First0x24BytesOfBootSector
,
4294 Irp
->IoStatus
.Information
= 0x24;
4296 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
4297 DebugTrace(-1, Dbg
, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS
);
4298 return STATUS_SUCCESS
;
4303 // Local Support Routine
4306 _Requires_lock_held_(_Global_critical_region_
)
4308 FatInvalidateVolumes (
4314 Routine Description:
4316 This routine searches for all the volumes mounted on the same real device
4317 of the current DASD handle, and marks them all bad. The only operation
4318 that can be done on such handles is cleanup and close.
4322 Irp - Supplies the Irp to process
4326 NTSTATUS - The return status for the operation
4332 IRP_CONTEXT IrpContext
;
4333 PIO_STACK_LOCATION IrpSp
;
4335 LUID TcbPrivilege
= {SE_TCB_PRIVILEGE
, 0};
4341 PFILE_OBJECT FileToMarkBad
;
4342 PDEVICE_OBJECT DeviceToMarkBad
;
4344 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4346 DebugTrace(+1, Dbg
, "FatInvalidateVolumes...\n", 0);
4349 // Check for the correct security access.
4350 // The caller must have the SeTcbPrivilege.
4353 if (!SeSinglePrivilegeCheck(TcbPrivilege
, Irp
->RequestorMode
)) {
4355 FatCompleteRequest( FatNull
, Irp
, STATUS_PRIVILEGE_NOT_HELD
);
4357 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD
);
4358 return STATUS_PRIVILEGE_NOT_HELD
;
4362 // Try to get a pointer to the device object from the handle passed in.
4365 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4366 if (IoIs32bitProcess( Irp
)) {
4368 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(UINT32
)) {
4370 FatCompleteRequest( FatNull
, Irp
, STATUS_INVALID_PARAMETER
);
4372 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER
);
4373 return STATUS_INVALID_PARAMETER
;
4376 Handle
= (HANDLE
) LongToHandle( (*(PUINT32
)Irp
->AssociatedIrp
.SystemBuffer
) );
4379 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
!= sizeof(HANDLE
)) {
4381 FatCompleteRequest( FatNull
, Irp
, STATUS_INVALID_PARAMETER
);
4383 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER
);
4384 return STATUS_INVALID_PARAMETER
;
4387 Handle
= *(PHANDLE
)Irp
->AssociatedIrp
.SystemBuffer
;
4388 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4393 Status
= ObReferenceObjectByHandle( Handle
,
4400 (PVOID
*)&FileToMarkBad
,
4404 if (!NT_SUCCESS(Status
)) {
4406 FatCompleteRequest( FatNull
, Irp
, Status
);
4408 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> %08lx\n", Status
);
4414 // We only needed the pointer, not a reference.
4417 ObDereferenceObject( FileToMarkBad
);
4420 // Grab the DeviceObject from the FileObject.
4423 DeviceToMarkBad
= FileToMarkBad
->DeviceObject
;
4426 RtlZeroMemory( &IrpContext
, sizeof(IRP_CONTEXT
) );
4428 SetFlag( IrpContext
.Flags
, IRP_CONTEXT_FLAG_WAIT
);
4429 IrpContext
.MajorFunction
= IrpSp
->MajorFunction
;
4430 IrpContext
.MinorFunction
= IrpSp
->MinorFunction
;
4433 #pragma prefast( push )
4434 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
4435 #pragma prefast( disable: 28193, "this will always wait" )
4438 FatAcquireExclusiveGlobal( &IrpContext
);
4441 #pragma prefast( pop )
4445 // First acquire the FatData resource shared, then walk through all the
4446 // mounted VCBs looking for candidates to mark BAD.
4448 // On volumes we mark bad, check for dismount possibility (which is
4449 // why we have to get the next link early).
4452 Links
= FatData
.VcbQueue
.Flink
;
4454 while (Links
!= &FatData
.VcbQueue
) {
4458 ExistingVcb
= CONTAINING_RECORD(Links
, VCB
, VcbLinks
);
4460 Links
= Links
->Flink
;
4463 // If we get a match, mark the volume Bad, and also check to
4464 // see if the volume should go away.
4467 if (ExistingVcb
->Vpb
->RealDevice
== DeviceToMarkBad
) {
4469 BOOLEAN VcbDeleted
= FALSE
;
4472 // Here we acquire the Vcb exclusive and try to purge
4473 // all the open files. The idea is to have as little as
4474 // possible stale data visible and to hasten the volume
4478 (VOID
)FatAcquireExclusiveVcb( &IrpContext
, ExistingVcb
);
4481 #pragma prefast( push )
4482 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
4485 if (ExistingVcb
->Vpb
== DeviceToMarkBad
->Vpb
) {
4489 IoAcquireVpbSpinLock( &OldIrql
);
4491 if (FlagOn( DeviceToMarkBad
->Vpb
->Flags
, VPB_MOUNTED
)) {
4495 NewVpb
= ExistingVcb
->SwapVpb
;
4496 ExistingVcb
->SwapVpb
= NULL
;
4497 SetFlag( ExistingVcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
4499 RtlZeroMemory( NewVpb
, sizeof( VPB
) );
4500 NewVpb
->Type
= IO_TYPE_VPB
;
4501 NewVpb
->Size
= sizeof( VPB
);
4502 NewVpb
->RealDevice
= DeviceToMarkBad
;
4503 NewVpb
->Flags
= FlagOn( DeviceToMarkBad
->Vpb
->Flags
, VPB_REMOVE_PENDING
);
4505 DeviceToMarkBad
->Vpb
= NewVpb
;
4508 NT_ASSERT( DeviceToMarkBad
->Vpb
->DeviceObject
== NULL
);
4511 #pragma prefast( pop )
4514 IoReleaseVpbSpinLock( OldIrql
);
4517 FatSetVcbCondition( ExistingVcb
, VcbBad
);
4520 // Process the root directory, if it is present.
4523 if (ExistingVcb
->RootDcb
!= NULL
) {
4526 // In order to safely mark all FCBs bad, we must acquire everything.
4529 FatAcquireExclusiveVolume(&IrpContext
, ExistingVcb
);
4530 FatMarkFcbCondition( &IrpContext
, ExistingVcb
->RootDcb
, FcbBad
, TRUE
);
4531 FatReleaseVolume(&IrpContext
, ExistingVcb
);
4534 // Purging the file objects on this volume could result in the memory manager
4535 // dereferencing it's file pointer which could be the last reference and
4536 // trigger object deletion and VCB deletion. Protect against that here by
4537 // temporarily biasing the file count, and later checking for dismount.
4540 ExistingVcb
->OpenFileCount
+= 1;
4542 FatPurgeReferencedFileObjects( &IrpContext
,
4543 ExistingVcb
->RootDcb
,
4546 ExistingVcb
->OpenFileCount
-= 1;
4548 VcbDeleted
= FatCheckForDismount( &IrpContext
, ExistingVcb
, FALSE
);
4552 // Only release the VCB if it did not go away.
4557 FatReleaseVcb( &IrpContext
, ExistingVcb
);
4562 FatReleaseGlobal( &IrpContext
);
4564 FatCompleteRequest( FatNull
, Irp
, STATUS_SUCCESS
);
4566 DebugTrace(-1, Dbg
, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
4568 return STATUS_SUCCESS
;
4573 // Local Support routine
4577 FatPerformVerifyDiskRead (
4578 IN PIRP_CONTEXT IrpContext
,
4582 IN ULONG NumberOfBytesToRead
,
4583 IN BOOLEAN ReturnOnError
4588 Routine Description:
4590 This routine is used to read in a range of bytes from the disk. It
4591 bypasses all of the caching and regular I/O logic, and builds and issues
4592 the requests itself. It does this operation overriding the verify
4593 volume flag in the device object.
4597 Vcb - Supplies the target device object for this operation.
4599 Buffer - Supplies the buffer that will recieve the results of this operation
4601 Lbo - Supplies the byte offset of where to start reading
4603 NumberOfBytesToRead - Supplies the number of bytes to read, this must
4604 be in multiple of bytes units acceptable to the disk driver.
4606 ReturnOnError - Indicates that we should return on an error, instead
4611 BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
4618 LARGE_INTEGER ByteOffset
;
4620 IO_STATUS_BLOCK Iosb
;
4624 DebugTrace(0, Dbg
, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo
);
4627 // Initialize the event we're going to use
4630 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
4633 // Build the irp for the operation and also set the overrride flag
4636 ByteOffset
.QuadPart
= Lbo
;
4638 Irp
= IoBuildSynchronousFsdRequest( IRP_MJ_READ
,
4639 Vcb
->TargetDeviceObject
,
4641 NumberOfBytesToRead
,
4646 if ( Irp
== NULL
) {
4648 FatRaiseStatus( IrpContext
, STATUS_INSUFFICIENT_RESOURCES
);
4651 SetFlag( IoGetNextIrpStackLocation( Irp
)->Flags
, SL_OVERRIDE_VERIFY_VOLUME
);
4654 // Call the device to do the read and wait for it to finish.
4657 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, Irp
);
4659 if (Status
== STATUS_PENDING
) {
4661 (VOID
)KeWaitForSingleObject( &Event
, Executive
, KernelMode
, FALSE
, (PLARGE_INTEGER
)NULL
);
4663 Status
= Iosb
.Status
;
4666 NT_ASSERT( Status
!= STATUS_VERIFY_REQUIRED
);
4669 // Special case this error code because this probably means we used
4670 // the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
4673 if (Status
== STATUS_INVALID_PARAMETER
) {
4679 // If it doesn't succeed then either return or raise the error.
4682 if (!NT_SUCCESS(Status
)) {
4684 if (ReturnOnError
) {
4690 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
4695 // And return to our caller
4703 // Local Support Routine
4706 _Requires_lock_held_(_Global_critical_region_
)
4708 FatQueryRetrievalPointers (
4709 IN PIRP_CONTEXT IrpContext
,
4715 Routine Description:
4717 This routine performs the query retrieval pointers operation.
4718 It returns the retrieval pointers for the specified input
4719 file from the start of the file to the request map size specified
4720 in the input buffer.
4724 Irp - Supplies the Irp to process
4728 NTSTATUS - The return status for the operation
4733 NTSTATUS Status
= STATUS_SUCCESS
;
4735 PIO_STACK_LOCATION IrpSp
;
4741 PLARGE_INTEGER RequestedMapSize
;
4742 PLARGE_INTEGER
*MappingPairs
;
4755 // Get the current stack location
4758 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4761 // Make this a synchronous IRP because we need access to the input buffer and
4762 // this Irp is marked METHOD_NEITHER.
4765 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
4768 // Decode the file object and ensure that it is the paging file
4770 // Only Kernel mode clients may query retrieval pointer information about
4771 // a file. Ensure that this is the case for this caller.
4774 (VOID
)FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
4776 if (Irp
->RequestorMode
!= KernelMode
||
4778 !FlagOn(Fcb
->FcbState
, FCB_STATE_PAGING_FILE
) ) {
4780 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
4781 return STATUS_INVALID_PARAMETER
;
4785 // Extract the input and output buffer information. The input contains
4786 // the requested size of the mappings in terms of VBO. The output
4787 // parameter will receive a pointer to nonpaged pool where the mapping
4788 // pairs are stored.
4791 NT_ASSERT( IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
== sizeof(LARGE_INTEGER
) );
4792 NT_ASSERT( IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
== sizeof(PVOID
) );
4794 RequestedMapSize
= IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
;
4795 MappingPairs
= Irp
->UserBuffer
;
4798 // Acquire exclusive access to the Fcb
4801 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
4806 // Verify the Fcb is still OK
4809 FatVerifyFcb( IrpContext
, Fcb
);
4812 // Check if the mapping the caller requested is too large
4815 if ((*RequestedMapSize
).QuadPart
> Fcb
->Header
.FileSize
.QuadPart
) {
4817 try_leave( Status
= STATUS_INVALID_PARAMETER
);
4821 // Now get the index for the mcb entry that will contain the
4822 // callers request and allocate enough pool to hold the
4823 // output mapping pairs. Mapping should always be present, but handle
4824 // the case where it isn't.
4827 Result
= FatLookupMcbEntry( Fcb
->Vcb
,
4829 RequestedMapSize
->LowPart
- 1,
4837 try_leave( Status
= STATUS_FILE_CORRUPT_ERROR
);
4841 *MappingPairs
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
4843 *MappingPairs
= FsRtlAllocatePoolWithTag( NonPagedPool
,
4845 (Index
+ 2) * (2 * sizeof(LARGE_INTEGER
)),
4846 TAG_OUTPUT_MAPPINGPAIRS
);
4849 // Now copy over the mapping pairs from the mcb
4850 // to the output buffer. We store in [sector count, lbo]
4851 // mapping pairs and end with a zero sector count.
4854 MapSize
= RequestedMapSize
->LowPart
;
4856 for (i
= 0; i
<= Index
; i
+= 1) {
4858 (VOID
)FatGetNextMcbEntry( Fcb
->Vcb
, &Fcb
->Mcb
, i
, (PVBO
)&Vbo
, &Lbo
, &SectorCount
);
4860 if (SectorCount
> MapSize
) {
4861 SectorCount
= MapSize
;
4864 (*MappingPairs
)[ i
*2 + 0 ].QuadPart
= SectorCount
;
4865 (*MappingPairs
)[ i
*2 + 1 ].QuadPart
= Lbo
;
4867 MapSize
-= SectorCount
;
4870 (*MappingPairs
)[ i
*2 + 0 ].QuadPart
= 0;
4872 Status
= STATUS_SUCCESS
;
4876 DebugUnwind( FatQueryRetrievalPointers
);
4879 // Release all of our resources
4882 FatReleaseFcb( IrpContext
, Fcb
);
4885 // If this is an abnormal termination then undo our work, otherwise
4889 if (!_SEH2_AbnormalTermination()) {
4891 FatCompleteRequest( IrpContext
, Irp
, Status
);
4900 // Local Support Routine
4905 IN PIRP_CONTEXT IrpContext
,
4911 Routine Description:
4913 This routine returns the filesystem performance counters from the
4918 Irp - Supplies the Irp to process
4922 NTSTATUS - The return status for the operation
4927 PIO_STACK_LOCATION IrpSp
;
4931 PFILE_SYSTEM_STATISTICS Buffer
;
4938 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4940 DebugTrace(+1, Dbg
, "FatGetStatistics...\n", 0);
4943 // Extract the buffer
4946 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
4949 // Get a pointer to the output buffer.
4952 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
4955 // Make sure the buffer is big enough for at least the common part.
4958 if (BufferLength
< sizeof(FILESYSTEM_STATISTICS
)) {
4960 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
4962 DebugTrace(-1, Dbg
, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL
);
4964 return STATUS_BUFFER_TOO_SMALL
;
4968 // Now see how many bytes we can copy.
4971 StatsSize
= sizeof(FILE_SYSTEM_STATISTICS
) * FatData
.NumberProcessors
;
4973 if (BufferLength
< StatsSize
) {
4975 BytesToCopy
= BufferLength
;
4976 Status
= STATUS_BUFFER_OVERFLOW
;
4980 BytesToCopy
= StatsSize
;
4981 Status
= STATUS_SUCCESS
;
4988 Vcb
= &((PVOLUME_DEVICE_OBJECT
)IrpSp
->DeviceObject
)->Vcb
;
4991 // Fill in the output buffer
4994 RtlCopyMemory( Buffer
, Vcb
->Statistics
, BytesToCopy
);
4996 Irp
->IoStatus
.Information
= BytesToCopy
;
4998 FatCompleteRequest( IrpContext
, Irp
, Status
);
5000 DebugTrace(-1, Dbg
, "FatGetStatistics -> %08lx\n", Status
);
5006 // Local Support Routine
5009 _Requires_lock_held_(_Global_critical_region_
)
5012 IN PIRP_CONTEXT IrpContext
,
5018 Routine Description:
5020 This routine returns the volume allocation bitmap.
5022 Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
5023 through the input buffer.
5024 Output = the VOLUME_BITMAP_BUFFER data structure is returned through
5027 We return as much as the user buffer allows starting the specified input
5028 LCN (trucated to a byte). If there is no input buffer, we start at zero.
5032 Irp - Supplies the Irp being processed.
5036 NTSTATUS - The return status for the operation.
5041 PIO_STACK_LOCATION IrpSp
;
5048 ULONG TotalClusters
;
5049 ULONG DesiredClusters
;
5050 ULONG StartingCluster
;
5051 ULONG EndingCluster
;
5052 ULONG InputBufferLength
;
5053 ULONG OutputBufferLength
;
5054 LARGE_INTEGER StartingLcn
;
5055 PVOLUME_BITMAP_BUFFER OutputBuffer
;
5060 // Get the current Irp stack location and save some references.
5063 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5065 DebugTrace(+1, Dbg
, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
5066 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5069 // Make this a synchronous IRP because we need access to the input buffer and
5070 // this Irp is marked METHOD_NEITHER.
5073 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5076 // Extract and decode the file object and check for type of open.
5079 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
5081 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5082 return STATUS_INVALID_PARAMETER
;
5085 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5087 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5089 DebugTrace(-1, Dbg
, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER
);
5090 return STATUS_INVALID_PARAMETER
;
5093 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5094 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
5096 OutputBuffer
= (PVOLUME_BITMAP_BUFFER
)FatMapUserBuffer( IrpContext
, Irp
);
5099 // Check for a minimum length on the input and output buffers.
5102 if ((InputBufferLength
< sizeof(STARTING_LCN_INPUT_BUFFER
)) ||
5103 (OutputBufferLength
< sizeof(VOLUME_BITMAP_BUFFER
))) {
5105 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5106 return STATUS_BUFFER_TOO_SMALL
;
5110 // Check if a starting cluster was specified.
5113 TotalClusters
= Vcb
->AllocationSupport
.NumberOfClusters
;
5116 // Check for valid buffers
5121 if (Irp
->RequestorMode
!= KernelMode
) {
5123 ProbeForRead( IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5127 ProbeForWrite( OutputBuffer
, OutputBufferLength
, sizeof(UCHAR
) );
5130 StartingLcn
= ((PSTARTING_LCN_INPUT_BUFFER
)IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
)->StartingLcn
;
5132 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5134 Status
= _SEH2_GetExceptionCode();
5136 FatRaiseStatus( IrpContext
,
5137 FsRtlIsNtstatusExpected(Status
) ?
5138 Status
: STATUS_INVALID_USER_BUFFER
);
5141 if (StartingLcn
.HighPart
|| StartingLcn
.LowPart
>= TotalClusters
) {
5143 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5144 return STATUS_INVALID_PARAMETER
;
5148 StartingCluster
= StartingLcn
.LowPart
& ~7;
5151 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
5154 // Only return what will fit in the user buffer.
5157 OutputBufferLength
-= FIELD_OFFSET(VOLUME_BITMAP_BUFFER
, Buffer
);
5158 DesiredClusters
= TotalClusters
- StartingCluster
;
5160 if (OutputBufferLength
< (DesiredClusters
+ 7) / 8) {
5162 BytesToCopy
= OutputBufferLength
;
5163 Status
= STATUS_BUFFER_OVERFLOW
;
5167 BytesToCopy
= (DesiredClusters
+ 7) / 8;
5168 Status
= STATUS_SUCCESS
;
5172 // Use try/finally for cleanup.
5180 // Verify the Vcb is still OK
5183 FatQuickVerifyVcb( IrpContext
, Vcb
);
5186 // Fill in the fixed part of the output buffer
5189 OutputBuffer
->StartingLcn
.QuadPart
= StartingCluster
;
5190 OutputBuffer
->BitmapSize
.QuadPart
= DesiredClusters
;
5192 if (Vcb
->NumberOfWindows
== 1) {
5195 // Just copy the volume bitmap into the user buffer.
5198 NT_ASSERT( Vcb
->FreeClusterBitMap
.Buffer
!= NULL
);
5200 RtlCopyMemory( &OutputBuffer
->Buffer
[0],
5201 (PUCHAR
)Vcb
->FreeClusterBitMap
.Buffer
+ StartingCluster
/8,
5206 // Call out to analyze the FAT. We must bias by two to account for
5207 // the zero base of this API and FAT's physical reality of starting
5208 // the file heap at cluster 2.
5210 // Note that the end index is inclusive - we need to subtract one to
5213 // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster
5214 // of 2 and end cluster of 9, a run of eight clusters.
5217 EndingCluster
= StartingCluster
+ (BytesToCopy
* 8);
5220 // Make sure we do not read past the end of the entries.
5223 if (EndingCluster
> TotalClusters
) {
5225 EndingCluster
= TotalClusters
;
5228 FatExamineFatEntries( IrpContext
,
5230 StartingCluster
+ 2,
5231 EndingCluster
+ 2 - 1,
5234 (PULONG
)&OutputBuffer
->Buffer
[0] );
5237 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5239 Status
= _SEH2_GetExceptionCode();
5241 FatRaiseStatus( IrpContext
,
5242 FsRtlIsNtstatusExpected(Status
) ?
5243 Status
: STATUS_INVALID_USER_BUFFER
);
5248 FatReleaseVcb( IrpContext
, Vcb
);
5251 Irp
->IoStatus
.Information
= FIELD_OFFSET(VOLUME_BITMAP_BUFFER
, Buffer
) +
5254 FatCompleteRequest( IrpContext
, Irp
, Status
);
5256 DebugTrace(-1, Dbg
, "FatGetVolumeBitmap -> VOID\n", 0);
5263 // Local Support Routine
5266 _Requires_lock_held_(_Global_critical_region_
)
5268 FatGetRetrievalPointers (
5269 IN PIRP_CONTEXT IrpContext
,
5275 Routine Description:
5277 This routine scans the MCB and builds an extent list. The first run in
5278 the output extent list will start at the begining of the contiguous
5279 run specified by the input parameter.
5281 Input = STARTING_VCN_INPUT_BUFFER;
5282 Output = RETRIEVAL_POINTERS_BUFFER.
5286 Irp - Supplies the Irp being processed.
5290 NTSTATUS - The return status for the operation.
5294 NTSTATUS Status
= STATUS_SUCCESS
;
5295 PIO_STACK_LOCATION IrpSp
;
5300 PLARGE_MCB McbToUse
= NULL
;
5301 TYPE_OF_OPEN TypeOfOpen
;
5304 ULONG ClusterShift
= 0;
5306 LONGLONG AllocationSize
= 0;
5311 LARGE_INTEGER StartingVcn
;
5313 ULONG InputBufferLength
;
5314 ULONG OutputBufferLength
;
5320 PRETRIEVAL_POINTERS_BUFFER OutputBuffer
;
5325 // Get the current Irp stack location and save some references.
5328 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5330 DebugTrace(+1, Dbg
, "FatGetRetrievalPointers, FsControlCode = %08lx\n",
5331 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5334 // Make this a synchronous IRP because we need access to the input buffer and
5335 // this Irp is marked METHOD_NEITHER.
5338 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5341 // Extract and decode the file object and check for type of open.
5344 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
5346 if ((TypeOfOpen
!= UserFileOpen
) && (TypeOfOpen
!= UserDirectoryOpen
) && (TypeOfOpen
!= UserVolumeOpen
) ) {
5348 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5349 return STATUS_INVALID_PARAMETER
;
5353 // Get the input and output buffer lengths and pointers.
5354 // Initialize some variables.
5357 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5358 OutputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
5360 OutputBuffer
= (PRETRIEVAL_POINTERS_BUFFER
)FatMapUserBuffer( IrpContext
, Irp
);
5363 // Check for a minimum length on the input and ouput buffers.
5366 if ((InputBufferLength
< sizeof(STARTING_VCN_INPUT_BUFFER
)) ||
5367 (OutputBufferLength
< sizeof(RETRIEVAL_POINTERS_BUFFER
))) {
5369 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5370 return STATUS_BUFFER_TOO_SMALL
;
5374 // Acquire the Fcb and enqueue the Irp if we didn't get access. Go for
5375 // shared on read-only media so we can allow prototype XIP to get
5376 // recursive, as well as recognizing this is safe anyway.
5378 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5380 if (FlagOn( FcbOrDcb
->Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
5382 (VOID
)FatAcquireSharedFcb( IrpContext
, FcbOrDcb
);
5386 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
5388 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5390 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5392 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
5394 DebugTrace(-1, Dbg
, "FatMoveFile -> 0x%x\n", STATUS_ACCESS_DENIED
);
5395 return STATUS_ACCESS_DENIED
;
5398 (VOID
)FatAcquireExclusiveVcb(IrpContext
, Vcb
);
5404 // Verify the Fcb is still OK, or if it is a volume handle, the VCB.
5407 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5409 FatVerifyFcb( IrpContext
, FcbOrDcb
);
5412 // If we haven't yet set the correct AllocationSize, do so.
5415 if (FcbOrDcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
5417 FatLookupFileAllocationSize( IrpContext
, FcbOrDcb
);
5420 // If this is a non-root directory, we have a bit more to
5421 // do since it has not gone through FatOpenDirectoryFile().
5424 if (NodeType(FcbOrDcb
) == FAT_NTC_DCB
||
5425 (NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& FatIsFat32(Vcb
))) {
5427 FcbOrDcb
->Header
.FileSize
.LowPart
=
5428 FcbOrDcb
->Header
.AllocationSize
.LowPart
;
5433 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5436 #pragma prefast( suppress:28931, "calculate it anyway, in case someone adds code that uses this in the future" )
5438 ClusterSize
= 1 << ClusterShift
;
5440 AllocationSize
= FcbOrDcb
->Header
.AllocationSize
.LowPart
;
5441 McbToUse
= &FcbOrDcb
->Mcb
;
5443 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5445 FatQuickVerifyVcb( IrpContext
, Vcb
);
5447 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_BAD_BLOCKS_POPULATED
)) {
5450 // If the bad cluster mcb isn't populated, something is wrong. (It should have been
5451 // populated during mount when we scanned the FAT.
5454 FatRaiseStatus(IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
5457 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5458 ClusterSize
= 1 << ClusterShift
;
5460 if (!FatLookupLastMcbEntry(Vcb
, &Vcb
->BadBlockMcb
, &LastVbo
, &LastLbo
, &LastIndex
)) {
5465 // Round the allocation size to a multiple of of the cluster size.
5468 AllocationSize
= (LastVbo
+ ((LONGLONG
)ClusterSize
-1)) & ~((LONGLONG
)ClusterSize
-1);
5471 McbToUse
= &Vcb
->BadBlockMcb
;
5476 // Check if a starting cluster was specified.
5481 if (Irp
->RequestorMode
!= KernelMode
) {
5483 ProbeForRead( IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
,
5487 ProbeForWrite( OutputBuffer
, OutputBufferLength
, sizeof(UCHAR
) );
5490 StartingVcn
= ((PSTARTING_VCN_INPUT_BUFFER
)IrpSp
->Parameters
.FileSystemControl
.Type3InputBuffer
)->StartingVcn
;
5492 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5494 Status
= _SEH2_GetExceptionCode();
5496 FatRaiseStatus( IrpContext
,
5497 FsRtlIsNtstatusExpected(Status
) ?
5498 Status
: STATUS_INVALID_USER_BUFFER
);
5501 if (StartingVcn
.HighPart
||
5502 StartingVcn
.LowPart
>= (AllocationSize
>> ClusterShift
)) {
5504 try_return( Status
= STATUS_END_OF_FILE
);
5509 // If we don't find the run, something is very wrong.
5514 if (!FatLookupMcbEntry( Vcb
, McbToUse
,
5515 StartingVcn
.LowPart
<< ClusterShift
,
5521 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5523 FatBugCheck( (ULONG_PTR
)FcbOrDcb
, (ULONG_PTR
)McbToUse
, StartingVcn
.LowPart
);
5528 // Now go fill in the ouput buffer with run information
5531 RunCount
= FsRtlNumberOfRunsInLargeMcb( McbToUse
);
5533 for (Index
= 0, Run
= StartingRun
; Run
< RunCount
; Index
++, Run
++) {
5540 // Check for an exhausted output buffer.
5543 if ((ULONG
)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
+1]) > OutputBufferLength
) {
5547 // We've run out of space, so we won't be storing as many runs to the
5548 // user's buffer as we had originally planned. We need to return the
5549 // number of runs that we did have room for.
5554 OutputBuffer
->ExtentCount
= Index
;
5556 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5558 Status
= _SEH2_GetExceptionCode();
5560 FatRaiseStatus( IrpContext
,
5561 FsRtlIsNtstatusExpected(Status
) ?
5562 Status
: STATUS_INVALID_USER_BUFFER
);
5565 Irp
->IoStatus
.Information
= FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
]);
5566 try_return( Status
= STATUS_BUFFER_OVERFLOW
);
5570 // Get the extent. If it's not there or malformed, something is very wrong.
5573 if (!FatGetNextMcbEntry(Vcb
, McbToUse
, Run
, (PVBO
)&Vcn
, &Lbo
, &ByteLength
)) {
5576 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5578 FatBugCheck( (ULONG_PTR
)FcbOrDcb
, (ULONG_PTR
)McbToUse
, Run
);
5582 // Fill in the next array element.
5587 OutputBuffer
->Extents
[Index
].NextVcn
.QuadPart
= ((LONGLONG
)Vcn
+ ByteLength
) >> ClusterShift
;
5588 OutputBuffer
->Extents
[Index
].Lcn
.QuadPart
= FatGetIndexFromLbo( Vcb
, Lbo
) - 2;
5591 // If this is the first run, fill in the starting Vcn
5595 OutputBuffer
->ExtentCount
= RunCount
- StartingRun
;
5596 OutputBuffer
->StartingVcn
.QuadPart
= Vcn
>> ClusterShift
;
5599 } _SEH2_EXCEPT( Irp
->RequestorMode
!= KernelMode
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
5601 Status
= _SEH2_GetExceptionCode();
5603 FatRaiseStatus( IrpContext
,
5604 FsRtlIsNtstatusExpected(Status
) ?
5605 Status
: STATUS_INVALID_USER_BUFFER
);
5610 // We successfully retrieved extent info to the end of the allocation.
5613 Irp
->IoStatus
.Information
= FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER
, Extents
[Index
]);
5614 Status
= STATUS_SUCCESS
;
5620 DebugUnwind( FatGetRetrievalPointers
);
5623 // Release resources
5626 if( (TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
) ) {
5628 FatReleaseFcb( IrpContext
, FcbOrDcb
);
5629 } else if ((TypeOfOpen
== UserVolumeOpen
)) {
5631 FatReleaseVcb(IrpContext
, Vcb
);
5635 // If nothing raised then complete the irp.
5638 if (!_SEH2_AbnormalTermination()) {
5640 FatCompleteRequest( IrpContext
, Irp
, Status
);
5643 DebugTrace(-1, Dbg
, "FatGetRetrievalPointers -> VOID\n", 0);
5651 // Local Support Routine
5654 _Requires_lock_held_(_Global_critical_region_
)
5656 FatMoveFileNeedsWriteThrough (
5657 _In_ PIRP_CONTEXT IrpContext
,
5659 _In_ ULONG OldWriteThroughFlags
5664 if (NodeType(FcbOrDcb
) == FAT_NTC_FCB
) {
5667 if (FcbOrDcb
->Header
.ValidDataLength
.QuadPart
== 0) {
5670 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WRITE_THROUGH
);
5671 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
);
5675 IrpContext
->Flags
&= ~(IRP_CONTEXT_FLAG_WRITE_THROUGH
|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
);
5676 IrpContext
->Flags
|= OldWriteThroughFlags
;
5682 _Requires_lock_held_(_Global_critical_region_
)
5685 IN PIRP_CONTEXT IrpContext
,
5691 Routine Description:
5693 Routine moves a file to the requested Starting Lcn from Starting Vcn for the length
5694 of cluster count. These values are passed in through the the input buffer as a
5695 MOVE_DATA structure.
5697 The call must be made with a DASD handle. The file to move is passed in as a
5702 Irp - Supplies the Irp being processed.
5706 NTSTATUS - The return status for the operation.
5712 PIO_STACK_LOCATION IrpSp
;
5714 PFILE_OBJECT FileObject
;
5715 TYPE_OF_OPEN TypeOfOpen
;
5720 ULONG InputBufferLength
;
5721 PMOVE_FILE_DATA InputBuffer
;
5729 ULONG TargetCluster
;
5730 LARGE_INTEGER LargeSourceLbo
;
5731 LARGE_INTEGER LargeTargetLbo
;
5735 ULONG BytesToReallocate
;
5737 ULONG FirstSpliceSourceCluster
;
5738 ULONG FirstSpliceTargetCluster
;
5739 ULONG SecondSpliceSourceCluster
;
5740 ULONG SecondSpliceTargetCluster
;
5742 LARGE_MCB SourceMcb
;
5743 LARGE_MCB TargetMcb
;
5747 PVOID Buffer
= NULL
;
5750 BOOLEAN SourceMcbInitialized
= FALSE
;
5751 BOOLEAN TargetMcbInitialized
= FALSE
;
5753 BOOLEAN FcbAcquired
= FALSE
;
5754 BOOLEAN EventArmed
= FALSE
;
5755 BOOLEAN DiskSpaceAllocated
= FALSE
;
5758 PBCB DirentBcb
= NULL
;
5760 ULONG OldWriteThroughFlags
= (IrpContext
->Flags
& (IRP_CONTEXT_FLAG_WRITE_THROUGH
|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
));
5762 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5763 MOVE_FILE_DATA LocalMoveFileData
;
5764 PMOVE_FILE_DATA32 MoveFileData32
;
5767 ULONG LocalAbnormalTermination
= 0;
5772 // Get the current Irp stack location and save some references.
5775 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
5777 DebugTrace(+1, Dbg
, "FatMoveFile, FsControlCode = %08lx\n",
5778 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
);
5781 // Force WAIT to true. We have a handle in the input buffer which can only
5782 // be referenced within the originating process.
5785 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
5788 // Extract and decode the file object and check for type of open.
5791 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
) != UserVolumeOpen
) {
5793 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5795 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5796 return STATUS_INVALID_PARAMETER
;
5799 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
5801 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5803 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5804 return STATUS_INVALID_PARAMETER
;
5807 InputBufferLength
= IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
;
5808 InputBuffer
= (PMOVE_FILE_DATA
)Irp
->AssociatedIrp
.SystemBuffer
;
5811 // Do a quick check on the input buffer.
5814 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5815 if (IoIs32bitProcess( Irp
)) {
5817 if (InputBuffer
== NULL
|| InputBufferLength
< sizeof(MOVE_FILE_DATA32
)) {
5819 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5820 return STATUS_BUFFER_TOO_SMALL
;
5823 MoveFileData32
= (PMOVE_FILE_DATA32
) InputBuffer
;
5825 LocalMoveFileData
.FileHandle
= (HANDLE
) LongToHandle( MoveFileData32
->FileHandle
);
5826 LocalMoveFileData
.StartingVcn
= MoveFileData32
->StartingVcn
;
5827 LocalMoveFileData
.StartingLcn
= MoveFileData32
->StartingLcn
;
5828 LocalMoveFileData
.ClusterCount
= MoveFileData32
->ClusterCount
;
5830 InputBuffer
= &LocalMoveFileData
;
5834 if (InputBuffer
== NULL
|| InputBufferLength
< sizeof(MOVE_FILE_DATA
)) {
5836 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
5837 return STATUS_BUFFER_TOO_SMALL
;
5839 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5843 MaxClusters
= Vcb
->AllocationSupport
.NumberOfClusters
;
5844 TargetCluster
= InputBuffer
->StartingLcn
.LowPart
+ 2;
5846 if (InputBuffer
->StartingVcn
.HighPart
||
5847 InputBuffer
->StartingLcn
.HighPart
||
5848 (TargetCluster
< 2) ||
5849 (TargetCluster
+ InputBuffer
->ClusterCount
< TargetCluster
) ||
5850 (TargetCluster
+ InputBuffer
->ClusterCount
> MaxClusters
+ 2) ||
5851 (InputBuffer
->StartingVcn
.LowPart
>= MaxClusters
) ||
5852 InputBuffer
->ClusterCount
== 0
5855 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5857 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5858 return STATUS_INVALID_PARAMETER
;
5862 // Try to get a pointer to the file object from the handle passed in.
5865 Status
= ObReferenceObjectByHandle( InputBuffer
->FileHandle
,
5872 (PVOID
*)&FileObject
,
5876 if (!NT_SUCCESS(Status
)) {
5878 FatCompleteRequest( IrpContext
, Irp
, Status
);
5880 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", Status
);
5885 // There are three basic ways this could be an invalid attempt, so
5888 // - check that this file object is opened on the same volume as the
5889 // DASD handle used to call this routine.
5891 // - extract and decode the file object and check for type of open.
5893 // - if this is a directory, verify that it's not the root and that
5894 // we are not trying to move the first cluster. We cannot move the
5895 // first cluster because sub-directories have this cluster number
5896 // in them and there is no safe way to simultaneously update them
5899 // We'll allow movefile on the root dir if its fat32, since the root dir
5900 // is a real chained file there.
5903 if (FileObject
->Vpb
!= Vcb
->Vpb
) {
5905 ObDereferenceObject( FileObject
);
5906 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5908 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5909 return STATUS_INVALID_PARAMETER
;
5912 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
5914 if ((TypeOfOpen
!= UserFileOpen
&&
5915 TypeOfOpen
!= UserDirectoryOpen
) ||
5917 ((TypeOfOpen
== UserDirectoryOpen
) &&
5918 ((NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& !FatIsFat32(Vcb
)) ||
5919 (InputBuffer
->StartingVcn
.QuadPart
== 0)))) {
5921 ObDereferenceObject( FileObject
);
5922 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
5924 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER
);
5925 return STATUS_INVALID_PARAMETER
;
5929 // If the VDL of the file is zero, it has no valid data in it anyway.
5930 // So it should be safe to avoid flushing the FAT entries and let them be
5931 // lazily written out.
5933 // This is done so that bitlocker's cover file doesn't cause
5934 // unnecessary FAT table I/O when it's moved around.
5935 // (See Win8 bug 106505)
5939 // If this is a file, and the VDL is zero, clear write through.
5942 FatMoveFileNeedsWriteThrough(IrpContext
, FcbOrDcb
, OldWriteThroughFlags
);
5946 // Indicate we're getting to parents of this fcb by their child, and that
5947 // this is a sufficient assertion of our ability to by synchronized
5948 // with respect to the parent directory going away.
5950 // The defrag path is an example of one which arrives at an Fcb by
5951 // a means which would be unreasonable to duplicate in the assertion
5952 // code. See FatOpenDirectoryFile.
5955 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_PARENT_BY_CHILD
);
5957 ClusterShift
= Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
5962 // Initialize our state variables and the event.
5965 FileOffset
= InputBuffer
->StartingVcn
.LowPart
<< ClusterShift
;
5967 ByteCount
= InputBuffer
->ClusterCount
<< ClusterShift
;
5969 TargetLbo
= FatGetLboFromIndex( Vcb
, TargetCluster
);
5970 LargeTargetLbo
.QuadPart
= TargetLbo
;
5975 // Do a quick check on parameters here
5978 if (FileOffset
+ ByteCount
< FileOffset
) {
5980 try_return( Status
= STATUS_INVALID_PARAMETER
);
5983 KeInitializeEvent( &StackEvent
, NotificationEvent
, FALSE
);
5986 // Initialize two MCBs we will be using
5989 FsRtlInitializeLargeMcb( &SourceMcb
, PagedPool
);
5990 SourceMcbInitialized
= TRUE
;
5992 FsRtlInitializeLargeMcb( &TargetMcb
, PagedPool
);
5993 TargetMcbInitialized
= TRUE
;
5996 // Ok, now if this is a directory open we need to switch to the internal
5997 // stream fileobject since it is set up for caching. The top-level
5998 // fileobject has no section object pointers in order prevent folks from
6002 if (TypeOfOpen
== UserDirectoryOpen
) {
6004 PFILE_OBJECT DirStreamFileObject
;
6007 // Open the stream fileobject if neccesary. We must acquire the Fcb
6008 // now to synchronize with other operations (such as dismount ripping
6009 // apart the allocator).
6012 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
6015 FatVerifyFcb( IrpContext
, FcbOrDcb
);
6017 FatOpenDirectoryFile( IrpContext
, FcbOrDcb
);
6018 DirStreamFileObject
= FcbOrDcb
->Specific
.Dcb
.DirectoryFile
;
6021 // Transfer our reference to the internal stream and proceed. Note that
6022 // if we dereferenced first, the user could sneak a teardown through since
6023 // we'd have no references.
6026 ObReferenceObject( DirStreamFileObject
);
6027 ObDereferenceObject( FileObject
);
6028 FileObject
= DirStreamFileObject
;
6031 // We've referenced the DirStreamFileObject, so it should be ok to drop
6035 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6036 FcbAcquired
= FALSE
;
6040 // Determine the size of the buffer we will use to move data.
6043 BufferSize
= FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES
;
6045 if (BufferSize
< (ULONG
)(1 << ClusterShift
)) {
6047 BufferSize
= (1 << ClusterShift
);
6054 ULONG TempByteCount
;
6057 // We must throttle our writes.
6060 CcCanIWrite( FileObject
,
6066 // Aqcuire file resource exclusive to freeze FileSize and block
6067 // user non-cached I/O. Verify the integrity of the fcb - the
6068 // media may have changed (or been dismounted) on us.
6071 if (FcbAcquired
== FALSE
) {
6073 (VOID
)FatAcquireExclusiveFcb( IrpContext
, FcbOrDcb
);
6076 FatVerifyFcb( IrpContext
, FcbOrDcb
);
6080 // Check if the handle indicates we're allowed to move the file.
6082 // FCB_STATE_DENY_DEFRAG indicates that someone blocked move file on this FCB.
6083 // CCB_FLAG_DENY_DEFRAG indicates that this handle was the one that blocked move file, and hence
6084 // it still gets to move the file around.
6087 if ((FcbOrDcb
->FcbState
& FCB_STATE_DENY_DEFRAG
) && !(Ccb
->Flags
& CCB_FLAG_DENY_DEFRAG
)) {
6088 DebugTrace(-1, Dbg
, "FatMoveFile -> %08lx\n", STATUS_ACCESS_DENIED
);
6089 try_return( Status
= STATUS_ACCESS_DENIED
);
6093 // Allocate our buffer, if we need to.
6096 if (Buffer
== NULL
) {
6099 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
6101 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPool
,
6104 TAG_DEFRAG_BUFFER
);
6108 // Analyzes the range of file allocation we are moving
6109 // and determines the actual amount of allocation to be
6110 // moved and how much needs to be written. In addition
6111 // it guarantees that the Mcb in the file is large enough
6112 // so that later MCB operations cannot fail.
6115 FatComputeMoveFileParameter( IrpContext
,
6125 // If ByteCount comes back zero, break here.
6128 if (ByteCount
== 0) {
6133 // At this point (before actually doing anything with the disk
6134 // meta data), calculate the FAT splice clusters and build an
6135 // MCB describing the space to be deallocated.
6138 FatComputeMoveFileSplicePoints( IrpContext
,
6143 &FirstSpliceSourceCluster
,
6144 &FirstSpliceTargetCluster
,
6145 &SecondSpliceSourceCluster
,
6146 &SecondSpliceTargetCluster
,
6150 // Now attempt to allocate the new disk storage using the
6151 // Target Lcn as a hint.
6154 TempByteCount
= BytesToReallocate
;
6155 FatAllocateDiskSpace( IrpContext
,
6162 DiskSpaceAllocated
= TRUE
;
6165 // If we didn't get EXACTLY what we wanted, return immediately.
6168 if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb
) != 1) ||
6169 !FatGetNextMcbEntry( Vcb
, &TargetMcb
, 0, &TempVbo
, &TempLbo
, &TempByteCount
) ||
6170 (FatGetIndexFromLbo( Vcb
, TempLbo
) != TargetCluster
) ||
6171 (TempByteCount
!= BytesToReallocate
)) {
6174 // It would be nice if we could be more specific, but such is life.
6176 try_return( Status
= STATUS_INVALID_PARAMETER
);
6181 // We are going to attempt a move, note it.
6184 if (FatMoveFileDebug
) {
6185 DbgPrint("0x%p: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n",
6186 PsGetCurrentThread(),
6187 FileOffset
>> ClusterShift
,
6189 BytesToReallocate
>> ClusterShift
);
6194 // Now attempt to commit the new allocation to disk. If this
6195 // raises, the allocation will be deallocated.
6197 // If the VDL of the file is zero, it has no valid data in it anyway.
6198 // So it should be safe to avoid flushing the FAT entries and let them be
6199 // lazily written out.
6201 // This is done so that bitlocker's cover file doesn't cause
6202 // unnecessary FAT table I/O when it's moved around.
6203 // (See Win8 bug 106505)
6206 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6208 FatFlushFatEntries( IrpContext
,
6211 BytesToReallocate
>> ClusterShift
);
6215 // Aqcuire both resources exclusive now, guaranteeing that NOBODY
6216 // is in either the read or write paths.
6219 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6222 // This is the first part of some tricky synchronization.
6224 // Set the Event pointer in the FCB. Any paging I/O will block on
6225 // this event (if set in FCB) after acquiring the PagingIo resource.
6227 // This is how I keep ALL I/O out of this path without holding the
6228 // PagingIo resource exclusive for an extended time.
6231 FcbOrDcb
->MoveFileEvent
= &StackEvent
;
6234 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6237 // Now write out the data, but only if we have to. We don't have
6238 // to copy any file data if the range being reallocated is wholly
6239 // beyond valid data length.
6246 IO_STATUS_BLOCK Iosb
;
6248 KeInitializeEvent( &IoEvent
,
6252 NT_ASSERT( LargeTargetLbo
.QuadPart
>= Vcb
->AllocationSupport
.FileAreaLbo
);
6255 // Read in the data that is being moved.
6258 IoIrp
= IoBuildSynchronousFsdRequest( IRP_MJ_READ
,
6259 Vcb
->TargetDeviceObject
,
6266 if (IoIrp
== NULL
) {
6268 FatRaiseStatus( IrpContext
,
6269 STATUS_INSUFFICIENT_RESOURCES
);
6272 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, IoIrp
);
6274 if (Status
== STATUS_PENDING
) {
6276 (VOID
)KeWaitForSingleObject( &IoEvent
,
6280 (PLARGE_INTEGER
)NULL
);
6282 Status
= Iosb
.Status
;
6285 if (!NT_SUCCESS( Status
)) {
6287 FatNormalizeAndRaiseStatus( IrpContext
,
6292 // Write the data to its new location.
6295 KeClearEvent( &IoEvent
);
6297 IoIrp
= IoBuildSynchronousFsdRequest( IRP_MJ_WRITE
,
6298 Vcb
->TargetDeviceObject
,
6305 if (IoIrp
== NULL
) {
6307 FatRaiseStatus( IrpContext
,
6308 STATUS_INSUFFICIENT_RESOURCES
);
6312 // Set a flag indicating that we want to write through any
6313 // cache on the controller. This eliminates the need for
6314 // an explicit flush-device after the write.
6317 SetFlag( IoGetNextIrpStackLocation(IoIrp
)->Flags
, SL_WRITE_THROUGH
);
6319 Status
= IoCallDriver( Vcb
->TargetDeviceObject
, IoIrp
);
6321 if (Status
== STATUS_PENDING
) {
6323 (VOID
)KeWaitForSingleObject( &IoEvent
,
6327 (PLARGE_INTEGER
)NULL
);
6329 Status
= Iosb
.Status
;
6332 if (!NT_SUCCESS( Status
)) {
6334 FatNormalizeAndRaiseStatus( IrpContext
,
6340 // Now that the file data has been moved successfully, we'll go
6341 // to fix up the links in the FAT table and perhaps change the
6342 // entry in the parent directory.
6344 // First we'll do the second splice and commit it. At that point,
6345 // while the volume is in an inconsistent state, the file is
6349 FatSetFatEntry( IrpContext
,
6351 SecondSpliceSourceCluster
,
6352 (FAT_ENTRY
)SecondSpliceTargetCluster
);
6354 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6356 FatFlushFatEntries( IrpContext
, Vcb
, SecondSpliceSourceCluster
, 1 );
6360 // Now do the first splice OR update the dirent in the parent
6361 // and flush the respective object. After this flush the file
6362 // now points to the new allocation.
6365 if (FirstSpliceSourceCluster
== 0) {
6367 NT_ASSERT( NodeType(FcbOrDcb
) == FAT_NTC_FCB
);
6370 // We are moving the first cluster of the file, so we need
6371 // to update our parent directory.
6374 FatGetDirentFromFcbOrDcb( IrpContext
,
6380 Dirent
->FirstClusterOfFile
= (USHORT
)FirstSpliceTargetCluster
;
6382 if (FatIsFat32(Vcb
)) {
6384 Dirent
->FirstClusterOfFileHi
=
6385 (USHORT
)(FirstSpliceTargetCluster
>> 16);
6389 FatSetDirtyBcb( IrpContext
, DirentBcb
, Vcb
, TRUE
);
6391 FatUnpinBcb( IrpContext
, DirentBcb
);
6394 FatFlushDirentForFile( IrpContext
, FcbOrDcb
);
6396 FcbOrDcb
->FirstClusterOfFile
= FirstSpliceTargetCluster
;
6400 FatSetFatEntry( IrpContext
,
6402 FirstSpliceSourceCluster
,
6403 (FAT_ENTRY
)FirstSpliceTargetCluster
);
6405 if ((FcbOrDcb
->Header
.ValidDataLength
.QuadPart
!= 0) || (NodeType(FcbOrDcb
) != FAT_NTC_FCB
)) {
6407 FatFlushFatEntries( IrpContext
, Vcb
, FirstSpliceSourceCluster
, 1 );
6412 // This was successfully committed. We no longer want to free
6413 // this allocation on error.
6416 DiskSpaceAllocated
= FALSE
;
6419 // Check if we need to turn off write through for this file.
6422 FatMoveFileNeedsWriteThrough(IrpContext
, FcbOrDcb
, OldWriteThroughFlags
);
6425 // Now we just have to free the orphaned space. We don't have
6426 // to commit this right now as the integrity of the file doesn't
6430 FatDeallocateDiskSpace( IrpContext
, Vcb
, &SourceMcb
, FALSE
);
6432 FatUnpinRepinnedBcbs( IrpContext
);
6434 Status
= FatHijackIrpAndFlushDevice( IrpContext
,
6436 Vcb
->TargetDeviceObject
);
6438 if (!NT_SUCCESS(Status
)) {
6439 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
6443 // Finally we must replace the old MCB extent information with
6444 // the new. If this fails from pool allocation, we fix it in
6445 // the finally clause by resetting the file's Mcb.
6448 FatRemoveMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6450 BytesToReallocate
);
6452 FatAddMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6455 BytesToReallocate
);
6458 // Now this is the second part of the tricky synchronization.
6460 // We drop the paging I/O here and signal the notification
6461 // event which allows all waiters (present or future) to proceed.
6462 // Then we block again on the PagingIo exclusive. When
6463 // we have it, we again know that there can be nobody in the
6464 // read/write path and thus nobody touching the event, so we
6465 // NULL the pointer to it and then drop the PagingIo resource.
6467 // This combined with our synchronization before the write above
6468 // guarantees that while we were moving the allocation, there
6469 // was no other I/O to this file and because we do not hold
6470 // the paging resource across a flush, we are not exposed to
6474 KeSetEvent( &StackEvent
, 0, FALSE
);
6476 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6478 FcbOrDcb
->MoveFileEvent
= NULL
;
6481 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6484 // Release the resources and let anyone else access the file before
6488 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6489 FcbAcquired
= FALSE
;
6492 // Advance the state variables.
6495 TargetCluster
+= BytesToReallocate
>> ClusterShift
;
6497 FileOffset
+= BytesToReallocate
;
6498 TargetLbo
+= BytesToReallocate
;
6499 ByteCount
-= BytesToReallocate
;
6501 LargeTargetLbo
.QuadPart
+= BytesToReallocate
;
6504 // Clear the two Mcbs
6507 FatRemoveMcbEntry( Vcb
, &SourceMcb
, 0, 0xFFFFFFFF );
6508 FatRemoveMcbEntry( Vcb
, &TargetMcb
, 0, 0xFFFFFFFF );
6511 // Make the event blockable again.
6514 KeClearEvent( &StackEvent
);
6517 Status
= STATUS_SUCCESS
;
6523 DebugUnwind( FatMoveFile
);
6525 LocalAbnormalTermination
|= _SEH2_AbnormalTermination();
6527 ClearFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_PARENT_BY_CHILD
);
6530 // Free the data buffer, if it was allocated.
6533 if (Buffer
!= NULL
) {
6535 ExFreePool( Buffer
);
6539 // Use a nested try-finally for cleanup if our unpinrepinned
6540 // encounters write-through errors. This may even be a re-raise.
6546 // If we have some new allocation hanging around, remove it. The
6547 // pages needed to do this are guaranteed to be resident because
6548 // we have already repinned them.
6551 if (DiskSpaceAllocated
) {
6552 FatDeallocateDiskSpace( IrpContext
, Vcb
, &TargetMcb
, FALSE
);
6553 FatUnpinRepinnedBcbs( IrpContext
);
6558 LocalAbnormalTermination
|= _SEH2_AbnormalTermination();
6561 // Check on the directory Bcb
6564 if (DirentBcb
!= NULL
) {
6565 FatUnpinBcb( IrpContext
, DirentBcb
);
6569 // Uninitialize our MCBs
6572 if (SourceMcbInitialized
) {
6573 FsRtlUninitializeLargeMcb( &SourceMcb
);
6576 if (TargetMcbInitialized
) {
6577 FsRtlUninitializeLargeMcb( &TargetMcb
);
6581 // If this is an abnormal termination then presumably something
6582 // bad happened. Set the Allocation size to unknown and clear
6583 // the Mcb, but only if we still own the Fcb.
6585 // It is important to make sure we use a 64bit form of -1. This is
6586 // what will convince the fastIO path that it cannot extend the file
6587 // in the cache until we have picked up the mapping pairs again.
6589 // Also, we have to do this while owning PagingIo or we can tear the
6590 // Mcb down in the midst of the noncached IO path looking up extents
6591 // (after we drop it and let them all in).
6594 if (LocalAbnormalTermination
&& FcbAcquired
) {
6596 if (FcbOrDcb
->FirstClusterOfFile
== 0) {
6598 FcbOrDcb
->Header
.AllocationSize
.QuadPart
= 0;
6602 FcbOrDcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
6605 FatRemoveMcbEntry( Vcb
, &FcbOrDcb
->Mcb
, 0, 0xFFFFFFFF );
6609 // If we broke out of the loop with the Event armed, defuse it
6610 // in the same way we do it after a write.
6614 KeSetEvent( &StackEvent
, 0, FALSE
);
6615 ExAcquireResourceExclusiveLite( FcbOrDcb
->Header
.PagingIoResource
, TRUE
);
6616 FcbOrDcb
->MoveFileEvent
= NULL
;
6617 ExReleaseResourceLite( FcbOrDcb
->Header
.PagingIoResource
);
6621 // Finally release the main file resource.
6626 FatReleaseFcb( IrpContext
, FcbOrDcb
);
6630 // Now dereference the fileobject. If the user was a wacko they could have
6631 // tried to nail us by closing the handle right after they threw this move
6632 // down, so we had to keep the fileobject referenced across the entire
6636 ObDereferenceObject( FileObject
);
6642 // Complete the irp if we terminated normally.
6645 FatCompleteRequest( IrpContext
, Irp
, Status
);
6652 // Local Support Routine
6655 _Requires_lock_held_(_Global_critical_region_
)
6657 FatComputeMoveFileParameter (
6658 IN PIRP_CONTEXT IrpContext
,
6660 IN ULONG BufferSize
,
6661 IN ULONG FileOffset
,
6662 IN OUT PULONG ByteCount
,
6663 OUT PULONG BytesToReallocate
,
6664 OUT PULONG BytesToWrite
,
6665 OUT PLARGE_INTEGER SourceLbo
6670 Routine Description:
6672 This is a helper routine for FatMoveFile that analyses the range of
6673 file allocation we are moving and determines the actual amount
6674 of allocation to be moved and how much needs to be written.
6678 FcbOrDcb - Supplies the file and its various sizes.
6680 BufferSize - Supplies the size of the buffer we are using to store the data
6683 FileOffset - Supplies the beginning Vbo of the reallocation zone.
6685 ByteCount - Supplies the request length to reallocate. This will
6686 be bounded by allocation size on return.
6688 BytesToReallocate - Receives ByteCount bounded by the file allocation size
6691 BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength.
6693 SourceLbo - Receives the logical byte offset of the source data on the volume.
6704 ULONG AllocationSize
;
6705 ULONG ValidDataLength
;
6706 ULONG ClusterAlignedVDL
;
6710 BOOLEAN RunAllocated
;
6711 BOOLEAN RunEndOnMax
;
6716 // If we haven't yet set the correct AllocationSize, do so.
6719 if (FcbOrDcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
6721 FatLookupFileAllocationSize( IrpContext
, FcbOrDcb
);
6724 // If this is a non-root directory, we have a bit more to
6725 // do since it has not gone through FatOpenDirectoryFile().
6728 if (NodeType(FcbOrDcb
) == FAT_NTC_DCB
||
6729 (NodeType(FcbOrDcb
) == FAT_NTC_ROOT_DCB
&& FatIsFat32(FcbOrDcb
->Vcb
))) {
6731 FcbOrDcb
->Header
.FileSize
.LowPart
=
6732 FcbOrDcb
->Header
.AllocationSize
.LowPart
;
6737 // Get the number of bytes left to write and ensure that it does
6738 // not extend beyond allocation size. We return here if FileOffset
6739 // is beyond AllocationSize which can happn on a truncation.
6742 AllocationSize
= FcbOrDcb
->Header
.AllocationSize
.LowPart
;
6743 ValidDataLength
= FcbOrDcb
->Header
.ValidDataLength
.LowPart
;
6745 if (FileOffset
+ *ByteCount
> AllocationSize
) {
6747 if (FileOffset
>= AllocationSize
) {
6749 *BytesToReallocate
= 0;
6755 *ByteCount
= AllocationSize
- FileOffset
;
6759 // If there is more than our max, then reduce the byte count for this
6760 // pass to our maximum. We must also align the file offset to a
6761 // buffer size byte boundary.
6764 if ((FileOffset
& (BufferSize
- 1)) + *ByteCount
> BufferSize
) {
6766 *BytesToReallocate
= BufferSize
- (FileOffset
& (BufferSize
- 1));
6770 *BytesToReallocate
= *ByteCount
;
6774 // Find where this data exists on the volume.
6777 FatLookupFileAllocation( IrpContext
,
6786 NT_ASSERT( RunAllocated
);
6789 // Limit this run to the contiguous length.
6792 if (RunByteCount
< *BytesToReallocate
) {
6794 *BytesToReallocate
= RunByteCount
;
6798 // Set the starting offset of the source.
6801 SourceLbo
->QuadPart
= RunLbo
;
6804 // We may be able to skip some (or all) of the write
6805 // if allocation size is significantly greater than valid data length.
6808 ClusterSize
= 1 << FcbOrDcb
->Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
6810 NT_ASSERT( ClusterSize
<= BufferSize
);
6812 ClusterAlignedVDL
= (ValidDataLength
+ (ClusterSize
- 1)) & ~(ClusterSize
- 1);
6814 if ((NodeType(FcbOrDcb
) == FAT_NTC_FCB
) &&
6815 (FileOffset
+ *BytesToReallocate
> ClusterAlignedVDL
)) {
6817 if (FileOffset
> ClusterAlignedVDL
) {
6823 *BytesToWrite
= ClusterAlignedVDL
- FileOffset
;
6828 *BytesToWrite
= *BytesToReallocate
;
6834 // Local Support Routine
6838 FatComputeMoveFileSplicePoints (
6839 IN PIRP_CONTEXT IrpContext
,
6841 IN ULONG FileOffset
,
6842 IN ULONG TargetCluster
,
6843 IN ULONG BytesToReallocate
,
6844 OUT PULONG FirstSpliceSourceCluster
,
6845 OUT PULONG FirstSpliceTargetCluster
,
6846 OUT PULONG SecondSpliceSourceCluster
,
6847 OUT PULONG SecondSpliceTargetCluster
,
6848 IN OUT PLARGE_MCB SourceMcb
6853 Routine Description:
6855 This is a helper routine for FatMoveFile that analyzes the range of
6856 file allocation we are moving and generates the splice points in the
6861 FcbOrDcb - Supplies the file and thus Mcb.
6863 FileOffset - Supplies the beginning Vbo of the reallocation zone.
6865 TargetCluster - Supplies the beginning cluster of the reallocation target.
6867 BytesToReallocate - Suppies the length of the reallocation zone.
6869 FirstSpliceSourceCluster - Receives the last cluster in previous allocation
6870 or zero if we are reallocating from VBO 0.
6872 FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation)
6874 SecondSpliceSourceCluster - Receives the final target cluster.
6876 SecondSpliceTargetCluster - Receives the first cluster of the remaining
6877 source allocation or FAT_CLUSTER_LAST if the reallocation zone
6878 extends to the end of the file.
6880 SourceMcb - This supplies an MCB that will be filled in with run
6881 information describing the file allocation being replaced. The Mcb
6882 must be initialized by the caller.
6894 ULONG SourceBytesInRun
;
6895 ULONG SourceBytesRemaining
;
6897 ULONG SourceMcbVbo
= 0;
6898 ULONG SourceMcbBytesInRun
= 0;
6905 Vcb
= FcbOrDcb
->Vcb
;
6908 // Get information on the final cluster in the previous allocation and
6909 // prepare to enumerate it in the follow loop.
6912 if (FileOffset
== 0) {
6915 *FirstSpliceSourceCluster
= 0;
6916 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6920 &SourceBytesInRun
);
6924 Result
= FatLookupMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6930 *FirstSpliceSourceCluster
= FatGetIndexFromLbo( Vcb
, SourceLbo
);
6932 if ((Result
) && (SourceBytesInRun
== 1)) {
6935 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6943 SourceVbo
= FileOffset
;
6945 SourceBytesInRun
-= 1;
6950 // Run should always be present, but don't bugcheck in the case where it's not.
6956 FatRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
6960 // At this point the variables:
6962 // - SourceIndex - SourceLbo - SourceBytesInRun -
6964 // all correctly decribe the allocation to be removed. In the loop
6965 // below we will start here and continue enumerating the Mcb runs
6966 // until we are finished with the allocation to be relocated.
6969 *FirstSpliceTargetCluster
= TargetCluster
;
6971 *SecondSpliceSourceCluster
=
6972 *FirstSpliceTargetCluster
+
6973 (BytesToReallocate
>> Vcb
->AllocationSupport
.LogOfBytesPerCluster
) - 1;
6975 for (SourceBytesRemaining
= BytesToReallocate
, SourceMcbVbo
= 0;
6977 SourceBytesRemaining
> 0;
6980 SourceBytesRemaining
-= SourceMcbBytesInRun
,
6981 SourceMcbVbo
+= SourceMcbBytesInRun
) {
6983 if (SourceMcbVbo
!= 0) {
6985 #pragma prefast( suppress:28931, "needed for debug build" )
6987 Result
= FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
6991 &SourceBytesInRun
);
6995 NT_ASSERT( SourceVbo
== SourceMcbVbo
+ FileOffset
);
6997 SourceMcbBytesInRun
=
6998 SourceBytesInRun
< SourceBytesRemaining
?
6999 SourceBytesInRun
: SourceBytesRemaining
;
7001 FatAddMcbEntry( Vcb
, SourceMcb
,
7004 SourceMcbBytesInRun
);
7008 // Now compute the cluster of the target of the second
7009 // splice. If the final run in the above loop was
7010 // more than we needed, then we can just do arithmetic,
7011 // otherwise we have to look up the next run.
7014 if (SourceMcbBytesInRun
< SourceBytesInRun
) {
7016 *SecondSpliceTargetCluster
=
7017 FatGetIndexFromLbo( Vcb
, SourceLbo
+ SourceMcbBytesInRun
);
7021 if (FatGetNextMcbEntry( Vcb
, &FcbOrDcb
->Mcb
,
7025 &SourceBytesInRun
)) {
7027 *SecondSpliceTargetCluster
= FatGetIndexFromLbo( Vcb
, SourceLbo
);
7031 *SecondSpliceTargetCluster
= FAT_CLUSTER_LAST
;
7038 FatAllowExtendedDasdIo(
7039 IN PIRP_CONTEXT IrpContext
,
7044 Routine Description:
7046 This routine marks the CCB to indicate that the handle
7047 may be used to read past the end of the volume file. The
7048 handle must be a dasd handle.
7052 Irp - Supplies the Irp being processed.
7056 NTSTATUS - The return status for the operation.
7060 PIO_STACK_LOCATION IrpSp
;
7068 // Get the current Irp stack location and save some references.
7071 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7074 // Extract and decode the file object and check for type of open.
7077 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7079 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7080 return STATUS_INVALID_PARAMETER
;
7083 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7085 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7087 DebugTrace(-1, Dbg
, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER
);
7088 return STATUS_INVALID_PARAMETER
;
7091 SetFlag( Ccb
->Flags
, CCB_FLAG_ALLOW_EXTENDED_DASD_IO
);
7093 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7094 return STATUS_SUCCESS
;
7097 #if (NTDDI_VERSION >= NTDDI_WIN7)
7099 _Requires_lock_held_(_Global_critical_region_
)
7101 FatGetRetrievalPointerBase (
7102 _In_ PIRP_CONTEXT IrpContext
,
7107 Routine Description:
7109 This routine retrieves the sector offset to the first allocation unit.
7113 IrpContext - Supplies the Irp Context.
7114 Irp - Supplies the Irp being processed.
7118 NTSTATUS - The return status for the operation.
7122 PIO_STACK_LOCATION IrpSp
= NULL
;
7126 ULONG BufferLength
= 0;
7127 PRETRIEVAL_POINTER_BASE RetrievalPointerBase
= NULL
;
7131 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7134 // Force WAIT to true.
7137 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7140 // Extract and decode the file object and check for type of open.
7143 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7145 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7146 return STATUS_INVALID_PARAMETER
;
7150 // Extract the buffer
7153 RetrievalPointerBase
= Irp
->AssociatedIrp
.SystemBuffer
;
7154 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
7157 // Verify the handle has manage volume access.
7160 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7162 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7163 return STATUS_INVALID_PARAMETER
;
7167 // Validate the output buffer is the right size.
7169 // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7172 if (BufferLength
< sizeof(RETRIEVAL_POINTER_BASE
)) {
7174 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7175 return STATUS_BUFFER_TOO_SMALL
;
7179 // Fill out the offset to the file area.
7182 RtlZeroMemory( RetrievalPointerBase
, BufferLength
);
7186 FatAcquireSharedVcb(IrpContext
, Vcb
);
7187 FatQuickVerifyVcb(IrpContext
, Vcb
);
7189 RetrievalPointerBase
->FileAreaOffset
.QuadPart
= Vcb
->AllocationSupport
.FileAreaLbo
>> Vcb
->AllocationSupport
.LogOfBytesPerSector
;
7190 Irp
->IoStatus
.Information
= sizeof( RETRIEVAL_POINTER_BASE
);
7194 FatReleaseVcb(IrpContext
, Vcb
);
7198 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7200 return STATUS_SUCCESS
;
7205 _Requires_lock_held_(_Global_critical_region_
)
7207 FatGetBootAreaInfo (
7208 _In_ PIRP_CONTEXT IrpContext
,
7213 Routine Description:
7215 This routine retrieves information about the boot areas of the filesystem.
7219 IrpContext - Supplies the Irp Context.
7220 Irp - Supplies the Irp being processed.
7224 NTSTATUS - The return status for the operation.
7228 PIO_STACK_LOCATION IrpSp
= NULL
;
7232 ULONG BufferLength
= 0;
7233 PBOOT_AREA_INFO BootAreaInfo
= NULL
;
7237 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7240 // Force WAIT to true.
7243 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7246 // Extract and decode the file object and check for type of open.
7249 if (FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) != UserVolumeOpen
) {
7251 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7252 return STATUS_INVALID_PARAMETER
;
7256 // Extract the buffer
7259 BootAreaInfo
= Irp
->AssociatedIrp
.SystemBuffer
;
7260 BufferLength
= IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
;
7263 // Verify the handle has manage volume access.
7266 if ((Ccb
== NULL
) || !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7268 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7269 return STATUS_INVALID_PARAMETER
;
7273 // Validate the output buffer is the right size.
7275 // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7278 if (BufferLength
< sizeof(BOOT_AREA_INFO
)) {
7280 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7281 return STATUS_BUFFER_TOO_SMALL
;
7285 // Fill out our boot areas.
7288 RtlZeroMemory( BootAreaInfo
, BufferLength
);
7292 FatAcquireSharedVcb(IrpContext
, Vcb
);
7293 FatQuickVerifyVcb(IrpContext
, Vcb
);
7295 if (FatIsFat32( Vcb
)) {
7297 BootAreaInfo
->BootSectorCount
= 2;
7298 BootAreaInfo
->BootSectors
[0].Offset
.QuadPart
= 0;
7299 BootAreaInfo
->BootSectors
[1].Offset
.QuadPart
= 6;
7302 BootAreaInfo
->BootSectorCount
= 1;
7303 BootAreaInfo
->BootSectors
[0].Offset
.QuadPart
= 0;
7306 Irp
->IoStatus
.Information
= sizeof( BOOT_AREA_INFO
);
7310 FatReleaseVcb(IrpContext
, Vcb
);
7313 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
7314 return STATUS_SUCCESS
;
7320 _Requires_lock_held_(_Global_critical_region_
)
7323 _In_ PIRP_CONTEXT IrpContext
,
7328 Routine Description:
7330 This routine is used to attach special properties to a user handle.
7334 IrpContext - Supplies the Irp Context.
7335 Irp - Supplies the Irp being processed.
7339 NTSTATUS - The return status for the operation.
7343 NTSTATUS Status
= STATUS_SUCCESS
;
7344 PIO_STACK_LOCATION IrpSp
= NULL
;
7348 PFCB DasdFcb
= NULL
;
7349 PCCB DasdCcb
= NULL
;
7350 TYPE_OF_OPEN TypeOfOpen
;
7351 PMARK_HANDLE_INFO HandleInfo
= NULL
;
7352 PFILE_OBJECT DasdFileObject
= NULL
;
7353 BOOLEAN ReleaseFcb
= FALSE
;
7355 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7356 MARK_HANDLE_INFO LocalMarkHandleInfo
= {0};
7361 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7364 // Always make this a synchronous IRP.
7367 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7370 // Extract and decode the file object and check for type of open.
7373 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
) ;
7376 // We currently support this call for files and directories only.
7379 if ((TypeOfOpen
!= UserFileOpen
) &&
7380 (TypeOfOpen
!= UserDirectoryOpen
)) {
7382 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7383 return STATUS_INVALID_PARAMETER
;
7386 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7389 // Win32/64 thunking code
7392 if (IoIs32bitProcess( Irp
)) {
7394 PMARK_HANDLE_INFO32 MarkHandle32
;
7396 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( MARK_HANDLE_INFO32
)) {
7398 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7399 return STATUS_BUFFER_TOO_SMALL
;
7402 MarkHandle32
= (PMARK_HANDLE_INFO32
) Irp
->AssociatedIrp
.SystemBuffer
;
7403 LocalMarkHandleInfo
.HandleInfo
= MarkHandle32
->HandleInfo
;
7404 LocalMarkHandleInfo
.UsnSourceInfo
= MarkHandle32
->UsnSourceInfo
;
7405 LocalMarkHandleInfo
.VolumeHandle
= (HANDLE
)(ULONG_PTR
)(LONG
) MarkHandle32
->VolumeHandle
;
7407 HandleInfo
= &LocalMarkHandleInfo
;
7414 // Get the input buffer pointer and check its length.
7417 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( MARK_HANDLE_INFO
)) {
7419 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7420 return STATUS_BUFFER_TOO_SMALL
;
7423 HandleInfo
= (PMARK_HANDLE_INFO
) Irp
->AssociatedIrp
.SystemBuffer
;
7425 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7430 // Check that only legal bits are being set.
7431 // We currently only support two bits: protect clusters and unprotect clusters.
7433 // Note that we don't actually support the USN journal, but we must ignore the flags in order
7434 // to preserve compatibility.
7437 if (FlagOn( HandleInfo
->HandleInfo
,
7438 ~(MARK_HANDLE_PROTECT_CLUSTERS
)) ||
7439 (FlagOn( HandleInfo
->HandleInfo
,
7441 (IrpSp
->MinorFunction
!= IRP_MN_KERNEL_CALL
)) ||
7442 FlagOn(HandleInfo
->UsnSourceInfo
,
7443 ~(USN_SOURCE_DATA_MANAGEMENT
|
7444 USN_SOURCE_AUXILIARY_DATA
|
7445 USN_SOURCE_REPLICATION_MANAGEMENT
) ) ) {
7447 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7448 return STATUS_INVALID_PARAMETER
;
7452 // Check that the user has a valid volume handle or the manage volume
7453 // privilege or is a kernel mode caller
7455 // NOTE: the kernel mode check is only valid because the rdr doesn't support this
7459 if ((Irp
->RequestorMode
!= KernelMode
) &&
7460 (IrpSp
->MinorFunction
!= IRP_MN_KERNEL_CALL
) &&
7461 !FlagOn( Ccb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
) &&
7462 ( FlagOn( HandleInfo
->HandleInfo
, MARK_HANDLE_PROTECT_CLUSTERS
) || (HandleInfo
->UsnSourceInfo
!= 0) )) {
7464 if (HandleInfo
->VolumeHandle
== 0) {
7465 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
7466 return STATUS_ACCESS_DENIED
;
7469 Status
= ObReferenceObjectByHandle( HandleInfo
->VolumeHandle
,
7476 (PVOID
*)&DasdFileObject
,
7480 if (!NT_SUCCESS(Status
)) {
7482 FatCompleteRequest( IrpContext
, Irp
, Status
);
7487 // Check that this file object is opened on the same volume as the
7488 // handle used to call this routine.
7491 if (DasdFileObject
->Vpb
!= Vcb
->Vpb
) {
7493 ObDereferenceObject( DasdFileObject
);
7495 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7496 return STATUS_INVALID_PARAMETER
;
7500 // Now decode this FileObject and verify it is a volume handle.
7501 // We don't care to raise on dismounts here because
7502 // we check for that further down anyway. So send FALSE.
7506 #pragma prefast( suppress:28931, "convenient for debugging" )
7508 TypeOfOpen
= FatDecodeFileObject( DasdFileObject
, &Vcb
, &DasdFcb
, &DasdCcb
) ;
7510 ObDereferenceObject( DasdFileObject
);
7512 if ((DasdCcb
== NULL
) || !FlagOn( DasdCcb
->Flags
, CCB_FLAG_MANAGE_VOLUME_ACCESS
)) {
7514 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
7515 return STATUS_ACCESS_DENIED
;
7522 FatAcquireExclusiveFcb(IrpContext
, Fcb
);
7525 FatVerifyFcb( IrpContext
, Fcb
);
7527 if (HandleInfo
->HandleInfo
& MARK_HANDLE_PROTECT_CLUSTERS
) {
7529 if (Fcb
->FcbState
& FCB_STATE_DENY_DEFRAG
) {
7532 // It's already set, bail out.
7535 try_return( Status
= STATUS_ACCESS_DENIED
);
7538 Ccb
->Flags
|= CCB_FLAG_DENY_DEFRAG
;
7539 Fcb
->FcbState
|= FCB_STATE_DENY_DEFRAG
;
7549 FatReleaseFcb(IrpContext
, Fcb
);
7554 FatCompleteRequest( IrpContext
, Irp
, Status
);
7559 _Requires_lock_held_(_Global_critical_region_
)
7561 FatFlushAndCleanVolume(
7562 IN PIRP_CONTEXT IrpContext
,
7565 IN FAT_FLUSH_TYPE FlushType
7569 Routine Description:
7571 This routine flushes and otherwise preparse a volume to be eligible
7572 for deletion. The dismount and PNP paths share the need for this
7575 The Vcb will always be valid on return from this function. It is the
7576 caller's responsibility to attempt the dismount/deletion, and to setup
7577 allocation support again if the volume will be brought back from the
7582 Irp - Irp for the overlying request
7584 Vcb - the volume being operated on
7586 FlushType - specifies the kind of flushing desired
7590 NTSTATUS - The return status for the operation.
7597 // The volume must be held exclusive.
7600 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext
, Vcb
));
7603 // There is no fail, flush everything. If invalidating, it is important
7604 // that we invalidate as we flush (eventually, w/ paging io held) so that we
7605 // error out the maximum number of late writes.
7608 if (FlushType
!= NoFlush
) {
7610 (VOID
) FatFlushVolume( IrpContext
, Vcb
, FlushType
);
7613 FatCloseEaFile( IrpContext
, Vcb
, FALSE
);
7616 // Now, tell the device to flush its buffers.
7619 if (FlushType
!= NoFlush
) {
7621 (VOID
)FatHijackIrpAndFlushDevice( IrpContext
, Irp
, Vcb
->TargetDeviceObject
);
7625 // Now purge everything in sight. We're trying to provoke as many closes as
7626 // soon as possible, this volume may be on its way out.
7629 if (FlushType
!= FlushWithoutPurge
) {
7631 CcPurgeCacheSection( &Vcb
->SectionObjectPointers
,
7636 (VOID
) FatPurgeReferencedFileObjects( IrpContext
, Vcb
->RootDcb
, NoFlush
);
7640 // If the volume was dirty and we were allowed to flush, do the processing that
7641 // the delayed callback would have done.
7644 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
)) {
7647 // Cancel any pending clean volumes.
7650 (VOID
)KeCancelTimer( &Vcb
->CleanVolumeTimer
);
7651 (VOID
)KeRemoveQueueDpc( &Vcb
->CleanVolumeDpc
);
7654 if (FlushType
!= NoFlush
) {
7657 // The volume is now clean, note it.
7660 if (!FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
)) {
7662 FatMarkVolume( IrpContext
, Vcb
, VolumeClean
);
7663 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
);
7667 // Unlock the volume if it is removable.
7670 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
) &&
7671 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE
)) {
7673 FatToggleMediaEjectDisable( IrpContext
, Vcb
, FALSE
);
7680 #if (NTDDI_VERSION >= NTDDI_WIN8)
7683 _Requires_lock_held_(_Global_critical_region_
)
7685 FatSetPurgeFailureMode (
7686 _In_ PIRP_CONTEXT IrpContext
,
7691 This routine is used to enable or disable the purge failure mode
7692 on a file. When in this mode the file system will propagate purge
7693 failures encountered during coherency purges. Normally these are
7694 ignored for application compatibilty purposes. Since the normal
7695 behavior can lead to cache incoherency there needs to be a way to
7696 force error propagation, particulary when a filter has mapped a
7697 section for the purposes of scanning the file in the background.
7699 The purge failure mode is a reference count because it is set
7700 per mapped section and there may be multiple sections backed by
7705 IrpContext - Supplies the Irp Context.
7706 Irp - Supplies the Irp being processed.
7710 NTSTATUS - The return status for the operation.
7714 NTSTATUS Status
= STATUS_SUCCESS
;
7715 PIO_STACK_LOCATION IrpSp
;
7716 TYPE_OF_OPEN TypeOfOpen
;
7720 PSET_PURGE_FAILURE_MODE_INPUT SetPurgeInput
;
7721 BOOLEAN FcbAcquired
= FALSE
;
7725 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
7728 // Force WAIT to true.
7731 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
7734 // This has to be a kernel only call. Can't let a user request
7735 // change the purge failure mode count
7738 if (Irp
->RequestorMode
!= KernelMode
) {
7740 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7741 return STATUS_INVALID_PARAMETER
;
7745 // Extract and decode the file object and check for type of open.
7748 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
7750 if (TypeOfOpen
== UserDirectoryOpen
) {
7752 FatCompleteRequest( IrpContext
, Irp
, STATUS_FILE_IS_A_DIRECTORY
);
7753 return STATUS_FILE_IS_A_DIRECTORY
;
7756 if (TypeOfOpen
!= UserFileOpen
) {
7758 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7759 return STATUS_INVALID_PARAMETER
;
7763 // Get the input buffer pointer and check its length.
7766 if (IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
< sizeof( SET_PURGE_FAILURE_MODE_INPUT
)) {
7768 FatCompleteRequest( IrpContext
, Irp
, STATUS_BUFFER_TOO_SMALL
);
7769 return STATUS_BUFFER_TOO_SMALL
;
7772 SetPurgeInput
= (PSET_PURGE_FAILURE_MODE_INPUT
) Irp
->AssociatedIrp
.SystemBuffer
;
7774 if (!FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_ENABLED
| SET_PURGE_FAILURE_MODE_DISABLED
)) {
7776 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
7777 return STATUS_INVALID_PARAMETER
;
7783 // Acquire the FCB exclusively to synchronize with coherency flush
7787 FatAcquireExclusiveFcb( IrpContext
, Fcb
);
7790 FatVerifyFcb( IrpContext
, Fcb
);
7792 if (FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_ENABLED
)) {
7794 if (Fcb
->PurgeFailureModeEnableCount
== MAXULONG
) {
7796 try_return( Status
= STATUS_INVALID_PARAMETER
);
7799 Fcb
->PurgeFailureModeEnableCount
+= 1;
7803 ASSERT( FlagOn( SetPurgeInput
->Flags
, SET_PURGE_FAILURE_MODE_DISABLED
));
7805 if (Fcb
->PurgeFailureModeEnableCount
== 0) {
7807 try_return( Status
= STATUS_INVALID_PARAMETER
);
7810 Fcb
->PurgeFailureModeEnableCount
-= 1;
7818 FatReleaseFcb( IrpContext
, Fcb
);
7823 // Complete the irp if we terminated normally.
7826 FatCompleteRequest( IrpContext
, Irp
, Status
);
7835 FatSearchBufferForLabel(
7836 IN PIRP_CONTEXT IrpContext
,
7840 OUT PBOOLEAN LabelFound
7844 Routine Description:
7846 Search a buffer (taken from the root directory) for a volume label
7847 matching the label in the
7851 IrpContext - Supplies our irp context
7852 Vpb - Vpb supplying the volume label
7853 Buffer - Supplies the buffer we'll search
7854 Size - The size of the buffer in bytes.
7855 LabelFound - Returns whether a label was found.
7859 There are four interesting cases:
7861 1) Some random error occurred - that error returned as status, LabelFound
7864 2) No label was found - STATUS_SUCCESS returned, LabelFound is FALSE.
7866 3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE.
7868 4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound
7875 WCHAR UnicodeBuffer
[11];
7878 PDIRENT TerminationDirent
;
7879 ULONG VolumeLabelLength
;
7880 OEM_STRING OemString
;
7881 UNICODE_STRING UnicodeString
;
7885 UNREFERENCED_PARAMETER( IrpContext
);
7889 TerminationDirent
= Dirent
+ Size
/ sizeof(DIRENT
);
7891 while ( Dirent
< TerminationDirent
) {
7893 if ( Dirent
->FileName
[0] == FAT_DIRENT_NEVER_USED
) {
7895 Dirent
= TerminationDirent
;
7900 // If the entry is the non-deleted volume label break from the loop.
7902 // Note that all out parameters are already correctly set.
7905 if (((Dirent
->Attributes
& ~FAT_DIRENT_ATTR_ARCHIVE
) ==
7906 FAT_DIRENT_ATTR_VOLUME_ID
) &&
7907 (Dirent
->FileName
[0] != FAT_DIRENT_DELETED
)) {
7915 if (Dirent
>= TerminationDirent
) {
7918 // We've run out of buffer.
7921 *LabelFound
= FALSE
;
7922 return STATUS_SUCCESS
;
7927 // Compute the length of the volume name
7930 OemString
.Buffer
= (PCHAR
)&Dirent
->FileName
[0];
7931 OemString
.MaximumLength
= 11;
7933 for ( OemString
.Length
= 11;
7934 OemString
.Length
> 0;
7935 OemString
.Length
-= 1) {
7937 if ( (Dirent
->FileName
[OemString
.Length
-1] != 0x00) &&
7938 (Dirent
->FileName
[OemString
.Length
-1] != 0x20) ) { break; }
7941 UnicodeString
.MaximumLength
= sizeof( UnicodeBuffer
);
7942 UnicodeString
.Buffer
= &UnicodeBuffer
[0];
7944 Status
= RtlOemStringToCountedUnicodeString( &UnicodeString
,
7948 if ( !NT_SUCCESS( Status
) ) {
7953 VolumeLabelLength
= UnicodeString
.Length
;
7955 if ( (VolumeLabelLength
!= (ULONG
)Vpb
->VolumeLabelLength
) ||
7956 (!RtlEqualMemory(&UnicodeBuffer
[0],
7957 &Vpb
->VolumeLabel
[0],
7958 VolumeLabelLength
)) ) {
7960 return STATUS_WRONG_VOLUME
;
7964 // We found a matching label.
7968 return STATUS_SUCCESS
;
7973 FatVerifyLookupFatEntry (
7974 IN PIRP_CONTEXT IrpContext
,
7977 IN OUT PULONG FatEntry
7980 ULONG PageEntryOffset
;
7981 ULONG OffsetIntoVolumeFile
;
7986 NT_ASSERT(Vcb
->AllocationSupport
.FatIndexBitSize
== 32);
7988 FatVerifyIndexIsValid( IrpContext
, Vcb
, FatIndex
);
7991 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned
,
7993 Buffer
= FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned
,
7996 TAG_ENTRY_LOOKUP_BUFFER
);
7998 OffsetIntoVolumeFile
= FatReservedBytes(&Vcb
->Bpb
) + FatIndex
* sizeof(ULONG
);
7999 PageEntryOffset
= (OffsetIntoVolumeFile
% PAGE_SIZE
) / sizeof(ULONG
);
8003 FatPerformVerifyDiskRead( IrpContext
,
8006 OffsetIntoVolumeFile
& ~(PAGE_SIZE
- 1),
8010 *FatEntry
= ((PULONG
)(Buffer
))[PageEntryOffset
];
8014 ExFreePool( Buffer
);
8019 // Local support routine
8022 _Requires_lock_held_(_Global_critical_region_
)
8024 FatScanForDismountedVcb (
8025 IN PIRP_CONTEXT IrpContext
8030 Routine Description:
8032 This routine walks through the list of Vcb's looking for any which may
8033 now be deleted. They may have been left on the list because there were
8034 outstanding references.
8053 // Walk through all of the Vcb's attached to the global data.
8056 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
8059 #pragma prefast( push )
8060 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
8061 #pragma prefast( disable: 28193, "this will always wait" )
8064 FatAcquireExclusiveGlobal( IrpContext
);
8067 #pragma prefast( pop )
8070 Links
= FatData
.VcbQueue
.Flink
;
8072 while (Links
!= &FatData
.VcbQueue
) {
8074 Vcb
= CONTAINING_RECORD( Links
, VCB
, VcbLinks
);
8077 // Move to the next link now since the current Vcb may be deleted.
8080 Links
= Links
->Flink
;
8083 // Try to acquire the VCB for exclusive access. If we cannot, just skip
8088 #pragma prefast( push )
8089 #pragma prefast( disable:28103,"prefast cannot work out that Vcb->Resource will be released below." )
8090 #pragma prefast( disable:28109,"prefast cannot work out the Vcb is not already held" );
8093 if (!ExAcquireResourceExclusiveLite( &(Vcb
->Resource
), FALSE
)) {
8099 #pragma prefast( pop )
8102 // Check if this Vcb can go away.
8105 VcbDeleted
= FatCheckForDismount( IrpContext
,
8110 // If the VCB was not deleted, release it.
8115 ExReleaseResourceLite( &(Vcb
->Resource
) );
8119 FatReleaseGlobal( IrpContext
);
8124 #if (NTDDI_VERSION >= NTDDI_WIN7)
8125 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8127 // FatSetZeroOnDeallocate is used when we need to stomp over the contents with zeros when a file is deleted.
8131 FatSetZeroOnDeallocate (
8132 __in PIRP_CONTEXT IrpContext
,
8136 NTSTATUS Status
= STATUS_SUCCESS
;
8142 TYPE_OF_OPEN TypeOfOpen
;
8144 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
8146 BOOLEAN ReleaseFcb
= FALSE
;
8151 // This call should always be synchronous.
8154 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
8156 TypeOfOpen
= FatDecodeFileObject( IrpSp
->FileObject
, &Vcb
, &FcbOrDcb
, &Ccb
);
8158 if ((TypeOfOpen
!= UserFileOpen
) ||
8159 (!IrpSp
->FileObject
->WriteAccess
) ) {
8161 FatCompleteRequest( IrpContext
, Irp
, STATUS_ACCESS_DENIED
);
8162 return STATUS_ACCESS_DENIED
;
8166 // Readonly mount should be just that: read only.
8169 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
8171 FatCompleteRequest( IrpContext
, Irp
, STATUS_MEDIA_WRITE_PROTECTED
);
8172 return STATUS_MEDIA_WRITE_PROTECTED
;
8176 // Acquire main then paging to exclude everyone from this FCB.
8179 FatAcquireExclusiveFcb(IrpContext
, FcbOrDcb
);
8184 SetFlag( FcbOrDcb
->FcbState
, FCB_STATE_ZERO_ON_DEALLOCATION
);
8189 FatReleaseFcb(IrpContext
, FcbOrDcb
);
8194 FatCompleteRequest( IrpContext
, Irp
, Status
);