1142 lines
34 KiB
C
1142 lines
34 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Cleanup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the File Cleanup routine for Fat called by the
|
|||
|
dispatch driver.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 28-Dec-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "FatProcs.h"
|
|||
|
|
|||
|
//
|
|||
|
// The Bug check file id for this module
|
|||
|
//
|
|||
|
|
|||
|
#define BugCheckFileId (FAT_BUG_CHECK_CLEANUP)
|
|||
|
|
|||
|
//
|
|||
|
// The local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_CLEANUP)
|
|||
|
|
|||
|
//
|
|||
|
// The following little routine exists solely because it need a spin lock.
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
FatAutoUnlock (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, FatCommonCleanup)
|
|||
|
#pragma alloc_text(PAGE, FatFsdCleanup)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFsdCleanup (
|
|||
|
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the FSD part of closing down a handle to a
|
|||
|
file object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
VolumeDeviceObject - Supplies the volume device object where the
|
|||
|
file being Cleanup exists
|
|||
|
|
|||
|
Irp - Supplies the Irp being processed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The FSD status for the IRP
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PIRP_CONTEXT IrpContext = NULL;
|
|||
|
|
|||
|
BOOLEAN TopLevel;
|
|||
|
|
|||
|
//
|
|||
|
// If we were called with our file system device object instead of a
|
|||
|
// volume device object, just complete this request with STATUS_SUCCESS
|
|||
|
//
|
|||
|
|
|||
|
if (VolumeDeviceObject->DeviceObject.Size == (USHORT)sizeof(DEVICE_OBJECT)) {
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = FILE_OPENED;
|
|||
|
|
|||
|
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "FatFsdCleanup\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Call the common Cleanup routine, with blocking allowed.
|
|||
|
//
|
|||
|
|
|||
|
FsRtlEnterFileSystem();
|
|||
|
|
|||
|
TopLevel = FatIsIrpTopLevel( Irp );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
IrpContext = FatCreateIrpContext( Irp, TRUE );
|
|||
|
|
|||
|
Status = FatCommonCleanup( IrpContext, Irp );
|
|||
|
|
|||
|
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|||
|
|
|||
|
//
|
|||
|
// We had some trouble trying to perform the requested
|
|||
|
// operation, so we'll abort the I/O request with
|
|||
|
// the error status that we get back from the
|
|||
|
// execption code
|
|||
|
//
|
|||
|
|
|||
|
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
|
|||
|
}
|
|||
|
|
|||
|
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
|||
|
|
|||
|
FsRtlExitFileSystem();
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatFsdCleanup -> %08lx\n", Status);
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatCommonCleanup (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the common routine for cleanup of a file/directory called by both
|
|||
|
the fsd and fsp threads.
|
|||
|
|
|||
|
Cleanup is invoked whenever the last handle to a file object is closed.
|
|||
|
This is different than the Close operation which is invoked when the last
|
|||
|
reference to a file object is deleted.
|
|||
|
|
|||
|
The function of cleanup is to essentially "cleanup" the file/directory
|
|||
|
after a user is done with it. The Fcb/Dcb remains around (because MM
|
|||
|
still has the file object referenced) but is now available for another
|
|||
|
user to open (i.e., as far as the user is concerned the is now closed).
|
|||
|
|
|||
|
See close for a more complete description of what close does.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp to process
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The return status for the operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
|
|||
|
PFILE_OBJECT FileObject;
|
|||
|
|
|||
|
TYPE_OF_OPEN TypeOfOpen;
|
|||
|
PVCB Vcb;
|
|||
|
PFCB Fcb;
|
|||
|
PCCB Ccb;
|
|||
|
|
|||
|
PSHARE_ACCESS ShareAccess;
|
|||
|
|
|||
|
PLARGE_INTEGER TruncateSize;
|
|||
|
LARGE_INTEGER LocalTruncateSize;
|
|||
|
|
|||
|
BOOLEAN AcquiredVcb = FALSE;
|
|||
|
BOOLEAN AcquiredFcb = FALSE;
|
|||
|
|
|||
|
BOOLEAN SetArchiveBit;
|
|||
|
|
|||
|
BOOLEAN UpdateFileSize;
|
|||
|
BOOLEAN UpdateLastWriteTime;
|
|||
|
BOOLEAN UpdateLastAccessTime;
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "FatCommonCleanup\n", 0);
|
|||
|
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
|
|||
|
DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject);
|
|||
|
|
|||
|
//
|
|||
|
// Extract and decode the file object
|
|||
|
//
|
|||
|
|
|||
|
FileObject = IrpSp->FileObject;
|
|||
|
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
|
|||
|
|
|||
|
//
|
|||
|
// Special case the unopened file object. This will occur only when
|
|||
|
// we are initializing Vcb and IoCreateStreamFileObject is being
|
|||
|
// called.
|
|||
|
//
|
|||
|
|
|||
|
if (TypeOfOpen == UnopenedFileObject) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Unopened File Object\n", 0);
|
|||
|
|
|||
|
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is not our first time through (for whatever reason)
|
|||
|
// only see if we have to flush the file.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) {
|
|||
|
|
|||
|
if ((TypeOfOpen == UserFileOpen) &&
|
|||
|
FlagOn(Vcb->VcbState, VCB_STATE_FLAG_FLOPPY) &&
|
|||
|
FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) {
|
|||
|
|
|||
|
//
|
|||
|
// Flush the file.
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushFile( IrpContext, Fcb );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we call change the allocation or call CcUninitialize,
|
|||
|
// we have to take the Fcb exclusive
|
|||
|
//
|
|||
|
|
|||
|
if ((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) {
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
AcquiredFcb = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Do a check here if this was a DELETE_ON_CLOSE FileObject, and
|
|||
|
// set the Fcb flag appropriately.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) {
|
|||
|
|
|||
|
SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
|
|||
|
|
|||
|
//
|
|||
|
// Report this to the dir notify package for a directory.
|
|||
|
//
|
|||
|
|
|||
|
if (TypeOfOpen == UserDirectoryOpen) {
|
|||
|
|
|||
|
FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
|
|||
|
&Vcb->DirNotifyList,
|
|||
|
FileObject->FsContext,
|
|||
|
NULL,
|
|||
|
FALSE,
|
|||
|
FALSE,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now if we may delete the file, drop the Fcb and acquire the Vcb
|
|||
|
// first. Note that while we own the Fcb exclusive, a file cannot
|
|||
|
// become DELETE_ON_CLOSE and cannot be opened via CommonCreate.
|
|||
|
//
|
|||
|
|
|||
|
if ((Fcb->UncleanCount == 1) &&
|
|||
|
FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
|
|||
|
(Fcb->FcbCondition != FcbBad) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
FatReleaseFcb( IrpContext, Fcb );
|
|||
|
AcquiredFcb = FALSE;
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
|
|||
|
AcquiredVcb = TRUE;
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
|
|||
|
AcquiredFcb = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For user DASD cleanups, grab the Vcb exclusive.
|
|||
|
//
|
|||
|
|
|||
|
if (TypeOfOpen == UserVolumeOpen) {
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
|
|||
|
AcquiredVcb = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Complete any Notify Irps on this file handle.
|
|||
|
//
|
|||
|
|
|||
|
if (TypeOfOpen == UserDirectoryOpen) {
|
|||
|
|
|||
|
FsRtlNotifyCleanup( Vcb->NotifySync,
|
|||
|
&Vcb->DirNotifyList,
|
|||
|
Ccb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine the Fcb state, Good or Bad, for better or for worse.
|
|||
|
//
|
|||
|
// We can only read the volume file if VcbCondition is good.
|
|||
|
//
|
|||
|
|
|||
|
if ( Fcb != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Stop any raises from FatVerifyFcb, unless it is REAL bad.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatVerifyFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
if ( AbnormalTermination() ) {
|
|||
|
|
|||
|
//
|
|||
|
// We will be raising out of here.
|
|||
|
//
|
|||
|
|
|||
|
if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); }
|
|||
|
if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
LARGE_INTEGER CurrentTime;
|
|||
|
LARGE_INTEGER CurrentDay;
|
|||
|
|
|||
|
//
|
|||
|
// Case on the type of open that we are trying to cleanup.
|
|||
|
// For all cases we need to set the share access to point to the
|
|||
|
// share access variable (if there is one). After the switch
|
|||
|
// we then remove the share access and complete the Irp.
|
|||
|
// In the case of UserFileOpen we actually have a lot more work
|
|||
|
// to do and we have the FsdLockControl complete the Irp for us.
|
|||
|
//
|
|||
|
|
|||
|
switch (TypeOfOpen) {
|
|||
|
|
|||
|
case DirectoryFile:
|
|||
|
case VirtualVolumeFile:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0);
|
|||
|
|
|||
|
ShareAccess = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case UserVolumeOpen:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup UserVolumeOpen\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// If this handle had write access, and actually wrote something,
|
|||
|
// flush the device buffers, and then set the verify bit now
|
|||
|
// just to be safe (in case there is no dismount).
|
|||
|
//
|
|||
|
|
|||
|
if (FileObject->WriteAccess &&
|
|||
|
FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) {
|
|||
|
|
|||
|
(VOID)FatHijackIrpAndFlushDevice( IrpContext,
|
|||
|
Irp,
|
|||
|
Vcb->TargetDeviceObject );
|
|||
|
|
|||
|
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the volume is locked by this file object then release
|
|||
|
// the volume.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) &&
|
|||
|
(Vcb->FileObjectWithVcbLocked == FileObject)) {
|
|||
|
|
|||
|
FatAutoUnlock( IrpContext, Vcb );
|
|||
|
}
|
|||
|
|
|||
|
ShareAccess = &Vcb->ShareAccess;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case EaFile:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup EaFileObject\n", 0);
|
|||
|
|
|||
|
ShareAccess = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case UserDirectoryOpen:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup UserDirectoryOpen\n", 0);
|
|||
|
|
|||
|
ShareAccess = &Fcb->ShareAccess;
|
|||
|
|
|||
|
//
|
|||
|
// Determine here if we should try do delayed close.
|
|||
|
//
|
|||
|
|
|||
|
if ((Fcb->UncleanCount == 1) &&
|
|||
|
(Fcb->OpenCount == 1) &&
|
|||
|
(Fcb->Specific.Dcb.DirectoryFileOpenCount == 0) &&
|
|||
|
!FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
|
|||
|
Fcb->FcbCondition == FcbGood) {
|
|||
|
|
|||
|
//
|
|||
|
// Delay our close.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the directory has a unclean count of 1 then we know
|
|||
|
// that this is the last handle for the file object. If
|
|||
|
// we are supposed to delete it, do so.
|
|||
|
//
|
|||
|
|
|||
|
if ((Fcb->UncleanCount == 1) &&
|
|||
|
(NodeType(Fcb) == FAT_NTC_DCB) &&
|
|||
|
(FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) &&
|
|||
|
(Fcb->FcbCondition != FcbBad) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
if (!FatIsDirectoryEmpty(IrpContext, Fcb)) {
|
|||
|
|
|||
|
//
|
|||
|
// If there are files in the directory at this point,
|
|||
|
// forget that we were trying to delete it.
|
|||
|
//
|
|||
|
|
|||
|
ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Even if something goes wrong, we cannot turn back!
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
DELETE_CONTEXT DeleteContext;
|
|||
|
|
|||
|
//
|
|||
|
// Before truncating file allocation remember this
|
|||
|
// info for FatDeleteDirent.
|
|||
|
//
|
|||
|
|
|||
|
DeleteContext.FileSize = Fcb->Header.FileSize.LowPart;
|
|||
|
DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile;
|
|||
|
|
|||
|
//
|
|||
|
// Synchronize here with paging IO
|
|||
|
//
|
|||
|
|
|||
|
(VOID)ExAcquireResourceExclusive( Fcb->Header.PagingIoResource,
|
|||
|
TRUE );
|
|||
|
|
|||
|
Fcb->Header.FileSize.LowPart = 0;
|
|||
|
|
|||
|
ExReleaseResource( Fcb->Header.PagingIoResource );
|
|||
|
|
|||
|
if (Vcb->VcbCondition == VcbGood) {
|
|||
|
|
|||
|
//
|
|||
|
// Truncate the file allocation down to zero
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Delete File allocation\n", 0);
|
|||
|
|
|||
|
FatTruncateFileAllocation( IrpContext, Fcb, 0 );
|
|||
|
|
|||
|
//
|
|||
|
// Tunnel and remove the dirent for the directory
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Delete the directory dirent\n", 0);
|
|||
|
|
|||
|
FatTunnelFcbOrDcb( Fcb, NULL );
|
|||
|
|
|||
|
FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Report that we have removed an entry.
|
|||
|
//
|
|||
|
|
|||
|
FatNotifyReportChange( IrpContext,
|
|||
|
Vcb,
|
|||
|
Fcb,
|
|||
|
FILE_NOTIFY_CHANGE_DIR_NAME,
|
|||
|
FILE_ACTION_REMOVED );
|
|||
|
}
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remove the entry from the name table.
|
|||
|
// This will ensure that
|
|||
|
// we will not collide with the Dcb if the user wants
|
|||
|
// to recreate the same file over again before we
|
|||
|
// get a close irp.
|
|||
|
//
|
|||
|
|
|||
|
FatRemoveNames( IrpContext, Fcb );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the unclean count.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( Fcb->UncleanCount != 0 );
|
|||
|
Fcb->UncleanCount -= 1;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case UserFileOpen:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup UserFileOpen\n", 0);
|
|||
|
|
|||
|
ShareAccess = &Fcb->ShareAccess;
|
|||
|
|
|||
|
//
|
|||
|
// Determine here if we should do a delayed close.
|
|||
|
//
|
|||
|
|
|||
|
if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) &&
|
|||
|
(FileObject->SectionObjectPointer->ImageSectionObject == NULL) &&
|
|||
|
(Fcb->UncleanCount == 1) &&
|
|||
|
(Fcb->OpenCount == 1) &&
|
|||
|
!FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
|
|||
|
Fcb->FcbCondition == FcbGood) {
|
|||
|
|
|||
|
//
|
|||
|
// Delay our close.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock all outstanding file locks.
|
|||
|
//
|
|||
|
|
|||
|
(VOID) FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock,
|
|||
|
FileObject,
|
|||
|
IoGetRequestorProcess( Irp ),
|
|||
|
NULL );
|
|||
|
|
|||
|
//
|
|||
|
// Check if we should be changing the time or file size and set
|
|||
|
// the archive bit on the file.
|
|||
|
//
|
|||
|
|
|||
|
KeQuerySystemTime( &CurrentTime );
|
|||
|
|
|||
|
//
|
|||
|
// Note that we HAVE to use BooleanFlagOn() here because
|
|||
|
// FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte).
|
|||
|
//
|
|||
|
|
|||
|
UpdateFileSize = BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED);
|
|||
|
|
|||
|
SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
|
|||
|
|
|||
|
UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
|
|||
|
!FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE);
|
|||
|
|
|||
|
//
|
|||
|
// Do one further check here of access time. Only update it if
|
|||
|
// the current version is at least one day old. We know that
|
|||
|
// the current Fcb-LastAccessTime corresponds to 12 midnight local
|
|||
|
// time, so just see if the current time is on the same day.
|
|||
|
//
|
|||
|
// Also, we don't update LastAccessData on write protected
|
|||
|
// media.
|
|||
|
//
|
|||
|
|
|||
|
if (FatData.ChicagoMode &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
|
|||
|
(UpdateLastWriteTime ||
|
|||
|
(FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ) &&
|
|||
|
!FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)))) {
|
|||
|
|
|||
|
LARGE_INTEGER LastAccessDay;
|
|||
|
|
|||
|
ExSystemTimeToLocalTime( &Fcb->LastAccessTime, &LastAccessDay );
|
|||
|
ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay );
|
|||
|
|
|||
|
LastAccessDay.QuadPart /= FatOneDay.QuadPart;
|
|||
|
CurrentDay.QuadPart /= FatOneDay.QuadPart;
|
|||
|
|
|||
|
if (LastAccessDay.LowPart != CurrentDay.LowPart) {
|
|||
|
|
|||
|
UpdateLastAccessTime = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
UpdateLastAccessTime = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
UpdateLastAccessTime = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if ((UpdateFileSize || SetArchiveBit ||
|
|||
|
UpdateLastWriteTime || UpdateLastAccessTime) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
PDIRENT Dirent;
|
|||
|
PBCB DirentBcb = NULL;
|
|||
|
ULONG NotifyFilter = 0;
|
|||
|
FAT_TIME_STAMP CurrentFatTime;
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Update Time and/or file size on File\n", 0);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Get the dirent
|
|||
|
//
|
|||
|
|
|||
|
FatGetDirentFromFcbOrDcb( IrpContext,
|
|||
|
Fcb,
|
|||
|
&Dirent,
|
|||
|
&DirentBcb );
|
|||
|
|
|||
|
if (UpdateLastWriteTime || UpdateLastAccessTime) {
|
|||
|
|
|||
|
(VOID)FatNtTimeToFatTime( IrpContext,
|
|||
|
&CurrentTime,
|
|||
|
TRUE,
|
|||
|
&CurrentFatTime,
|
|||
|
NULL );
|
|||
|
}
|
|||
|
|
|||
|
if (SetArchiveBit) {
|
|||
|
|
|||
|
Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE;
|
|||
|
Fcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE;
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateLastWriteTime) {
|
|||
|
|
|||
|
//
|
|||
|
// And update its time of last write and set the archive
|
|||
|
// bit
|
|||
|
//
|
|||
|
|
|||
|
Fcb->LastWriteTime = CurrentTime;
|
|||
|
|
|||
|
Dirent->LastWriteTime = CurrentFatTime;
|
|||
|
|
|||
|
//
|
|||
|
// We call the notify package to report that the
|
|||
|
// attribute and last modification times have both
|
|||
|
// changed.
|
|||
|
//
|
|||
|
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES
|
|||
|
| FILE_NOTIFY_CHANGE_LAST_WRITE;
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateLastAccessTime) {
|
|||
|
|
|||
|
//
|
|||
|
// Now we have to truncate the local time down
|
|||
|
// to the current day, then convert back to UTC.
|
|||
|
//
|
|||
|
|
|||
|
Fcb->LastAccessTime.QuadPart =
|
|||
|
CurrentDay.QuadPart * FatOneDay.QuadPart;
|
|||
|
|
|||
|
ExLocalTimeToSystemTime( &Fcb->LastAccessTime,
|
|||
|
&Fcb->LastAccessTime );
|
|||
|
|
|||
|
Dirent->LastAccessDate = CurrentFatTime.Date;
|
|||
|
|
|||
|
//
|
|||
|
// We call the notify package to report that the
|
|||
|
// last access time has changed.
|
|||
|
//
|
|||
|
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
|||
|
}
|
|||
|
|
|||
|
if (UpdateFileSize) {
|
|||
|
|
|||
|
//
|
|||
|
// Update the dirent file size
|
|||
|
//
|
|||
|
|
|||
|
Dirent->FileSize = Fcb->Header.FileSize.LowPart;
|
|||
|
|
|||
|
//
|
|||
|
// We call the notify package to report that the
|
|||
|
// size has changed.
|
|||
|
//
|
|||
|
|
|||
|
NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE;
|
|||
|
}
|
|||
|
|
|||
|
FatNotifyReportChange( IrpContext,
|
|||
|
Vcb,
|
|||
|
Fcb,
|
|||
|
NotifyFilter,
|
|||
|
FILE_ACTION_MODIFIED );
|
|||
|
|
|||
|
//
|
|||
|
// If all we did was update last access time,
|
|||
|
// don't mark the volume dirty.
|
|||
|
//
|
|||
|
|
|||
|
FatSetDirtyBcb( IrpContext,
|
|||
|
DirentBcb,
|
|||
|
NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ?
|
|||
|
NULL : Vcb );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the file has a unclean count of 1 then we know
|
|||
|
// that this is the last handle for the file object.
|
|||
|
// Set the truncate size pointer to null this will only
|
|||
|
// be reset to something else if we actually need to
|
|||
|
// truncate the file allocation.
|
|||
|
//
|
|||
|
|
|||
|
TruncateSize = NULL;
|
|||
|
|
|||
|
if ( (Fcb->UncleanCount == 1) && (Fcb->FcbCondition != FcbBad) ) {
|
|||
|
|
|||
|
DELETE_CONTEXT DeleteContext;
|
|||
|
|
|||
|
//
|
|||
|
// Check if we should be deleting the file. The
|
|||
|
// delete operation really deletes the file but
|
|||
|
// keeps the Fcb around for close to do away with.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
//
|
|||
|
// Before truncating file allocation remember this
|
|||
|
// info for FatDeleteDirent.
|
|||
|
//
|
|||
|
|
|||
|
DeleteContext.FileSize = Fcb->Header.FileSize.LowPart;
|
|||
|
DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile;
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Delete File allocation\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Synchronize here with paging IO
|
|||
|
//
|
|||
|
|
|||
|
(VOID)ExAcquireResourceExclusive( Fcb->Header.PagingIoResource,
|
|||
|
TRUE );
|
|||
|
|
|||
|
Fcb->Header.FileSize.LowPart = 0;
|
|||
|
Fcb->Header.ValidDataLength.LowPart = 0;
|
|||
|
Fcb->ValidDataToDisk = 0;
|
|||
|
|
|||
|
ExReleaseResource( Fcb->Header.PagingIoResource );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We must zero between ValidDataLength and FileSize
|
|||
|
//
|
|||
|
|
|||
|
if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) &&
|
|||
|
(Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) {
|
|||
|
|
|||
|
ULONG ValidDataLength;
|
|||
|
|
|||
|
ValidDataLength = Fcb->Header.ValidDataLength.LowPart;
|
|||
|
|
|||
|
if (ValidDataLength < Fcb->ValidDataToDisk) {
|
|||
|
ValidDataLength = Fcb->ValidDataToDisk;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
(VOID)FatZeroData( IrpContext,
|
|||
|
Vcb,
|
|||
|
FileObject,
|
|||
|
ValidDataLength,
|
|||
|
Fcb->Header.FileSize.LowPart -
|
|||
|
ValidDataLength );
|
|||
|
|
|||
|
//
|
|||
|
// Since we just zeroed this, we can now bump
|
|||
|
// up VDL in the Fcb.
|
|||
|
//
|
|||
|
|
|||
|
Fcb->ValidDataToDisk =
|
|||
|
Fcb->Header.ValidDataLength.LowPart =
|
|||
|
Fcb->Header.FileSize.LowPart;
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See if we are supposed to truncate the file on the last
|
|||
|
// close. If we cannot wait we'll ship this off to the fsp
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
if (FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "truncate file allocation\n", 0);
|
|||
|
|
|||
|
if (Vcb->VcbCondition == VcbGood) {
|
|||
|
|
|||
|
FatTruncateFileAllocation( IrpContext,
|
|||
|
Fcb,
|
|||
|
Fcb->Header.FileSize.LowPart );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We also have to get rid of the Cache Map because
|
|||
|
// this is the only way we have of trashing the
|
|||
|
// truncated pages.
|
|||
|
//
|
|||
|
|
|||
|
LocalTruncateSize = Fcb->Header.FileSize;
|
|||
|
TruncateSize = &LocalTruncateSize;
|
|||
|
|
|||
|
//
|
|||
|
// Mark the Fcb as having now been truncated, just incase
|
|||
|
// we have to reship this off to the fsp.
|
|||
|
//
|
|||
|
|
|||
|
Fcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now check again if we are to delete the file and if
|
|||
|
// so then we remove the file from the disk.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Delete File\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Now tunnel and delete the dirent
|
|||
|
//
|
|||
|
|
|||
|
FatTunnelFcbOrDcb( Fcb, Ccb );
|
|||
|
|
|||
|
FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Report that we have removed an entry.
|
|||
|
//
|
|||
|
|
|||
|
FatNotifyReportChange( IrpContext,
|
|||
|
Vcb,
|
|||
|
Fcb,
|
|||
|
FILE_NOTIFY_CHANGE_FILE_NAME,
|
|||
|
FILE_ACTION_REMOVED );
|
|||
|
}
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
}
|
|||
|
|
|||
|
if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the entry from the splay table. This will
|
|||
|
// ensure that we will not collide with the Fcb if the
|
|||
|
// user wants to recreate the same file over again
|
|||
|
// before we get a close irp.
|
|||
|
//
|
|||
|
// Note that we remove the name even if we couldn't
|
|||
|
// truncate the allocation and remove the dirent above.
|
|||
|
//
|
|||
|
|
|||
|
FatRemoveNames( IrpContext, Fcb );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( Fcb->FcbCondition == FcbBad ) {
|
|||
|
|
|||
|
TruncateSize = &FatLargeZero;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We've just finished everything associated with an unclean
|
|||
|
// fcb so now decrement the unclean count before releasing
|
|||
|
// the resource.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( Fcb->UncleanCount != 0 );
|
|||
|
Fcb->UncleanCount -= 1;
|
|||
|
if (!FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED )) {
|
|||
|
ASSERT( Fcb->NonCachedUncleanCount != 0 );
|
|||
|
Fcb->NonCachedUncleanCount -= 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this was the last cached open, and there are open
|
|||
|
// non-cached handles, attempt a flush and purge operation
|
|||
|
// to avoid cache coherency overhead from these non-cached
|
|||
|
// handles later. We ignore any I/O errors from the flush.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED ) &&
|
|||
|
(Fcb->NonCachedUncleanCount != 0) &&
|
|||
|
(Fcb->NonCachedUncleanCount == Fcb->UncleanCount) &&
|
|||
|
(Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) {
|
|||
|
|
|||
|
CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL );
|
|||
|
CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// cleanup the cache map
|
|||
|
//
|
|||
|
|
|||
|
CcUninitializeCacheMap( FileObject, TruncateSize, NULL );
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
FatBugCheck( TypeOfOpen, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We must clean up the share access at this time, since we may not
|
|||
|
// get a Close call for awhile if the file was mapped through this
|
|||
|
// File Object.
|
|||
|
//
|
|||
|
|
|||
|
if (ShareAccess != NULL) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Cleanup the Share access\n", 0);
|
|||
|
IoRemoveShareAccess( FileObject, ShareAccess );
|
|||
|
}
|
|||
|
|
|||
|
if (TypeOfOpen == UserFileOpen) {
|
|||
|
|
|||
|
//
|
|||
|
// Coordinate the cleanup operation with the oplock state.
|
|||
|
// Cleanup operations can always cleanup immediately.
|
|||
|
//
|
|||
|
|
|||
|
FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock,
|
|||
|
Irp,
|
|||
|
IrpContext,
|
|||
|
NULL,
|
|||
|
NULL );
|
|||
|
|
|||
|
Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// First set the FO_CLEANUP_COMPLETE flag.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Now unpin any repinned Bcbs.
|
|||
|
//
|
|||
|
|
|||
|
FatUnpinRepinnedBcbs( IrpContext );
|
|||
|
|
|||
|
//
|
|||
|
// If this was removeable media, flush the volume. We do
|
|||
|
// this in lieu of write through for removeable media for
|
|||
|
// performance considerations. That is, data is guarenteed
|
|||
|
// to be out when NtCloseFile returns.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_FLOPPY) &&
|
|||
|
FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
|
|||
|
(TypeOfOpen == UserFileOpen)) {
|
|||
|
|
|||
|
//
|
|||
|
// Flush the file.
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushFile( IrpContext, Fcb );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
DebugUnwind( FatCommonCleanup );
|
|||
|
|
|||
|
if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); }
|
|||
|
if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); }
|
|||
|
|
|||
|
//
|
|||
|
// If this is a normal termination then complete the request
|
|||
|
//
|
|||
|
|
|||
|
if (!AbnormalTermination()) {
|
|||
|
|
|||
|
FatCompleteRequest( IrpContext, Irp, Status );
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatCommonCleanup -> %08lx\n", Status);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
FatAutoUnlock (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb
|
|||
|
)
|
|||
|
{
|
|||
|
KIRQL SavedIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the volume.
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireVpbSpinLock( &SavedIrql );
|
|||
|
|
|||
|
ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED );
|
|||
|
|
|||
|
Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED;
|
|||
|
Vcb->FileObjectWithVcbLocked = NULL;
|
|||
|
|
|||
|
IoReleaseVpbSpinLock( SavedIrql );
|
|||
|
}
|