3 Copyright (c) 1990-2000 Microsoft Corporation
11 This module implements the cache management routines for the Fat
12 FSD and FSP, by calling the Common Cache Manager.
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_CACHESUP)
26 // Local debug trace level
29 #define Dbg (DEBUG_TRACE_CACHESUP)
34 FatIsCurrentOperationSynchedForDcbTeardown (
35 IN PIRP_CONTEXT IrpContext
,
42 #pragma alloc_text(PAGE, FatCloseEaFile)
43 #pragma alloc_text(PAGE, FatCompleteMdl)
44 #pragma alloc_text(PAGE, FatOpenDirectoryFile)
45 #pragma alloc_text(PAGE, FatOpenEaFile)
46 #pragma alloc_text(PAGE, FatPinMappedData)
47 #pragma alloc_text(PAGE, FatPrepareWriteDirectoryFile)
48 #pragma alloc_text(PAGE, FatPrepareWriteVolumeFile)
49 #pragma alloc_text(PAGE, FatReadDirectoryFile)
50 #pragma alloc_text(PAGE, FatReadVolumeFile)
51 #pragma alloc_text(PAGE, FatRepinBcb)
52 #pragma alloc_text(PAGE, FatSyncUninitializeCacheMap)
53 #pragma alloc_text(PAGE, FatUnpinRepinnedBcbs)
54 #pragma alloc_text(PAGE, FatZeroData)
56 #pragma alloc_text(PAGE, FatIsCurrentOperationSynchedForDcbTeardown)
63 IN PIRP_CONTEXT IrpContext
,
75 This routine is called when the specified range of sectors is to be
76 read into the cache. In fat, the volume file only contains the boot
77 sector, reserved sectors, and the "fat(s)." Thus the volume file is
78 of fixed size and only extends up to (but not not including) the root
79 directory entry, and will never move or change size.
81 The fat volume file is also peculiar in that, since it starts at the
82 logical beginning of the disk, Vbo == Lbo.
86 Vcb - Pointer to the VCB for the volume
88 StartingVbo - The virtual offset of the first desired byte
90 ByteCount - Number of bytes desired
92 Bcb - Returns a pointer to the BCB which is valid until unpinned
94 Buffer - Returns a pointer to the sectors, which is valid until unpinned
104 // Check to see that all references are within the Bios Parameter Block
105 // or the fat(s). A special case is made when StartingVbo == 0 at
106 // mounting time since we do not know how big the fat is.
109 ASSERT( ((StartingVbo
== 0) || ((StartingVbo
+ ByteCount
) <= (ULONG
)
110 (FatRootDirectoryLbo( &Vcb
->Bpb
) + PAGE_SIZE
))));
112 DebugTrace(+1, Dbg
, "FatReadVolumeFile\n", 0);
113 DebugTrace( 0, Dbg
, "Vcb = %08lx\n", Vcb
);
114 DebugTrace( 0, Dbg
, "StartingVbo = %08lx\n", StartingVbo
);
115 DebugTrace( 0, Dbg
, "ByteCount = %08lx\n", ByteCount
);
118 // Call the Cache manager to attempt the transfer.
121 Vbo
.QuadPart
= StartingVbo
;
123 if (!CcMapData( Vcb
->VirtualVolumeFile
,
126 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
130 ASSERT( !FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
133 // Could not read the data without waiting (cache miss).
136 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
139 DbgDoit( IrpContext
->PinCount
+= 1 )
141 DebugTrace(-1, Dbg
, "FatReadVolumeFile -> VOID, *BCB = %08lx\n", *Bcb
);
148 FatPrepareWriteVolumeFile (
149 IN PIRP_CONTEXT IrpContext
,
155 IN BOOLEAN Reversible
,
163 This routine first looks to see if the specified range of sectors,
164 is already in the cache. If so, it increments the BCB PinCount,
165 sets the BCB dirty, and returns with the location of the sectors.
167 If the sectors are not in the cache and Wait is TRUE, it finds a
168 free BCB (potentially causing a flush), and clears out the entire
169 buffer. Once this is done, it increments the BCB PinCount, sets the
170 BCB dirty, and returns with the location of the sectors.
172 If the sectors are not in the cache and Wait is FALSE, this routine
173 raises STATUS_CANT_WAIT.
177 Vcb - Pointer to the VCB for the volume
179 StartingVbo - The virtual offset of the first byte to be written
181 ByteCount - Number of bytes to be written
183 Bcb - Returns a pointer to the BCB which is valid until unpinned
185 Buffer - Returns a pointer to the sectors, which is valid until unpinned
187 Reversible - Supplies TRUE if the specified range of modification should
188 be repinned so that the operation can be reversed in a controlled
189 fashion if errors are encountered.
191 Zero - Supplies TRUE if the specified range of bytes should be zeroed
201 // Check to see that all references are within the Bios Parameter Block
205 ASSERT( ((StartingVbo
+ ByteCount
) <= (ULONG
)
206 (FatRootDirectoryLbo( &Vcb
->Bpb
))));
208 DebugTrace(+1, Dbg
, "FatPrepareWriteVolumeFile\n", 0);
209 DebugTrace( 0, Dbg
, "Vcb = %08lx\n", Vcb
);
210 DebugTrace( 0, Dbg
, "StartingVbo = %08lx\n", (ULONG
)StartingVbo
);
211 DebugTrace( 0, Dbg
, "ByteCount = %08lx\n", ByteCount
);
212 DebugTrace( 0, Dbg
, "Zero = %08lx\n", Zero
);
215 // Call the Cache manager to attempt the transfer.
218 Vbo
.QuadPart
= StartingVbo
;
220 if (!CcPinRead( Vcb
->VirtualVolumeFile
,
223 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
227 ASSERT( !FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
230 // Could not read the data without waiting (cache miss).
233 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
237 // This keeps the data pinned until we complete the request
238 // and writes the dirty bit through to the disk.
241 DbgDoit( IrpContext
->PinCount
+= 1 )
247 RtlZeroMemory( *Buffer
, ByteCount
);
250 FatSetDirtyBcb( IrpContext
, *Bcb
, Vcb
, Reversible
);
254 if (_SEH2_AbnormalTermination()) {
256 FatUnpinBcb(IrpContext
, *Bcb
);
260 DebugTrace(-1, Dbg
, "FatPrepareWriteVolumeFile -> VOID, *Bcb = %08lx\n", *Bcb
);
267 FatReadDirectoryFile (
268 IN PIRP_CONTEXT IrpContext
,
282 This routine is called when the specified range of sectors is to be
283 read into the cache. If the desired range falls beyond the current
284 cache mapping, the fat will be searched, and if the desired range can
285 be satisfied, the cache mapping will be extended and the MCB updated
290 Dcb - Pointer to the DCB for the directory
292 StartingVbo - The virtual offset of the first desired byte
294 ByteCount - Number of bytes desired
296 Pin - Tells us if we should pin instead of just mapping.
298 Bcb - Returns a pointer to the BCB which is valid until unpinned
300 Buffer - Returns a pointer to the sectors, which is valid until unpinned
302 Status - Returns the status of the operation.
311 DebugTrace(+1, Dbg
, "FatReadDirectoryFile\n", 0);
312 DebugTrace( 0, Dbg
, "Dcb = %08lx\n", Dcb
);
313 DebugTrace( 0, Dbg
, "StartingVbo = %08lx\n", StartingVbo
);
314 DebugTrace( 0, Dbg
, "ByteCount = %08lx\n", ByteCount
);
317 // Check for the zero case
320 if (ByteCount
== 0) {
322 DebugTrace(0, Dbg
, "Nothing to read\n", 0);
326 *Status
= STATUS_SUCCESS
;
328 DebugTrace(-1, Dbg
, "FatReadDirectoryFile -> VOID\n", 0);
333 // If we need to create a directory file and initialize the
337 FatOpenDirectoryFile( IrpContext
, Dcb
);
340 // Now if the transfer is beyond the allocation size return EOF.
343 if (StartingVbo
>= Dcb
->Header
.AllocationSize
.LowPart
) {
345 DebugTrace(0, Dbg
, "End of file read for directory\n", 0);
349 *Status
= STATUS_END_OF_FILE
;
351 DebugTrace(-1, Dbg
, "FatReadDirectoryFile -> VOID\n", 0);
356 // If the caller is trying to read past the EOF, truncate the
360 ByteCount
= (Dcb
->Header
.AllocationSize
.LowPart
- StartingVbo
< ByteCount
) ?
361 Dcb
->Header
.AllocationSize
.LowPart
- StartingVbo
: ByteCount
;
363 ASSERT( ByteCount
!= 0 );
366 // Call the Cache manager to attempt the transfer.
369 Vbo
.QuadPart
= StartingVbo
;
373 !CcPinRead( Dcb
->Specific
.Dcb
.DirectoryFile
,
376 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
381 !CcMapData( Dcb
->Specific
.Dcb
.DirectoryFile
,
384 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
389 // Could not read the data without waiting (cache miss).
394 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
397 DbgDoit( IrpContext
->PinCount
+= 1 )
399 *Status
= STATUS_SUCCESS
;
401 DebugTrace(-1, Dbg
, "FatReadDirectoryFile -> VOID, *BCB = %08lx\n", *Bcb
);
408 FatPrepareWriteDirectoryFile (
409 IN PIRP_CONTEXT IrpContext
,
416 IN BOOLEAN Reversible
,
424 This routine first looks to see if the specified range of sectors
425 is already in the cache. If so, it increments the BCB PinCount,
426 sets the BCB dirty, and returns TRUE with the location of the sectors.
428 The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to
429 FatPrepareWriteVolumeFile() above.
433 Dcb - Pointer to the DCB for the directory
435 StartingVbo - The virtual offset of the first byte to be written
437 ByteCount - Number of bytes to be written
439 Bcb - Returns a pointer to the BCB which is valid until unpinned
441 Buffer - Returns a pointer to the sectors, which is valid until unpinned
443 Zero - Supplies TRUE if the specified range of bytes should be zeroed
445 Reversible - Supplies TRUE if the specified range of modification should
446 be repinned so that the operation can be reversed in a controlled
447 fashion if errors are encountered.
449 Status - Returns the status of the operation.
455 ULONG InitialAllocation
;
456 BOOLEAN UnwindWeAllocatedDiskSpace
= FALSE
;
463 DebugTrace(+1, Dbg
, "FatPrepareWriteDirectoryFile\n", 0);
464 DebugTrace( 0, Dbg
, "Dcb = %08lx\n", Dcb
);
465 DebugTrace( 0, Dbg
, "StartingVbo = %08lx\n", (ULONG
)StartingVbo
);
466 DebugTrace( 0, Dbg
, "ByteCount = %08lx\n", ByteCount
);
467 DebugTrace( 0, Dbg
, "Zero = %08lx\n", Zero
);
473 // If we need to create a directory file and initialize the
477 FatOpenDirectoryFile( IrpContext
, Dcb
);
480 // If the transfer is beyond the allocation size we need to
481 // extend the directory's allocation. The call to
482 // AddFileAllocation will raise a condition if
483 // it runs out of disk space. Note that the root directory
484 // cannot be extended.
487 Vbo
.QuadPart
= StartingVbo
;
491 if (StartingVbo
+ ByteCount
> Dcb
->Header
.AllocationSize
.LowPart
) {
493 if (NodeType(Dcb
) == FAT_NTC_ROOT_DCB
&&
494 !FatIsFat32(Dcb
->Vcb
)) {
496 FatRaiseStatus( IrpContext
, STATUS_DISK_FULL
);
499 DebugTrace(0, Dbg
, "Try extending normal directory\n", 0);
501 InitialAllocation
= Dcb
->Header
.AllocationSize
.LowPart
;
503 FatAddFileAllocation( IrpContext
,
505 Dcb
->Specific
.Dcb
.DirectoryFile
,
506 StartingVbo
+ ByteCount
);
508 UnwindWeAllocatedDiskSpace
= TRUE
;
511 // Inform the cache manager of the new allocation
514 Dcb
->Header
.FileSize
.LowPart
=
515 Dcb
->Header
.AllocationSize
.LowPart
;
517 CcSetFileSizes( Dcb
->Specific
.Dcb
.DirectoryFile
,
518 (PCC_FILE_SIZES
)&Dcb
->Header
.AllocationSize
);
521 // Set up the Bitmap buffer if it is not big enough already
524 FatCheckFreeDirentBitmap( IrpContext
, Dcb
);
527 // The newly allocated clusters should be zeroed starting at
528 // the previous allocation size
532 Vbo
.QuadPart
= InitialAllocation
;
533 ByteCount
= Dcb
->Header
.AllocationSize
.LowPart
- InitialAllocation
;
537 // Call the Cache Manager to attempt the transfer, going one cluster
538 // at a time to avoid pinning across a page boundary.
542 1 << Dcb
->Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
544 while (ByteCount
> 0) {
550 if (ByteCount
> ClusterSize
) {
551 BytesToPin
= ClusterSize
;
553 BytesToPin
= ByteCount
;
556 ASSERT( (Vbo
.QuadPart
/ ClusterSize
) ==
557 (Vbo
.QuadPart
+ BytesToPin
- 1)/ClusterSize
);
559 if (!CcPinRead( Dcb
->Specific
.Dcb
.DirectoryFile
,
562 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
567 // Could not read the data without waiting (cache miss).
570 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
574 // Update our caller with the beginning of their request.
577 if (*Buffer
== NULL
) {
579 *Buffer
= LocalBuffer
;
582 DbgDoit( IrpContext
->PinCount
+= 1 )
587 // We set this guy dirty right now so that we can raise CANT_WAIT when
588 // it needs to be done. It'd be beautiful if we could noop the read IO
589 // since we know we don't care about it.
592 RtlZeroMemory( LocalBuffer
, BytesToPin
);
593 CcSetDirtyPinnedData( *Bcb
, NULL
);
596 ByteCount
-= BytesToPin
;
597 Vbo
.QuadPart
+= BytesToPin
;
602 FatUnpinBcb( IrpContext
, *Bcb
);
607 // This lets us get the data pinned until we complete the request
608 // and writes the dirty bit through to the disk.
611 FatSetDirtyBcb( IrpContext
, *Bcb
, Dcb
->Vcb
, Reversible
);
613 *Status
= STATUS_SUCCESS
;
617 DebugUnwind( FatPrepareWriteDirectoryFile
);
619 if (_SEH2_AbnormalTermination()) {
622 // These steps are carefully arranged - FatTruncateFileAllocation can raise.
623 // Make sure we unpin the buffer. If FTFA raises, the effect should be benign.
626 FatUnpinBcb(IrpContext
, *Bcb
);
628 if (UnwindWeAllocatedDiskSpace
== TRUE
) {
631 // Inform the cache manager of the change.
634 FatTruncateFileAllocation( IrpContext
, Dcb
, InitialAllocation
);
636 Dcb
->Header
.FileSize
.LowPart
=
637 Dcb
->Header
.AllocationSize
.LowPart
;
639 CcSetFileSizes( Dcb
->Specific
.Dcb
.DirectoryFile
,
640 (PCC_FILE_SIZES
)&Dcb
->Header
.AllocationSize
);
644 DebugTrace(-1, Dbg
, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %08lx\n", *Bcb
);
652 BOOLEAN FatDisableParentCheck
= 0;
655 FatIsCurrentOperationSynchedForDcbTeardown (
656 IN PIRP_CONTEXT IrpContext
,
660 PIRP Irp
= IrpContext
->OriginatingIrp
;
661 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation( Irp
) ;
663 PFILE_OBJECT FileObject
= Stack
->FileObject
;
669 PFILE_OBJECT ToCheck
[3];
675 // While mounting, we're OK without having to own anything.
678 if (Stack
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&&
679 Stack
->MinorFunction
== IRP_MN_MOUNT_VOLUME
) {
685 // With the Vcb held, the close path is blocked out.
688 if (ExIsResourceAcquiredSharedLite( &Dcb
->Vcb
->Resource
) ||
689 ExIsResourceAcquiredExclusiveLite( &Dcb
->Vcb
->Resource
)) {
695 // Accept this assertion at face value. It comes from GetDirentForFcbOrDcb,
699 if (FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_PARENT_BY_CHILD
)) {
705 // Determine which fileobjects are around on this operation.
708 if (Stack
->MajorFunction
== IRP_MJ_SET_INFORMATION
&&
709 Stack
->Parameters
.SetFile
.FileObject
) {
711 ToCheck
[Index
++] = Stack
->Parameters
.SetFile
.FileObject
;
714 if (Stack
->FileObject
) {
716 ToCheck
[Index
++] = Stack
->FileObject
;
719 ToCheck
[Index
] = NULL
;
722 // If the fileobjects we have are for this dcb or a child of it, we are
723 // also guaranteed that this dcb isn't going anywhere (even without
727 for (Index
= 0; ToCheck
[Index
] != NULL
; Index
++) {
729 (VOID
) FatDecodeFileObject( ToCheck
[Index
], &Vcb
, &Fcb
, &Ccb
);
738 Fcb
= Fcb
->ParentDcb
;
742 return FatDisableParentCheck
;
747 FatOpenDirectoryFile (
748 IN PIRP_CONTEXT IrpContext
,
756 This routine opens a new directory file if one is not already open.
760 Dcb - Pointer to the DCB for the directory
771 DebugTrace(+1, Dbg
, "FatOpenDirectoryFile\n", 0);
772 DebugTrace( 0, Dbg
, "Dcb = %08lx\n", Dcb
);
775 // If we don't have some hold on this Dcb (there are several ways), there is nothing
776 // to prevent child files from closing and tearing this branch of the tree down in the
777 // midst of our slapping this reference onto it.
779 // I really wish we had a proper Fcb synchronization model (like CDFS/UDFS/NTFS).
782 ASSERT( FatIsCurrentOperationSynchedForDcbTeardown( IrpContext
, Dcb
));
785 // If we haven't yet set the correct AllocationSize, do so.
788 if (Dcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
790 FatLookupFileAllocationSize( IrpContext
, Dcb
);
792 Dcb
->Header
.FileSize
.LowPart
=
793 Dcb
->Header
.AllocationSize
.LowPart
;
797 // Setup the Bitmap buffer if it is not big enough already
800 FatCheckFreeDirentBitmap( IrpContext
, Dcb
);
803 // Check if we need to create a directory file.
805 // We first do a spot check and then synchronize and check again.
808 if (Dcb
->Specific
.Dcb
.DirectoryFile
== NULL
) {
810 PFILE_OBJECT DirectoryFileObject
= NULL
;
812 FatAcquireDirectoryFileMutex( Dcb
->Vcb
);
816 if (Dcb
->Specific
.Dcb
.DirectoryFile
== NULL
) {
818 PDEVICE_OBJECT RealDevice
;
821 // Create the special file object for the directory file, and set
822 // up its pointers back to the Dcb and the section object pointer.
823 // Note that setting the DirectoryFile pointer in the Dcb has
824 // to be the last thing done.
826 // Preallocate a close context since we have no Ccb for this object.
829 RealDevice
= Dcb
->Vcb
->CurrentDevice
;
831 DirectoryFileObject
= IoCreateStreamFileObject( NULL
, RealDevice
);
832 FatPreallocateCloseContext();
834 FatSetFileObject( DirectoryFileObject
,
840 // Remember this internal open.
844 InterlockedIncrement( &(Dcb
->Vcb
->InternalOpenCount
) );
846 InterlockedIncrement( (LONG
*)&(Dcb
->Vcb
->InternalOpenCount
) );
850 // If this is the root directory, it is also a residual open.
853 if (NodeType( Dcb
) == FAT_NTC_ROOT_DCB
) {
856 InterlockedIncrement( &(Dcb
->Vcb
->ResidualOpenCount
) );
858 InterlockedIncrement( (LONG
*)&(Dcb
->Vcb
->ResidualOpenCount
) );
862 DirectoryFileObject
->SectionObjectPointer
= &Dcb
->NonPaged
->SectionObjectPointers
;
864 DirectoryFileObject
->ReadAccess
= TRUE
;
865 DirectoryFileObject
->WriteAccess
= TRUE
;
866 DirectoryFileObject
->DeleteAccess
= TRUE
;
869 InterlockedIncrement( &Dcb
->Specific
.Dcb
.DirectoryFileOpenCount
);
871 InterlockedIncrement( (LONG
*)&Dcb
->Specific
.Dcb
.DirectoryFileOpenCount
);
874 Dcb
->Specific
.Dcb
.DirectoryFile
= DirectoryFileObject
;
877 // Indicate we're happy with the fileobject now.
880 DirectoryFileObject
= NULL
;
885 FatReleaseDirectoryFileMutex( Dcb
->Vcb
);
888 // Rip the object up if we couldn't get the close context.
891 if (DirectoryFileObject
) {
893 ObDereferenceObject( DirectoryFileObject
);
899 // Finally check if we need to initialize the Cache Map for the
900 // directory file. The size of the section we are going to map
901 // the current allocation size for the directory. Note that the
902 // cache manager will provide syncronization for us.
905 if ( Dcb
->Specific
.Dcb
.DirectoryFile
->PrivateCacheMap
== NULL
) {
907 Dcb
->Header
.ValidDataLength
= FatMaxLarge
;
908 Dcb
->ValidDataToDisk
= MAXULONG
;
910 CcInitializeCacheMap( Dcb
->Specific
.Dcb
.DirectoryFile
,
911 (PCC_FILE_SIZES
)&Dcb
->Header
.AllocationSize
,
913 &FatData
.CacheManagerNoOpCallbacks
,
917 DebugTrace(-1, Dbg
, "FatOpenDirectoryFile -> VOID\n", 0);
925 IN PIRP_CONTEXT IrpContext
,
933 This routine opens the Ea file.
937 EaFcb - Pointer to the Fcb for the Ea file.
941 Pointer to the new file object.
946 PFILE_OBJECT EaFileObject
= NULL
;
947 PDEVICE_OBJECT RealDevice
;
951 DebugTrace(+1, Dbg
, "FatOpenEaFile\n", 0);
952 DebugTrace( 0, Dbg
, "EaFcb = %08lx\n", EaFcb
);
955 // Create the special file object for the ea file, and set
956 // up its pointers back to the Fcb and the section object pointer
959 RealDevice
= EaFcb
->Vcb
->CurrentDevice
;
961 EaFileObject
= IoCreateStreamFileObject( NULL
, RealDevice
);
965 FatPreallocateCloseContext();
967 FatSetFileObject( EaFileObject
,
973 // Remember this internal, residual open.
977 InterlockedIncrement( &(EaFcb
->Vcb
->InternalOpenCount
) );
978 InterlockedIncrement( &(EaFcb
->Vcb
->ResidualOpenCount
) );
980 InterlockedIncrement( (LONG
*)&(EaFcb
->Vcb
->InternalOpenCount
) );
981 InterlockedIncrement( (LONG
*)&(EaFcb
->Vcb
->ResidualOpenCount
) );
984 EaFileObject
->SectionObjectPointer
= &EaFcb
->NonPaged
->SectionObjectPointers
;
986 EaFileObject
->ReadAccess
= TRUE
;
987 EaFileObject
->WriteAccess
= TRUE
;
990 // Finally check if we need to initialize the Cache Map for the
991 // ea file. The size of the section we are going to map
992 // the current allocation size for the Fcb.
995 EaFcb
->Header
.ValidDataLength
= FatMaxLarge
;
997 CcInitializeCacheMap( EaFileObject
,
998 (PCC_FILE_SIZES
)&EaFcb
->Header
.AllocationSize
,
1000 &FatData
.CacheManagerCallbacks
,
1003 CcSetAdditionalCacheAttributes( EaFileObject
, TRUE
, TRUE
);
1008 // Drop the fileobject if we're raising. Two cases: couldn't get
1009 // the close context, and it is still an UnopenedFileObject, or
1010 // we lost trying to build the cache map - in which case we're
1011 // OK for the close context if we have to.
1014 if (_SEH2_AbnormalTermination()) {
1016 ObDereferenceObject( EaFileObject
);
1020 DebugTrace(-1, Dbg
, "FatOpenEaFile -> %08lx\n", EaFileObject
);
1022 UNREFERENCED_PARAMETER( IrpContext
);
1024 return EaFileObject
;
1030 IN PIRP_CONTEXT IrpContext
,
1032 IN BOOLEAN FlushFirst
1037 Routine Description:
1039 This routine shuts down the ea file. Usually this is required when the volume
1040 begins to leave the system: after verify, dismount, deletion, pnp.
1044 Vcb - the volume to close the ea file on
1046 FlushFirst - whether the file should be flushed
1050 None. As a side effect, the EA fileobject in the Vcb is cleared.
1052 Caller must have the Vcb exclusive.
1057 PFILE_OBJECT EaFileObject
= Vcb
->VirtualEaFile
;
1061 DebugTrace(+1, Dbg
, "FatCloseEaFile\n", 0);
1062 DebugTrace( 0, Dbg
, "Vcb = %08lx\n", Vcb
);
1064 ASSERT( FatVcbAcquiredExclusive(IrpContext
, Vcb
) );
1066 if (EaFileObject
!= NULL
) {
1068 EaFileObject
= Vcb
->VirtualEaFile
;
1072 CcFlushCache( Vcb
->VirtualEaFile
->SectionObjectPointer
, NULL
, 0, NULL
);
1075 Vcb
->VirtualEaFile
= NULL
;
1078 // Empty the Mcb for the Ea file.
1081 FatRemoveMcbEntry( Vcb
, &Vcb
->EaFcb
->Mcb
, 0, 0xFFFFFFFF );
1084 // Uninitialize the cache for this file object and dereference it.
1087 FatSyncUninitializeCacheMap( IrpContext
, EaFileObject
);
1089 ObDereferenceObject( EaFileObject
);
1092 DebugTrace(-1, Dbg
, "FatCloseEaFile -> %08lx\n", EaFileObject
);
1098 IN PIRP_CONTEXT IrpContext
,
1100 IN PVCB Vcb OPTIONAL
,
1101 IN BOOLEAN Reversible
1106 Routine Description:
1108 This routine saves a reference to the bcb in the irp context and
1109 sets the bcb dirty. This will have the affect of keeping the page in
1110 memory until we complete the request
1112 In addition, a DPC is set to fire in 5 seconds (or if one is pending,
1113 pushed back 5 seconds) to mark the volume clean.
1117 Bcb - Supplies the Bcb being set dirty
1119 Vcb - Supplies the volume being marked dirty
1121 Reversible - Supplies TRUE if the specified range of bcb should be repinned
1122 so that the changes can be reversed in a controlled fashion if errors
1132 DebugTrace(+1, Dbg
, "FatSetDirtyBcb\n", 0 );
1133 DebugTrace( 0, Dbg
, "IrpContext = %08lx\n", IrpContext
);
1134 DebugTrace( 0, Dbg
, "Bcb = %08lx\n", Bcb
);
1135 DebugTrace( 0, Dbg
, "Vcb = %08lx\n", Vcb
);
1138 // Repin the bcb as required
1143 FatRepinBcb( IrpContext
, Bcb
);
1147 // Set the bcb dirty
1150 CcSetDirtyPinnedData( Bcb
, NULL
);
1153 // If volume dirtying isn't disabled for this operation (for
1154 // instance, when we're changing the dirty state), set the
1155 // volume dirty if we were given a Vcb that we want to perform
1156 // clean volume processing on, and return.
1158 // As a historical note, we used to key off of the old floppy
1159 // (now deferred flush) bit to disable dirtying behavior. Since
1160 // hotpluggable media can still be yanked while operations are
1161 // in flight, recognize that its really the case that FAT12
1162 // doesn't have the dirty bit.
1165 if ( !FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_DIRTY
) &&
1166 ARGUMENT_PRESENT(Vcb
) &&
1173 LARGE_INTEGER TimeSincePreviousCall
;
1174 LARGE_INTEGER CurrentTime
;
1177 // "Borrow" the irp context spinlock.
1180 KeQuerySystemTime( &CurrentTime
);
1182 KeAcquireSpinLock( &FatData
.GeneralSpinLock
, &SavedIrql
);
1184 TimeSincePreviousCall
.QuadPart
=
1185 CurrentTime
.QuadPart
- Vcb
->LastFatMarkVolumeDirtyCall
.QuadPart
;
1188 // If more than one second has elapsed since the prior call
1189 // to here, bump the timer up again and see if we need to
1190 // physically mark the volume dirty.
1193 if ( (TimeSincePreviousCall
.HighPart
!= 0) ||
1194 (TimeSincePreviousCall
.LowPart
> (1000 * 1000 * 10)) ) {
1203 KeReleaseSpinLock( &FatData
.GeneralSpinLock
, SavedIrql
);
1207 LARGE_INTEGER CleanVolumeTimer
;
1210 // We use a shorter volume clean timer for hot plug volumes.
1213 CleanVolumeTimer
.QuadPart
= FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
)
1214 ? (LONG
)-1500*1000*10
1215 : (LONG
)-8*1000*1000*10;
1217 (VOID
)KeCancelTimer( &Vcb
->CleanVolumeTimer
);
1218 (VOID
)KeRemoveQueueDpc( &Vcb
->CleanVolumeDpc
);
1221 // We have now synchronized with anybody clearing the dirty
1222 // flag, so we can now see if we really have to actually write
1223 // out the physical bit.
1226 if ( !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
) ) {
1229 // We want to really mark the volume dirty now.
1232 if (!FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_MOUNTED_DIRTY
)) {
1234 FatMarkVolume( IrpContext
, Vcb
, VolumeDirty
);
1237 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VOLUME_DIRTY
);
1240 // Lock the volume if it is removable.
1243 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
)) {
1245 FatToggleMediaEjectDisable( IrpContext
, Vcb
, TRUE
);
1249 KeAcquireSpinLock( &FatData
.GeneralSpinLock
, &SavedIrql
);
1251 KeQuerySystemTime( &Vcb
->LastFatMarkVolumeDirtyCall
);
1253 KeReleaseSpinLock( &FatData
.GeneralSpinLock
, SavedIrql
);
1255 KeSetTimer( &Vcb
->CleanVolumeTimer
,
1257 &Vcb
->CleanVolumeDpc
);
1261 DebugTrace(-1, Dbg
, "FatSetDirtyBcb -> VOID\n", 0 );
1267 IN PIRP_CONTEXT IrpContext
,
1273 Routine Description:
1275 This routine saves a reference to the bcb in the irp context. This will
1276 have the affect of keeping the page in memory until we complete the
1281 Bcb - Supplies the Bcb being referenced
1290 PREPINNED_BCBS Repinned
;
1295 DebugTrace(+1, Dbg
, "FatRepinBcb\n", 0 );
1296 DebugTrace( 0, Dbg
, "IrpContext = %08lx\n", IrpContext
);
1297 DebugTrace( 0, Dbg
, "Bcb = %08lx\n", Bcb
);
1300 // The algorithm is to search the list of repinned records until
1301 // we either find a match for the bcb or we find a null slot.
1304 Repinned
= &IrpContext
->Repinned
;
1309 // For every entry in the repinned record check if the bcb's
1310 // match or if the entry is null. If the bcb's match then
1311 // we've done because we've already repinned this bcb, if
1312 // the entry is null then we know, because it's densely packed,
1313 // that the bcb is not in the list so add it to the repinned
1314 // record and repin it.
1317 for (i
= 0; i
< REPINNED_BCBS_ARRAY_SIZE
; i
+= 1) {
1319 if (Repinned
->Bcb
[i
] == Bcb
) {
1321 DebugTrace(-1, Dbg
, "FatRepinBcb -> VOID\n", 0 );
1325 if (Repinned
->Bcb
[i
] == NULL
) {
1327 Repinned
->Bcb
[i
] = Bcb
;
1330 DebugTrace(-1, Dbg
, "FatRepinBcb -> VOID\n", 0 );
1336 // We finished checking one repinned record so now locate the next
1337 // repinned record, If there isn't one then allocate and zero out
1341 if (Repinned
->Next
== NULL
) {
1343 Repinned
->Next
= FsRtlAllocatePoolWithTag( PagedPool
,
1344 sizeof(REPINNED_BCBS
),
1347 RtlZeroMemory( Repinned
->Next
, sizeof(REPINNED_BCBS
) );
1350 Repinned
= Repinned
->Next
;
1356 FatUnpinRepinnedBcbs (
1357 IN PIRP_CONTEXT IrpContext
1362 Routine Description:
1364 This routine frees all of the repinned bcbs, stored in an IRP context.
1375 IO_STATUS_BLOCK RaiseIosb
;
1376 PREPINNED_BCBS Repinned
;
1377 BOOLEAN WriteThroughToDisk
;
1378 PFILE_OBJECT FileObject
= NULL
;
1379 BOOLEAN ForceVerify
= FALSE
;
1384 DebugTrace(+1, Dbg
, "FatUnpinRepinnedBcbs\n", 0 );
1385 DebugTrace( 0, Dbg
, "IrpContext = %08lx\n", IrpContext
);
1388 // The algorithm for this procedure is to scan the entire list of
1389 // repinned records unpinning any repinned bcbs. We start off
1390 // with the first record in the irp context, and while there is a
1391 // record to scan we do the following loop.
1394 Repinned
= &IrpContext
->Repinned
;
1395 RaiseIosb
.Status
= STATUS_SUCCESS
;
1398 // If the request is write through or the media is deferred flush,
1399 // unpin the bcb's write through.
1402 WriteThroughToDisk
= (BOOLEAN
) (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH
) &&
1403 IrpContext
->Vcb
!= NULL
&&
1404 (FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WRITE_THROUGH
) ||
1405 FlagOn(IrpContext
->Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
)));
1407 while (Repinned
!= NULL
) {
1410 // For every non-null entry in the repinned record unpin the
1413 // If the this is removable media (therefore all requests write-
1414 // through) and the write fails, purge the cache so that we throw
1415 // away the modifications as we will be returning an error to the
1419 for (i
= 0; i
< REPINNED_BCBS_ARRAY_SIZE
; i
+= 1) {
1421 if (Repinned
->Bcb
[i
] != NULL
) {
1423 IO_STATUS_BLOCK Iosb
;
1425 if (WriteThroughToDisk
&&
1426 FlagOn(IrpContext
->Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
)) {
1428 FileObject
= CcGetFileObjectFromBcb( Repinned
->Bcb
[i
] );
1431 CcUnpinRepinnedBcb( Repinned
->Bcb
[i
],
1435 if ( !NT_SUCCESS(Iosb
.Status
) ) {
1437 if (RaiseIosb
.Status
== STATUS_SUCCESS
) {
1443 // If this was a writethrough device, purge the cache,
1444 // except for Irp major codes that either don't handle
1445 // the error paths correctly or are simple victims like
1450 (IrpContext
->MajorFunction
!= IRP_MJ_CLEANUP
) &&
1451 (IrpContext
->MajorFunction
!= IRP_MJ_FLUSH_BUFFERS
) &&
1452 (IrpContext
->MajorFunction
!= IRP_MJ_SET_INFORMATION
)) {
1455 // The call to CcPurgeCacheSection() below will
1456 // purge the entire file from memory. It will also
1457 // block until all the file's BCB's are pinned.
1459 // We end up in a deadlock situation of there
1460 // are any other pinned BCB's in this IRP context
1461 // so the first thing we do is search the list
1462 // for BCB's pinned in the same file and unpin
1465 // We are probably not going to lose data because
1466 // it's safe to assume that all flushes will
1467 // fail after the first one fails.
1472 for (j
= i
+ 1; j
< REPINNED_BCBS_ARRAY_SIZE
; j
++) {
1474 if (Repinned
->Bcb
[j
] != NULL
) {
1476 if (CcGetFileObjectFromBcb( Repinned
->Bcb
[j
] ) == FileObject
) {
1478 CcUnpinRepinnedBcb( Repinned
->Bcb
[j
],
1482 Repinned
->Bcb
[j
] = NULL
;
1487 CcPurgeCacheSection( FileObject
->SectionObjectPointer
,
1493 // Force a verify operation here since who knows
1494 // what state things are in.
1501 Repinned
->Bcb
[i
] = NULL
;
1507 // Now find the next repinned record in the list, and possibly
1508 // delete the one we've just processed.
1511 if (Repinned
!= &IrpContext
->Repinned
) {
1513 PREPINNED_BCBS Saved
;
1515 Saved
= Repinned
->Next
;
1516 ExFreePool( Repinned
);
1521 Repinned
= Repinned
->Next
;
1522 IrpContext
->Repinned
.Next
= NULL
;
1527 // Now if we weren't completely successful in the our unpin
1528 // then raise the iosb we got
1531 if (!NT_SUCCESS(RaiseIosb
.Status
)) {
1533 if (ForceVerify
&& FileObject
) {
1535 SetFlag(FileObject
->DeviceObject
->Flags
, DO_VERIFY_VOLUME
);
1537 IoSetHardErrorOrVerifyDevice( IrpContext
->OriginatingIrp
,
1538 FileObject
->DeviceObject
);
1541 if (!FlagOn( IrpContext
->Flags
, IRP_CONTEXT_FLAG_DISABLE_RAISE
)) {
1543 IrpContext
->OriginatingIrp
->IoStatus
= RaiseIosb
;
1544 FatNormalizeAndRaiseStatus( IrpContext
, RaiseIosb
.Status
);
1548 DebugTrace(-1, Dbg
, "FatUnpinRepinnedBcbs -> VOID\n", 0 );
1556 IN PIRP_CONTEXT IrpContext
,
1558 IN PFILE_OBJECT FileObject
,
1559 IN ULONG StartingZero
,
1565 **** Temporary function - Remove when CcZeroData is capable of handling
1566 non sector aligned requests.
1571 LARGE_INTEGER ZeroStart
= {0,0};
1572 LARGE_INTEGER BeyondZeroEnd
= {0,0};
1574 LARGE_INTEGER ZeroStart
= {{0}};
1575 LARGE_INTEGER BeyondZeroEnd
= {{0}};
1584 SectorSize
= (ULONG
)Vcb
->Bpb
.BytesPerSector
;
1586 ZeroStart
.LowPart
= (StartingZero
+ (SectorSize
- 1)) & ~(SectorSize
- 1);
1589 // Detect overflow if we were asked to zero in the last sector of the file,
1590 // which must be "zeroed" already (or we're in trouble).
1593 if (StartingZero
!= 0 && ZeroStart
.LowPart
== 0) {
1599 // Note that BeyondZeroEnd can take the value 4gb.
1602 BeyondZeroEnd
.QuadPart
= ((ULONGLONG
) StartingZero
+ ByteCount
+ (SectorSize
- 1))
1603 & (~((LONGLONG
) SectorSize
- 1));
1606 // If we were called to just zero part of a sector we are in trouble.
1609 if ( ZeroStart
.QuadPart
== BeyondZeroEnd
.QuadPart
) {
1614 Finished
= CcZeroData( FileObject
,
1617 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
1625 IN PIRP_CONTEXT IrpContext
,
1631 Routine Description:
1633 This routine performs the function of completing Mdl read and write
1634 requests. It should be called only from FatFsdRead and FatFsdWrite.
1638 Irp - Supplies the originating Irp.
1642 NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS.
1647 PFILE_OBJECT FileObject
;
1648 PIO_STACK_LOCATION IrpSp
;
1652 DebugTrace(+1, Dbg
, "FatCompleteMdl\n", 0 );
1653 DebugTrace( 0, Dbg
, "IrpContext = %08lx\n", IrpContext
);
1654 DebugTrace( 0, Dbg
, "Irp = %08lx\n", Irp
);
1657 // Do completion processing.
1660 FileObject
= IoGetCurrentIrpStackLocation( Irp
)->FileObject
;
1662 switch( IrpContext
->MajorFunction
) {
1666 CcMdlReadComplete( FileObject
, Irp
->MdlAddress
);
1671 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
1673 ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
));
1675 CcMdlWriteComplete( FileObject
, &IrpSp
->Parameters
.Write
.ByteOffset
, Irp
->MdlAddress
);
1677 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1683 DebugTrace( DEBUG_TRACE_ERROR
, 0, "Illegal Mdl Complete.\n", 0);
1684 FatBugCheck( IrpContext
->MajorFunction
, 0, 0 );
1688 // Mdl is now deallocated.
1691 Irp
->MdlAddress
= NULL
;
1694 // Complete the request and exit right away.
1697 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
1699 DebugTrace(-1, Dbg
, "FatCompleteMdl -> STATUS_SUCCESS\n", 0 );
1701 return STATUS_SUCCESS
;
1705 FatSyncUninitializeCacheMap (
1706 IN PIRP_CONTEXT IrpContext
,
1707 IN PFILE_OBJECT FileObject
1712 Routine Description:
1714 The routine performs a CcUnitializeCacheMap to LargeZero synchronously. That
1715 is it waits on the Cc event. This call is useful when we want to be certain
1716 when a close will actually some in.
1725 CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent
;
1726 NTSTATUS WaitStatus
;
1730 KeInitializeEvent( &UninitializeCompleteEvent
.Event
,
1731 SynchronizationEvent
,
1734 CcUninitializeCacheMap( FileObject
,
1736 &UninitializeCompleteEvent
);
1739 // Now wait for the cache manager to finish purging the file.
1740 // This will garentee that Mm gets the purge before we
1744 WaitStatus
= KeWaitForSingleObject( &UninitializeCompleteEvent
.Event
,
1750 ASSERT(WaitStatus
== STATUS_SUCCESS
);
1755 IN PIRP_CONTEXT IrpContext
,
1764 Routine Description:
1766 This routine pins data that was previously mapped before setting it dirty.
1770 Dcb - Pointer to the DCB for the directory
1772 StartingVbo - The virtual offset of the first desired byte
1774 ByteCount - Number of bytes desired
1776 Bcb - Returns a pointer to the BCB which is valid until unpinned
1785 DebugTrace(+1, Dbg
, "FatPinMappedData\n", 0);
1786 DebugTrace( 0, Dbg
, "Dcb = %08lx\n", Dcb
);
1787 DebugTrace( 0, Dbg
, "StartingVbo = %08lx\n", StartingVbo
);
1788 DebugTrace( 0, Dbg
, "ByteCount = %08lx\n", ByteCount
);
1791 // Call the Cache manager to perform the operation.
1794 Vbo
.QuadPart
= StartingVbo
;
1796 if (!CcPinMappedData( Dcb
->Specific
.Dcb
.DirectoryFile
,
1799 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
1803 // Could not pin the data without waiting (cache miss).
1806 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
1809 DebugTrace(-1, Dbg
, "FatReadDirectoryFile -> VOID, *BCB = %08lx\n", *Bcb
);