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 );
|
||
}
|