3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Cleanup routine for Fat called by the
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_CLEANUP)
26 // The local debug trace level
29 #define Dbg (DEBUG_TRACE_CLEANUP)
32 // The following little routine exists solely because it need a spin lock.
37 IN PIRP_CONTEXT IrpContext
,
42 #pragma alloc_text(PAGE, FatCommonCleanup)
43 #pragma alloc_text(PAGE, FatFsdCleanup)
50 IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
58 This routine implements the FSD part of closing down a handle to a
63 VolumeDeviceObject - Supplies the volume device object where the
64 file being Cleanup exists
66 Irp - Supplies the Irp being processed
70 NTSTATUS - The FSD status for the IRP
76 PIRP_CONTEXT IrpContext
= NULL
;
81 // If we were called with our file system device object instead of a
82 // volume device object, just complete this request with STATUS_SUCCESS
85 if ( FatDeviceIsFatFsdo( VolumeDeviceObject
)) {
87 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
88 Irp
->IoStatus
.Information
= FILE_OPENED
;
90 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
92 return STATUS_SUCCESS
;
95 DebugTrace(+1, Dbg
, "FatFsdCleanup\n", 0);
98 // Call the common Cleanup routine, with blocking allowed.
101 FsRtlEnterFileSystem();
103 TopLevel
= FatIsIrpTopLevel( Irp
);
107 IrpContext
= FatCreateIrpContext( Irp
, TRUE
);
109 Status
= FatCommonCleanup( IrpContext
, Irp
);
111 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
114 // We had some trouble trying to perform the requested
115 // operation, so we'll abort the I/O request with
116 // the error status that we get back from the
120 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
123 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
125 FsRtlExitFileSystem();
128 // And return to our caller
131 DebugTrace(-1, Dbg
, "FatFsdCleanup -> %08lx\n", Status
);
133 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
141 IN PIRP_CONTEXT IrpContext
,
149 This is the common routine for cleanup of a file/directory called by both
150 the fsd and fsp threads.
152 Cleanup is invoked whenever the last handle to a file object is closed.
153 This is different than the Close operation which is invoked when the last
154 reference to a file object is deleted.
156 The function of cleanup is to essentially "cleanup" the file/directory
157 after a user is done with it. The Fcb/Dcb remains around (because MM
158 still has the file object referenced) but is now available for another
159 user to open (i.e., as far as the user is concerned the is now closed).
161 See close for a more complete description of what close does.
165 Irp - Supplies the Irp to process
169 NTSTATUS - The return status for the operation
176 PIO_STACK_LOCATION IrpSp
;
178 PFILE_OBJECT FileObject
;
180 TYPE_OF_OPEN TypeOfOpen
;
185 BOOLEAN SendUnlockNotification
= FALSE
;
187 PSHARE_ACCESS ShareAccess
;
189 PLARGE_INTEGER TruncateSize
= NULL
;
190 LARGE_INTEGER LocalTruncateSize
;
192 BOOLEAN AcquiredVcb
= FALSE
;
193 BOOLEAN AcquiredFcb
= FALSE
;
195 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
197 DebugTrace(+1, Dbg
, "FatCommonCleanup\n", 0);
198 DebugTrace( 0, Dbg
, "Irp = %08lx\n", Irp
);
199 DebugTrace( 0, Dbg
, "->FileObject = %08lx\n", IrpSp
->FileObject
);
202 // Extract and decode the file object
205 FileObject
= IrpSp
->FileObject
;
206 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
209 // Special case the unopened file object. This will occur only when
210 // we are initializing Vcb and IoCreateStreamFileObject is being
214 if (TypeOfOpen
== UnopenedFileObject
) {
216 DebugTrace(0, Dbg
, "Unopened File Object\n", 0);
218 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
220 DebugTrace(-1, Dbg
, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
221 return STATUS_SUCCESS
;
225 // If this is not our first time through (for whatever reason)
226 // only see if we have to flush the file.
229 if (FlagOn( FileObject
->Flags
, FO_CLEANUP_COMPLETE
)) {
231 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
) &&
232 FlagOn(FileObject
->Flags
, FO_FILE_MODIFIED
) &&
233 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
) &&
234 (TypeOfOpen
== UserFileOpen
)) {
240 Status
= FatFlushFile( IrpContext
, Fcb
, Flush
);
242 if (!NT_SUCCESS(Status
)) {
244 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
248 FatCompleteRequest( IrpContext
, Irp
, STATUS_SUCCESS
);
250 DebugTrace(-1, Dbg
, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
251 return STATUS_SUCCESS
;
255 // If we call change the allocation or call CcUninitialize,
256 // we have to take the Fcb exclusive
259 if ((TypeOfOpen
== UserFileOpen
) || (TypeOfOpen
== UserDirectoryOpen
)) {
261 ASSERT( Fcb
!= NULL
);
263 (VOID
)FatAcquireExclusiveFcb( IrpContext
, Fcb
);
268 // Do a check here if this was a DELETE_ON_CLOSE FileObject, and
269 // set the Fcb flag appropriately.
272 if (FlagOn(Ccb
->Flags
, CCB_FLAG_DELETE_ON_CLOSE
)) {
274 ASSERT( NodeType(Fcb
) != FAT_NTC_ROOT_DCB
);
276 SetFlag(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
279 // Report this to the dir notify package for a directory.
282 if (TypeOfOpen
== UserDirectoryOpen
) {
284 FsRtlNotifyFullChangeDirectory( Vcb
->NotifySync
,
286 FileObject
->FsContext
,
298 // Now if we may delete the file, drop the Fcb and acquire the Vcb
299 // first. Note that while we own the Fcb exclusive, a file cannot
300 // become DELETE_ON_CLOSE and cannot be opened via CommonCreate.
303 if ((Fcb
->UncleanCount
== 1) &&
304 FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
) &&
305 (Fcb
->FcbCondition
!= FcbBad
) &&
306 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
308 FatReleaseFcb( IrpContext
, Fcb
);
311 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
314 (VOID
)FatAcquireExclusiveFcb( IrpContext
, Fcb
);
320 // For user DASD cleanups, grab the Vcb exclusive.
323 if (TypeOfOpen
== UserVolumeOpen
) {
325 (VOID
)FatAcquireExclusiveVcb( IrpContext
, Vcb
);
330 // Complete any Notify Irps on this file handle.
333 if (TypeOfOpen
== UserDirectoryOpen
) {
335 FsRtlNotifyCleanup( Vcb
->NotifySync
,
341 // Determine the Fcb state, Good or Bad, for better or for worse.
343 // We can only read the volume file if VcbCondition is good.
349 // Stop any raises from FatVerifyFcb, unless it is REAL bad.
356 FatVerifyFcb( IrpContext
, Fcb
);
358 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
359 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
361 FatResetExceptionState( IrpContext
);
366 if ( _SEH2_AbnormalTermination() ) {
369 // We will be raising out of here.
372 if (AcquiredFcb
) { FatReleaseFcb( IrpContext
, Fcb
); }
373 if (AcquiredVcb
) { FatReleaseVcb( IrpContext
, Vcb
); }
381 // Case on the type of open that we are trying to cleanup.
382 // For all cases we need to set the share access to point to the
383 // share access variable (if there is one). After the switch
384 // we then remove the share access and complete the Irp.
385 // In the case of UserFileOpen we actually have a lot more work
386 // to do and we have the FsdLockControl complete the Irp for us.
389 switch (TypeOfOpen
) {
392 case VirtualVolumeFile
:
394 DebugTrace(0, Dbg
, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0);
402 DebugTrace(0, Dbg
, "Cleanup UserVolumeOpen\n", 0);
404 if (FlagOn( Ccb
->Flags
, CCB_FLAG_COMPLETE_DISMOUNT
)) {
406 FatCheckForDismount( IrpContext
, Vcb
, TRUE
);
409 // If this handle had write access, and actually wrote something,
410 // flush the device buffers, and then set the verify bit now
411 // just to be safe (in case there is no dismount).
414 } else if (FileObject
->WriteAccess
&&
415 FlagOn(FileObject
->Flags
, FO_FILE_MODIFIED
)) {
417 (VOID
)FatHijackIrpAndFlushDevice( IrpContext
,
419 Vcb
->TargetDeviceObject
);
421 SetFlag(Vcb
->Vpb
->RealDevice
->Flags
, DO_VERIFY_VOLUME
);
425 // If the volume is locked by this file object then release
426 // the volume and send notification.
429 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
) &&
430 (Vcb
->FileObjectWithVcbLocked
== FileObject
)) {
432 FatAutoUnlock( IrpContext
, Vcb
);
433 SendUnlockNotification
= TRUE
;
436 ShareAccess
= &Vcb
->ShareAccess
;
442 DebugTrace(0, Dbg
, "Cleanup EaFileObject\n", 0);
448 case UserDirectoryOpen
:
450 DebugTrace(0, Dbg
, "Cleanup UserDirectoryOpen\n", 0);
452 ShareAccess
= &Fcb
->ShareAccess
;
455 // Determine here if we should try do delayed close.
458 if ((Fcb
->UncleanCount
== 1) &&
459 (Fcb
->OpenCount
== 1) &&
460 (Fcb
->Specific
.Dcb
.DirectoryFileOpenCount
== 0) &&
461 !FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
) &&
462 Fcb
->FcbCondition
== FcbGood
) {
468 SetFlag( Fcb
->FcbState
, FCB_STATE_DELAY_CLOSE
);
471 FatUpdateDirentFromFcb( IrpContext
, FileObject
, Fcb
, Ccb
);
474 // If the directory has a unclean count of 1 then we know
475 // that this is the last handle for the file object. If
476 // we are supposed to delete it, do so.
479 if ((Fcb
->UncleanCount
== 1) &&
480 (NodeType(Fcb
) == FAT_NTC_DCB
) &&
481 (FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
)) &&
482 (Fcb
->FcbCondition
!= FcbBad
) &&
483 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
485 if (!FatIsDirectoryEmpty(IrpContext
, Fcb
)) {
488 // If there are files in the directory at this point,
489 // forget that we were trying to delete it.
492 ClearFlag( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
497 // Even if something goes wrong, we cannot turn back!
502 DELETE_CONTEXT DeleteContext
;
505 // Before truncating file allocation remember this
506 // info for FatDeleteDirent.
509 DeleteContext
.FileSize
= Fcb
->Header
.FileSize
.LowPart
;
510 DeleteContext
.FirstClusterOfFile
= Fcb
->FirstClusterOfFile
;
513 // Synchronize here with paging IO
516 (VOID
)ExAcquireResourceExclusiveLite( Fcb
->Header
.PagingIoResource
,
519 Fcb
->Header
.FileSize
.LowPart
= 0;
521 ExReleaseResourceLite( Fcb
->Header
.PagingIoResource
);
523 if (Vcb
->VcbCondition
== VcbGood
) {
526 // Truncate the file allocation down to zero
529 DebugTrace(0, Dbg
, "Delete File allocation\n", 0);
531 FatTruncateFileAllocation( IrpContext
, Fcb
, 0 );
533 if (Fcb
->Header
.AllocationSize
.LowPart
== 0) {
536 // Tunnel and remove the dirent for the directory
539 DebugTrace(0, Dbg
, "Delete the directory dirent\n", 0);
541 FatTunnelFcbOrDcb( Fcb
, NULL
);
543 FatDeleteDirent( IrpContext
, Fcb
, &DeleteContext
, TRUE
);
546 // Report that we have removed an entry.
549 FatNotifyReportChange( IrpContext
,
552 FILE_NOTIFY_CHANGE_DIR_NAME
,
553 FILE_ACTION_REMOVED
);
557 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
558 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
560 FatResetExceptionState( IrpContext
);
564 // Remove the entry from the name table.
565 // This will ensure that
566 // we will not collide with the Dcb if the user wants
567 // to recreate the same file over again before we
571 FatRemoveNames( IrpContext
, Fcb
);
576 // Decrement the unclean count.
579 ASSERT( Fcb
->UncleanCount
!= 0 );
580 Fcb
->UncleanCount
-= 1;
586 DebugTrace(0, Dbg
, "Cleanup UserFileOpen\n", 0);
588 ShareAccess
= &Fcb
->ShareAccess
;
591 // Determine here if we should do a delayed close.
594 if ((FileObject
->SectionObjectPointer
->DataSectionObject
== NULL
) &&
595 (FileObject
->SectionObjectPointer
->ImageSectionObject
== NULL
) &&
596 (Fcb
->UncleanCount
== 1) &&
597 (Fcb
->OpenCount
== 1) &&
598 !FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
) &&
599 Fcb
->FcbCondition
== FcbGood
) {
605 SetFlag( Fcb
->FcbState
, FCB_STATE_DELAY_CLOSE
);
609 // Unlock all outstanding file locks.
612 (VOID
) FsRtlFastUnlockAll( &Fcb
->Specific
.Fcb
.FileLock
,
614 IoGetRequestorProcess( Irp
),
618 // We can proceed with on-disk updates only if the volume is mounted.
619 // Remember that we toss all sections in the failed-verify and dismount
623 if (Vcb
->VcbCondition
== VcbGood
) {
625 if (Fcb
->FcbCondition
!= FcbBad
) {
627 FatUpdateDirentFromFcb( IrpContext
, FileObject
, Fcb
, Ccb
);
631 // If the file has a unclean count of 1 then we know
632 // that this is the last handle for the file object.
635 if ( (Fcb
->UncleanCount
== 1) && (Fcb
->FcbCondition
!= FcbBad
) ) {
637 DELETE_CONTEXT DeleteContext
;
640 // Check if we should be deleting the file. The
641 // delete operation really deletes the file but
642 // keeps the Fcb around for close to do away with.
645 if (FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
) &&
646 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
649 // Before truncating file allocation remember this
650 // info for FatDeleteDirent.
653 DeleteContext
.FileSize
= Fcb
->Header
.FileSize
.LowPart
;
654 DeleteContext
.FirstClusterOfFile
= Fcb
->FirstClusterOfFile
;
656 DebugTrace(0, Dbg
, "Delete File allocation\n", 0);
659 // Synchronize here with paging IO
662 (VOID
)ExAcquireResourceExclusiveLite( Fcb
->Header
.PagingIoResource
,
665 Fcb
->Header
.FileSize
.LowPart
= 0;
666 Fcb
->Header
.ValidDataLength
.LowPart
= 0;
667 Fcb
->ValidDataToDisk
= 0;
669 ExReleaseResourceLite( Fcb
->Header
.PagingIoResource
);
673 FatSetFileSizeInDirent( IrpContext
, Fcb
, NULL
);
675 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
676 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
678 FatResetExceptionState( IrpContext
);
681 Fcb
->FcbState
|= FCB_STATE_TRUNCATE_ON_CLOSE
;
686 // We must zero between ValidDataLength and FileSize
689 if (!FlagOn(Fcb
->FcbState
, FCB_STATE_PAGING_FILE
) &&
690 (Fcb
->Header
.ValidDataLength
.LowPart
< Fcb
->Header
.FileSize
.LowPart
)) {
692 ULONG ValidDataLength
;
694 ValidDataLength
= Fcb
->Header
.ValidDataLength
.LowPart
;
696 if (ValidDataLength
< Fcb
->ValidDataToDisk
) {
697 ValidDataLength
= Fcb
->ValidDataToDisk
;
701 // Recheck, VDD can be >= FS
704 if (ValidDataLength
< Fcb
->Header
.FileSize
.LowPart
) {
708 (VOID
)FatZeroData( IrpContext
,
712 Fcb
->Header
.FileSize
.LowPart
-
716 // Since we just zeroed this, we can now bump
717 // up VDL in the Fcb.
720 Fcb
->ValidDataToDisk
=
721 Fcb
->Header
.ValidDataLength
.LowPart
=
722 Fcb
->Header
.FileSize
.LowPart
;
725 // We inform Cc of the motion so that the cache map is updated.
726 // This prevents optimized zero-page faults in case the cache
727 // structures are re-used for another handle before they are torn
728 // down by our soon-to-occur uninitialize. If they were, a noncached
729 // producer could write into the region we just zeroed and Cc would
730 // be none the wiser, then our async cached reader comes in and takes
731 // the optimized path, and we get bad (zero) data.
733 // If this was memory mapped, we don't have to (can't) tell Cc, it'll
734 // figure it out when a cached handle is opened.
737 if (CcIsFileCached( FileObject
)) {
738 CcSetFileSizes( FileObject
, (PCC_FILE_SIZES
)&Fcb
->Header
.AllocationSize
);
741 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
742 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
744 FatResetExceptionState( IrpContext
);
751 // See if we are supposed to truncate the file on the last
752 // close. If we cannot wait we'll ship this off to the fsp
757 if (FlagOn(Fcb
->FcbState
, FCB_STATE_TRUNCATE_ON_CLOSE
)) {
759 DebugTrace(0, Dbg
, "truncate file allocation\n", 0);
761 if (Vcb
->VcbCondition
== VcbGood
) {
763 FatTruncateFileAllocation( IrpContext
,
765 Fcb
->Header
.FileSize
.LowPart
);
769 // We also have to get rid of the Cache Map because
770 // this is the only way we have of trashing the
774 LocalTruncateSize
= Fcb
->Header
.FileSize
;
775 TruncateSize
= &LocalTruncateSize
;
778 // Mark the Fcb as having now been truncated, just incase
779 // we have to reship this off to the fsp.
782 Fcb
->FcbState
&= ~FCB_STATE_TRUNCATE_ON_CLOSE
;
786 // Now check again if we are to delete the file and if
787 // so then we remove the file from the disk.
790 if (FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
) &&
791 Fcb
->Header
.AllocationSize
.LowPart
== 0) {
793 DebugTrace(0, Dbg
, "Delete File\n", 0);
796 // Now tunnel and delete the dirent
799 FatTunnelFcbOrDcb( Fcb
, Ccb
);
801 FatDeleteDirent( IrpContext
, Fcb
, &DeleteContext
, TRUE
);
804 // Report that we have removed an entry.
807 FatNotifyReportChange( IrpContext
,
810 FILE_NOTIFY_CHANGE_FILE_NAME
,
811 FILE_ACTION_REMOVED
);
814 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
815 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
) {
817 FatResetExceptionState( IrpContext
);
820 if (FlagOn(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
)) {
823 // Remove the entry from the splay table. This will
824 // ensure that we will not collide with the Fcb if the
825 // user wants to recreate the same file over again
826 // before we get a close irp.
828 // Note that we remove the name even if we couldn't
829 // truncate the allocation and remove the dirent above.
832 FatRemoveNames( IrpContext
, Fcb
);
838 // We've just finished everything associated with an unclean
839 // fcb so now decrement the unclean count before releasing
843 ASSERT( Fcb
->UncleanCount
!= 0 );
844 Fcb
->UncleanCount
-= 1;
845 if (!FlagOn( FileObject
->Flags
, FO_CACHE_SUPPORTED
)) {
846 ASSERT( Fcb
->NonCachedUncleanCount
!= 0 );
847 Fcb
->NonCachedUncleanCount
-= 1;
851 // If this was the last cached open, and there are open
852 // non-cached handles, attempt a flush and purge operation
853 // to avoid cache coherency overhead from these non-cached
854 // handles later. We ignore any I/O errors from the flush.
857 if (FlagOn( FileObject
->Flags
, FO_CACHE_SUPPORTED
) &&
858 (Fcb
->NonCachedUncleanCount
!= 0) &&
859 (Fcb
->NonCachedUncleanCount
== Fcb
->UncleanCount
) &&
860 (Fcb
->NonPaged
->SectionObjectPointers
.DataSectionObject
!= NULL
)) {
862 CcFlushCache( &Fcb
->NonPaged
->SectionObjectPointers
, NULL
, 0, NULL
);
865 // Grab and release PagingIo to serialize ourselves with the lazy writer.
866 // This will work to ensure that all IO has completed on the cached
867 // data and we will succesfully tear away the cache section.
870 ExAcquireResourceExclusiveLite( Fcb
->Header
.PagingIoResource
, TRUE
);
871 ExReleaseResourceLite( Fcb
->Header
.PagingIoResource
);
873 CcPurgeCacheSection( &Fcb
->NonPaged
->SectionObjectPointers
,
880 // If the file is invalid, hint to the cache that we should throw everything out.
883 if ( Fcb
->FcbCondition
== FcbBad
) {
885 TruncateSize
= &FatLargeZero
;
889 // Cleanup the cache map
892 CcUninitializeCacheMap( FileObject
, TruncateSize
, NULL
);
898 FatBugCheck( TypeOfOpen
, 0, 0 );
902 // We must clean up the share access at this time, since we may not
903 // get a Close call for awhile if the file was mapped through this
907 if (ShareAccess
!= NULL
) {
909 DebugTrace(0, Dbg
, "Cleanup the Share access\n", 0);
910 IoRemoveShareAccess( FileObject
, ShareAccess
);
913 if (TypeOfOpen
== UserFileOpen
) {
916 // Coordinate the cleanup operation with the oplock state.
917 // Cleanup operations can always cleanup immediately.
920 FsRtlCheckOplock( &Fcb
->Specific
.Fcb
.Oplock
,
926 Fcb
->Header
.IsFastIoPossible
= FatIsFastIoPossible( Fcb
);
930 // First set the FO_CLEANUP_COMPLETE flag.
933 SetFlag( FileObject
->Flags
, FO_CLEANUP_COMPLETE
);
935 Status
= STATUS_SUCCESS
;
938 // Now unpin any repinned Bcbs.
941 FatUnpinRepinnedBcbs( IrpContext
);
944 // If this was deferred flush media, flush the volume.
945 // We used to do this in lieu of write through for all removable
949 if (FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
) &&
950 !FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_WRITE_PROTECTED
)) {
956 if ((TypeOfOpen
== UserFileOpen
) &&
957 FlagOn(FileObject
->Flags
, FO_FILE_MODIFIED
)) {
959 Status
= FatFlushFile( IrpContext
, Fcb
, Flush
);
963 // If that worked ok, then see if we should flush the FAT as well.
966 if (NT_SUCCESS(Status
) && Fcb
&& !FatIsFat12( Vcb
) &&
967 FlagOn( Fcb
->FcbState
, FCB_STATE_FLUSH_FAT
)) {
969 Status
= FatFlushFat( IrpContext
, Vcb
);
972 if (!NT_SUCCESS(Status
)) {
974 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
980 DebugUnwind( FatCommonCleanup
);
982 if (AcquiredFcb
) { FatReleaseFcb( IrpContext
, Fcb
); }
983 if (AcquiredVcb
) { FatReleaseVcb( IrpContext
, Vcb
); }
985 if (SendUnlockNotification
) {
987 FsRtlNotifyVolumeEvent( FileObject
, FSRTL_VOLUME_UNLOCK
);
991 // If this is a normal termination then complete the request
994 if (!_SEH2_AbnormalTermination()) {
996 FatCompleteRequest( IrpContext
, Irp
, Status
);
999 DebugTrace(-1, Dbg
, "FatCommonCleanup -> %08lx\n", Status
);
1007 IN PIRP_CONTEXT IrpContext
,
1014 // Unlock the volume.
1017 IoAcquireVpbSpinLock( &SavedIrql
);
1019 ClearFlag( Vcb
->Vpb
->Flags
, VPB_LOCKED
);
1021 Vcb
->VcbState
&= ~VCB_STATE_FLAG_LOCKED
;
1022 Vcb
->FileObjectWithVcbLocked
= NULL
;
1024 IoReleaseVpbSpinLock( SavedIrql
);