NT4/private/ntos/cntfs/cleanup.c
2020-09-30 17:12:29 +02:00

2130 lines
74 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
Cleanup.c
Abstract:
This module implements the File Cleanup routine for Ntfs called by the
dispatch driver.
Author:
Your Name [Email] dd-Mon-Year
Revision History:
--*/
#include "NtfsProc.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (NTFS_BUG_CHECK_CLEANUP)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_CLEANUP)
VOID
NtfsContractQuotaToFileSize (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtfsCommonCleanup)
#pragma alloc_text(PAGE, NtfsFsdCleanup)
#pragma alloc_text(PAGE, NtfsContractQuotaToFileSize)
#endif
NTSTATUS
NtfsFsdCleanup (
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of Cleanup.
Arguments:
VolumeDeviceObject - Supplies the volume device object where the
file exists
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The FSD status for the IRP
--*/
{
TOP_LEVEL_CONTEXT TopLevelContext;
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
NTSTATUS Status = STATUS_SUCCESS;
PIRP_CONTEXT IrpContext = NULL;
ULONG LogFileFullCount = 0;
ASSERT_IRP( Irp );
PAGED_CODE();
//
// 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, ("NtfsFsdCleanup\n") );
//
// Call the common Cleanup routine
//
FsRtlEnterFileSystem();
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE );
//
// Do the following in a loop to catch the log file full and cant wait
// calls.
//
do {
try {
//
// We are either initiating this request or retrying it.
//
if (IrpContext == NULL) {
IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) );
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
} else if (Status == STATUS_LOG_FILE_FULL) {
NtfsCheckpointForLogFileFull( IrpContext );
if (++LogFileFullCount >= 2) {
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
}
}
Status = NtfsCommonCleanup( IrpContext, Irp );
break;
} except(NtfsExceptionFilter( 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 = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
}
} while (Status == STATUS_CANT_WAIT ||
Status == STATUS_LOG_FILE_FULL);
if (ThreadTopLevelContext == &TopLevelContext) {
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
}
FsRtlExitFileSystem();
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsFsdCleanup -> %08lx\n", Status) );
return Status;
}
NTSTATUS
NtfsCommonCleanup (
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp
)
/*++
Routine Description:
This is the common routine for Cleanup called by both the fsd and fsp
threads.
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;
PSCB Scb;
PCCB Ccb;
PLCB Lcb;
PLCB LcbForUpdate;
PLCB LcbForCounts;
PSCB ParentScb = NULL;
PFCB ParentFcb = NULL;
PLCB ThisLcb;
PSCB ThisScb;
ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
PLONGLONG TruncateSize = NULL;
LONGLONG LocalTruncateSize;
BOOLEAN DeleteFile = FALSE;
BOOLEAN DeleteStream = FALSE;
BOOLEAN OpenById;
BOOLEAN RemoveLink;
BOOLEAN AcquiredParentScb = FALSE;
BOOLEAN AcquiredScb = FALSE;
BOOLEAN CleanupAttrContext = FALSE;
BOOLEAN UpdateDuplicateInfo = FALSE;
BOOLEAN AddToDelayQueue = TRUE;
USHORT TotalLinkAdj = 0;
PLIST_ENTRY Links;
NAME_PAIR NamePair;
ASSERT_IRP_CONTEXT( IrpContext );
ASSERT_IRP( Irp );
PAGED_CODE();
NtfsInitializeNamePair(&NamePair);
//
// Get the current Irp stack location
//
IrpSp = IoGetCurrentIrpStackLocation( Irp );
DebugTrace( +1, Dbg, ("NtfsCommonCleanup\n") );
DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
//
// Extract and decode the file object
//
FileObject = IrpSp->FileObject;
TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
Status = STATUS_SUCCESS;
//
// Special case the unopened file object and stream files.
//
if ((TypeOfOpen == UnopenedFileObject) ||
(TypeOfOpen == StreamFileOpen)) {
//
// Just set the FO_CLEANUP_COMPLETE flag, and get outsky...
//
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
//
// Theoretically we should never hit this case. It means an app
// tried to close a handle he didn't open (call NtClose with a handle
// value that happens to be in the handle table). It is safe to
// simply return SUCCESS in this case.
//
// Trigger an assert so we can find the bad app though.
//
ASSERT( TypeOfOpen != StreamFileOpen );
NtfsCompleteRequest( &IrpContext, &Irp, Status );
DebugTrace( -1, Dbg, ("NtfsCommonCleanup -> %08lx\n", Status) );
return Status;
}
//
// Let's make sure we can wait.
//
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
Status = NtfsPostRequest( IrpContext, Irp );
DebugTrace( -1, Dbg, ("NtfsCommonCleanup -> %08lx\n", Status) );
return Status;
}
//
// Remember if this is an open by file Id open.
//
OpenById = BooleanFlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID );
//
// Acquire exclusive access to the Vcb and enqueue the irp if we didn't
// get access
//
if (TypeOfOpen == UserVolumeOpen) {
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX );
}
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_VCB_EX )) {
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
} else {
NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
}
//
// Use a try-finally to facilitate cleanup.
//
try {
LcbForUpdate = LcbForCounts = Lcb = Ccb->Lcb;
if (Lcb != NULL) {
ParentScb = Lcb->Scb;
if (ParentScb != NULL) {
ParentFcb = ParentScb->Fcb;
}
}
//
// Acquire Paging I/O first, since we may be deleting or truncating.
// Testing for the PagingIoResource is not really safe without
// holding the main resource, so we correct for that below.
//
if (Fcb->PagingIoResource != NULL) {
NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
NtfsAcquireExclusiveScb( IrpContext, Scb );
} else {
NtfsAcquireExclusiveScb( IrpContext, Scb );
//
// If we now do not see a paging I/O resource we are golden,
// othewise we can absolutely release and acquire the resources
// safely in the right order, since a resource in the Fcb is
// not going to go away.
//
if (Fcb->PagingIoResource != NULL) {
NtfsReleaseScb( IrpContext, Scb );
NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
NtfsAcquireExclusiveScb( IrpContext, Scb );
}
}
AcquiredScb = TRUE;
//
// Update the Lcb/Scb to reflect the case where this opener had
// specified delete on close.
//
if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE )) {
if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
BOOLEAN LastLink;
BOOLEAN NonEmptyIndex;
//
// It is ok to get rid of this guy. All we need to do is
// mark this Lcb for delete and decrement the link count
// in the Fcb. If this is a primary link, then we
// indicate that the primary link has been deleted.
//
if (!LcbLinkIsDeleted( Lcb ) &&
(!IsDirectory( &Fcb->Info ) ||
NtfsIsLinkDeleteable( IrpContext, Fcb, &NonEmptyIndex, &LastLink))) {
if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
SetFlag( Fcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED );
}
Fcb->LinkCount -= 1;
SetFlag( Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
//
// Call into the notify package to close any handles on
// a directory being deleted.
//
if (IsDirectory( &Fcb->Info )) {
FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
&Vcb->DirNotifyList,
FileObject->FsContext,
NULL,
FALSE,
FALSE,
0,
NULL,
NULL,
NULL );
}
}
//
// Otherwise we are simply removing the attribute.
//
} else {
SetFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
}
//
// Clear the flag so we will ignore it in the log file full case.
//
ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
}
//
// If we are going to try and delete something, anything, knock the file
// size and valid data down to zero. Then update the snapshot
// so that the sizes will be zero even if the operation fails.
//
// If we're deleting the file, go through all of the Scb's.
//
if ((Fcb->CleanupCount == 1) &&
(Fcb->LinkCount == 0)) {
DeleteFile = TRUE;
NtfsFreeSnapshotsForFcb( IrpContext, Scb->Fcb );
for (Links = Fcb->ScbQueue.Flink;
Links != &Fcb->ScbQueue;
Links = Links->Flink) {
ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
//
// Set the Scb sizes to zero except for the attribute list.
//
if (ThisScb->AttributeTypeCode != $ATTRIBUTE_LIST) {
ThisScb->Header.FileSize =
ThisScb->Header.ValidDataLength = Li0;
}
if (FlagOn( ThisScb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
NtfsSnapshotScb( IrpContext, ThisScb );
}
}
//
// Otherwise we may only be deleting this stream.
//
} else if ((Scb->CleanupCount == 1) &&
FlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE )) {
DeleteStream = TRUE;
Scb->Header.FileSize =
Scb->Header.ValidDataLength = Li0;
NtfsFreeSnapshotsForFcb( IrpContext, Scb->Fcb );
if (FlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
NtfsSnapshotScb( IrpContext, Scb );
}
}
//
// Let's do a sanity check.
//
ASSERT( Fcb->CleanupCount != 0 );
ASSERT( Scb->CleanupCount != 0 );
//
// If the cleanup count on the file will go to zero and there is
// a large security descriptor and we haven't exceeded the security
// creation count for this Fcb then dereference and possibly deallocate
// the security descriptor for the Fcb. This is to prevent us from
// holding onto pool while waiting for closes to come in.
//
if ((Fcb->CleanupCount == 1) &&
(Fcb->SharedSecurity != NULL) &&
(Fcb->CreateSecurityCount < FCB_CREATE_SECURITY_COUNT) &&
(GetSharedSecurityLength( Fcb->SharedSecurity ) > FCB_LARGE_ACL_SIZE)) {
NtfsAcquireFcbSecurity( Fcb->Vcb );
NtfsDereferenceSharedSecurity( Fcb );
NtfsReleaseFcbSecurity( Fcb->Vcb );
}
//
// Case on the type of open that we are trying to cleanup.
//
switch (TypeOfOpen) {
case UserVolumeOpen :
DebugTrace( 0, Dbg, ("Cleanup on user volume\n") );
//
// First set the FO_CLEANUP_COMPLETE flag.
//
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
//
// For a volume open, we check if this open locked the volume.
// All the other work is done in common code below.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED ) &&
((Vcb->FileObjectWithVcbLocked == FileObject) ||
((ULONG)Vcb->FileObjectWithVcbLocked == ((ULONG)FileObject)+1))) {
if ((ULONG)Vcb->FileObjectWithVcbLocked == ((ULONG)FileObject)+1) {
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
//
// Purge the volume for the autocheck case.
//
} else if (FlagOn( FileObject->Flags, FO_FILE_MODIFIED )) {
//
// Drop the Scb for the volume Dasd around this call.
//
NtfsReleaseScb( IrpContext, Scb );
AcquiredScb = FALSE;
NtfsFlushVolume( IrpContext, Vcb, FALSE, TRUE, TRUE, FALSE );
NtfsAcquireExclusiveScb( IrpContext, Scb );
AcquiredScb = TRUE;
//
// If this is not the boot partition then dismount the Vcb.
//
if ((Vcb->CleanupCount == 1) &&
((Vcb->CloseCount - Vcb->SystemFileCloseCount) == 1)) {
NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE );
}
}
ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_EXPLICIT_LOCK );
Vcb->FileObjectWithVcbLocked = NULL;
#ifdef _CAIRO_
//
// If the quota tracking has been requested and the quotas
// need to be repaired then try to repair them now.
//
if (FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED) &&
FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE |
QUOTA_FLAG_CORRUPT |
QUOTA_FLAG_PENDING_DELETES)) {
NtfsPostRepairQuotaIndex( IrpContext, Vcb );
}
#endif // _CAIRO_
}
break;
case UserDirectoryOpen :
DebugTrace( 0, Dbg, ("Cleanup on user directory/file\n") );
NtfsSnapshotScb( IrpContext, Scb );
//
// Capture any changes to the time stamps for this file.
//
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
//
// Now set the FO_CLEANUP_COMPLETE flag.
//
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
//
// To perform cleanup on a directory, we first complete any
// Irps watching from this directory. If we are deleting the
// file then we remove all prefix entries for all the Lcb's going
// into this directory and delete the file. We then report to
// dir notify that this file is going away.
//
//
// Complete any Notify Irps on this file handle.
//
if (FlagOn( Ccb->Flags, CCB_FLAG_DIR_NOTIFY )) {
FsRtlNotifyCleanup( Vcb->NotifySync, &Vcb->DirNotifyList, Ccb );
ClearFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
InterlockedDecrement( &Vcb->NotifyCount );
}
//
// When cleaning up a user directory, we always remove the
// share access and modify the file counts. If the Fcb
// has been marked as delete on close and this is the last
// open file handle, we remove the file from the Mft and
// remove it from it's parent index entry.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
(NodeType( Scb ) == NTFS_NTC_SCB_INDEX)) {
if (DeleteFile) {
ASSERT( (Lcb == NULL) ||
(LcbLinkIsDeleted( Lcb ) && Lcb->CleanupCount == 1 ));
//
// If we don't have an Lcb and there is one on the Fcb then
// let's use it.
//
if ((Lcb == NULL) && !IsListEmpty( &Fcb->LcbQueue )) {
Lcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
ParentScb = Lcb->Scb;
if (ParentScb != NULL) {
ParentFcb = ParentScb->Fcb;
}
}
//
// Now acquire the Parent Scb exclusive while still holding
// the Vcb, to avoid deadlocks. The Parent Scb is required
// since we will be deleting index entries in it.
//
if (ParentScb != NULL) {
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
AcquiredParentScb = TRUE;
}
try {
NtfsDeleteFile( IrpContext, Fcb, ParentScb, NULL);
TotalLinkAdj += 1;
//
// Remove all tunneling entries for this directory
//
FsRtlDeleteKeyFromTunnelCache(&Vcb->Tunnel, *(PULONGLONG)&Fcb->FileReference);
if (ParentFcb != NULL) {
NtfsUpdateFcb( ParentFcb );
}
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
if (!OpenById && (Vcb->NotifyCount != 0)) {
NtfsReportDirNotify( IrpContext,
Vcb,
&Ccb->FullFileName,
Ccb->LastFileNameOffset,
NULL,
((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
Ccb->Lcb != NULL &&
Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
&Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
NULL),
FILE_NOTIFY_CHANGE_DIR_NAME,
FILE_ACTION_REMOVED,
ParentFcb );
}
SetFlag( Fcb->FcbState, FCB_STATE_FILE_DELETED );
//
// We need to mark all of the links on the file as gone.
// If there is a parent Scb then it will be the parent
// for all of the links.
//
for (Links = Fcb->LcbQueue.Flink;
Links != &Fcb->LcbQueue;
Links = Links->Flink) {
ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
//
// Remove all remaining prefixes on this link.
//
NtfsRemovePrefix( ThisLcb );
SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
//
// We don't need to report any changes on this link.
//
ThisLcb->InfoFlags = 0;
}
//
// We need to mark all of the Scbs as gone.
//
for (Links = Fcb->ScbQueue.Flink;
Links != &Fcb->ScbQueue;
Links = Links->Flink) {
ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
ClearFlag( Scb->ScbState,
SCB_STATE_NOTIFY_ADD_STREAM |
SCB_STATE_NOTIFY_REMOVE_STREAM |
SCB_STATE_NOTIFY_RESIZE_STREAM |
SCB_STATE_NOTIFY_MODIFY_STREAM );
if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
NtfsSnapshotScb( IrpContext, ThisScb );
ThisScb->ValidDataToDisk =
ThisScb->Header.AllocationSize.QuadPart =
ThisScb->Header.FileSize.QuadPart =
ThisScb->Header.ValidDataLength.QuadPart = 0;
SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
}
}
//
// We certainly don't need to any on disk update for this
// file now.
//
Fcb->InfoFlags = 0;
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
ClearFlag( Ccb->Flags,
CCB_FLAG_USER_SET_LAST_MOD_TIME |
CCB_FLAG_USER_SET_LAST_CHANGE_TIME |
CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
AddToDelayQueue = FALSE;
}
} else {
AddToDelayQueue = FALSE;
}
//
// Determine if we should put this on the delayed close list.
// The following must be true.
//
// - This is not the root directory
// - This directory is not about to be deleted
// - This is the last handle and last file object for this
// directory.
// - There are no other file objects on this file.
// - We are not currently reducing the delayed close queue.
//
NtfsAcquireFsrtlHeader( Scb );
if (AddToDelayQueue &&
!FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE ) &&
(NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount) &&
(Fcb->CloseCount == 1)) {
SetFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
} else {
ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
}
NtfsReleaseFsrtlHeader( Scb );
break;
case UserFileOpen :
#ifdef _CAIRO_
case UserPropertySetOpen :
#endif // _CAIRO_
DebugTrace( 0, Dbg, ("Cleanup on user file\n") );
//
// If the Scb is uninitialized, we read it from the disk.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
try {
NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
}
NtfsSnapshotScb( IrpContext, Scb );
//
// Coordinate the cleanup operation with the oplock state.
// Cleanup operations can always cleanup immediately.
//
FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
Irp,
IrpContext,
NULL,
NULL );
//
// In this case, we have to unlock all the outstanding file
// locks, update the time stamps for the file and sizes for
// this attribute, and set the archive bit if necessary.
//
if (Scb->ScbType.Data.FileLock != NULL) {
(VOID) FsRtlFastUnlockAll( Scb->ScbType.Data.FileLock,
FileObject,
IoGetRequestorProcess( Irp ),
NULL );
}
//
// Update the FastIoField.
//
NtfsAcquireFsrtlHeader( Scb );
Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
NtfsReleaseFsrtlHeader( Scb );
//
// If the Fcb is in valid shape, we check on the cases where we delete
// the file or attribute.
//
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// Capture any changes to the time stamps for this file.
//
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
//
// Now set the FO_CLEANUP_COMPLETE flag.
//
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
//
// We are checking here for special actions we take when
// we have the last user handle on a link and the link has
// been marked for delete. We could either be removing the
// file or removing a link.
//
if ((Lcb == NULL) || (LcbLinkIsDeleted( Lcb ) && (Lcb->CleanupCount == 1))) {
if (DeleteFile) {
//
// If we don't have an Lcb and the Fcb has some entries then
// grab one of these to do the update.
//
if (Lcb == NULL) {
for (Links = Fcb->LcbQueue.Flink;
Links != &Fcb->LcbQueue;
Links = Links->Flink) {
ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
LCB,
FcbLinks );
if (!FlagOn( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE )) {
Lcb = ThisLcb;
ParentScb = Lcb->Scb;
if (ParentScb != NULL) {
ParentFcb = ParentScb->Fcb;
}
break;
}
}
}
// Now acquire the Parent Scb exclusive while still holding
// the Vcb, to avoid deadlocks. The Parent Scb is required
// since we will be deleting index entries in it.
//
if (ParentScb != NULL) {
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
AcquiredParentScb = TRUE;
}
try {
AddToDelayQueue = FALSE;
NtfsDeleteFile( IrpContext, Fcb, ParentScb, &NamePair );
TotalLinkAdj += 1;
//
// Stash property information in the tunnel if the object was
// opened by name, has a parent directory caller was treating it
// as a non-POSIX object and we had an good, active link
//
if (!OpenById &&
ParentScb &&
Ccb->Lcb &&
!FlagOn(FileObject->Flags, FO_OPENED_CASE_SENSITIVE)) {
FsRtlAddToTunnelCache( &Vcb->Tunnel,
*(PULONGLONG)&ParentScb->Fcb->FileReference,
&NamePair.Short,
&NamePair.Long,
BooleanFlagOn(Ccb->Lcb->FileNameAttr->Flags, FILE_NAME_DOS),
sizeof(LONGLONG),
&Fcb->Info.CreationTime);
}
if (ParentFcb != NULL) {
NtfsUpdateFcb( ParentFcb );
}
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
if (!OpenById && (Vcb->NotifyCount != 0)) {
NtfsReportDirNotify( IrpContext,
Vcb,
&Ccb->FullFileName,
Ccb->LastFileNameOffset,
NULL,
((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
Ccb->Lcb != NULL &&
Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
&Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
NULL),
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_REMOVED,
ParentFcb );
}
SetFlag( Fcb->FcbState, FCB_STATE_FILE_DELETED );
//
// We need to mark all of the links on the file as gone.
//
for (Links = Fcb->LcbQueue.Flink;
Links != &Fcb->LcbQueue;
Links = Links->Flink) {
ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
if (ThisLcb->Scb == ParentScb) {
//
// Remove all remaining prefixes on this link.
//
NtfsRemovePrefix( ThisLcb );
SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
//
// We don't need to report any changes on this link.
//
ThisLcb->InfoFlags = 0;
}
}
//
// We need to mark all of the Scbs as gone.
//
for (Links = Fcb->ScbQueue.Flink;
Links != &Fcb->ScbQueue;
Links = Links->Flink) {
ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
ClearFlag( Scb->ScbState,
SCB_STATE_NOTIFY_ADD_STREAM |
SCB_STATE_NOTIFY_REMOVE_STREAM |
SCB_STATE_NOTIFY_RESIZE_STREAM |
SCB_STATE_NOTIFY_MODIFY_STREAM );
if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
NtfsSnapshotScb( IrpContext, ThisScb );
ThisScb->ValidDataToDisk =
ThisScb->Header.AllocationSize.QuadPart =
ThisScb->Header.FileSize.QuadPart =
ThisScb->Header.ValidDataLength.QuadPart = 0;
SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
}
}
//
// We certainly don't need to any on disk update for this
// file now.
//
Fcb->InfoFlags = 0;
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
ClearFlag( Ccb->Flags,
CCB_FLAG_USER_SET_LAST_MOD_TIME |
CCB_FLAG_USER_SET_LAST_CHANGE_TIME |
CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
//
// We will truncate the attribute to size 0.
//
TruncateSize = (PLONGLONG)&Li0;
//
// Now we want to check for the last user's handle on a
// link (or the last handle on a Ntfs/8.3 pair). In this
// case we want to remove the links from the disk.
//
} else if (Lcb != NULL) {
ThisLcb = NULL;
RemoveLink = TRUE;
if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS ) &&
(Lcb->FileNameAttr->Flags != (FILE_NAME_NTFS | FILE_NAME_DOS))) {
//
// Walk through all the links looking for a link
// with a flag set which is not the same as the
// link we already have.
//
for (Links = Fcb->LcbQueue.Flink;
Links != &Fcb->LcbQueue;
Links = Links->Flink) {
ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
//
// If this has a flag set and is not the Lcb
// for this cleanup, then we check if there
// are no Ccb's left for this.
//
if (FlagOn( ThisLcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )
&&
(ThisLcb != Lcb)) {
if (ThisLcb->CleanupCount != 0) {
RemoveLink = FALSE;
}
break;
}
ThisLcb = NULL;
}
}
//
// If we are to remove the link, we do so now. This removes
// the filename attributes and the entries in the parent
// indexes for this link. In addition, we mark the links
// as having been removed and decrement the number of links
// left on the file.
//
if (RemoveLink) {
NtfsAcquireExclusiveScb( IrpContext, ParentScb );
AcquiredParentScb = TRUE;
try {
AddToDelayQueue = FALSE;
NtfsRemoveLink( IrpContext,
Fcb,
ParentScb,
Lcb->ExactCaseLink.LinkName,
&NamePair );
//
// Stash property information in the tunnel if caller opened the
// object by name and was treating it as a non-POSIX object
//
if (!OpenById && !FlagOn(FileObject->Flags, FO_OPENED_CASE_SENSITIVE)) {
FsRtlAddToTunnelCache( &Vcb->Tunnel,
*(PULONGLONG)&ParentScb->Fcb->FileReference,
&NamePair.Short,
&NamePair.Long,
BooleanFlagOn(Lcb->FileNameAttr->Flags, FILE_NAME_DOS),
sizeof(LONGLONG),
&Fcb->Info.CreationTime);
}
TotalLinkAdj += 1;
NtfsUpdateFcb( ParentFcb );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
if (!OpenById && (Vcb->NotifyCount != 0)) {
NtfsReportDirNotify( IrpContext,
Vcb,
&Ccb->FullFileName,
Ccb->LastFileNameOffset,
NULL,
((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
Ccb->Lcb != NULL &&
Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
&Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
NULL),
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_REMOVED,
ParentFcb );
}
//
// Remove all remaining prefixes on this link.
//
NtfsRemovePrefix( Lcb );
//
// Mark the links as being removed.
//
SetFlag( Lcb->LcbState, LCB_STATE_LINK_IS_GONE );
if (ThisLcb != NULL) {
//
// Remove all remaining prefixes on this link.
//
NtfsRemovePrefix( ThisLcb );
SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
ThisLcb->InfoFlags = 0;
}
//
// Since the link is gone we don't want to update the
// duplicate information for this link.
//
Lcb->InfoFlags = 0;
LcbForUpdate = NULL;
//
// Update the time stamps for removing the link. Clear the
// FO_CLEANUP_COMPLETE flag around this call so the time
// stamp change is not nooped.
//
SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
ClearFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
}
}
}
//
// If the file/attribute is not going away, we update the
// attribute size now rather than waiting for the Lazy
// Writer to catch up. If the cleanup count isn't 1 then
// defer the following actions.
//
if ((Scb->CleanupCount == 1) && (Fcb->LinkCount != 0)) {
//
// We may also have to delete this attribute only.
//
if (DeleteStream) {
ClearFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
try {
//
// Delete the attribute only.
//
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
NtfsInitializeAttributeContext( &AttrContext );
CleanupAttrContext = TRUE;
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
do {
NtfsDeleteAttributeRecord( IrpContext, Fcb, TRUE, FALSE, &AttrContext );
} while (NtfsLookupNextAttributeForScb( IrpContext, Scb, &AttrContext ));
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
SetFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
}
//
// Set the Scb flag to indicate that the attribute is
// gone.
//
Scb->ValidDataToDisk =
Scb->Header.AllocationSize.QuadPart =
Scb->Header.FileSize.QuadPart =
Scb->Header.ValidDataLength.QuadPart = 0;
SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
SetFlag( Scb->ScbState, SCB_STATE_NOTIFY_REMOVE_STREAM );
ClearFlag( Scb->ScbState,
SCB_STATE_NOTIFY_RESIZE_STREAM |
SCB_STATE_NOTIFY_MODIFY_STREAM |
SCB_STATE_NOTIFY_ADD_STREAM );
//
// Update the time stamps for removing the link. Clear the
// FO_CLEANUP_COMPLETE flag around this call so the time
// stamp change is not nooped.
//
SetFlag( Ccb->Flags,
CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE );
ClearFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
TruncateSize = (PLONGLONG)&Li0;
//
// Check if we're to modify the allocation size or file size.
//
} else {
if (FlagOn( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
//
// Acquire the parent now so we enforce our locking
// rules that the Mft Scb must be acquired after
// the normal file resources.
//
NtfsPrepareForUpdateDuplicate( IrpContext,
Fcb,
&LcbForUpdate,
&ParentScb,
TRUE );
ClearFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
//
// For the non-resident streams we will write the file
// size to disk.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// Setting AdvanceOnly to FALSE guarantees we will not
// incorrectly advance the valid data size.
//
try {
NtfsWriteFileSizes( IrpContext,
Scb,
&Scb->Header.ValidDataLength.QuadPart,
FALSE,
TRUE );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
}
//
// For resident streams we will write the correct size to
// the resident attribute.
//
} else {
//
// We need to lookup the attribute and change
// the attribute value. We can point to
// the attribute itself as the changing
// value.
//
NtfsInitializeAttributeContext( &AttrContext );
CleanupAttrContext = TRUE;
try {
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
NtfsChangeAttributeValue( IrpContext,
Fcb,
Scb->Header.FileSize.LowPart,
NULL,
0,
TRUE,
TRUE,
FALSE,
FALSE,
&AttrContext );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
}
//
// Verify the allocation size is now correct.
//
if (QuadAlign( Scb->Header.FileSize.LowPart ) != Scb->Header.AllocationSize.LowPart) {
Scb->Header.AllocationSize.LowPart = QuadAlign(Scb->Header.FileSize.LowPart);
}
}
//
// Update the size change to the Fcb.
//
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
}
#ifdef _CAIRO_
if (NtfsPerformQuotaOperation( Fcb )) {
if ( FlagOn( Scb->ScbState, SCB_STATE_QUOTA_ENLARGED )) {
ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
ASSERT( FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA ));
//
// Acquire the parent now so we enforce our locking
// rules that the Mft Scb must be acquired after
// the normal file resources.
//
NtfsPrepareForUpdateDuplicate( IrpContext,
Fcb,
&LcbForUpdate,
&ParentScb,
TRUE );
NtfsContractQuotaToFileSize( IrpContext, Scb );
}
SetFlag( IrpContext->Flags,
IRP_CONTEXT_FLAG_QUOTA_DISABLE );
}
#endif // _CAIRO_
if (FlagOn( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE )) {
//
// Acquire the parent now so we enforce our locking
// rules that the Mft Scb must be acquired after
// the normal file resources.
//
NtfsPrepareForUpdateDuplicate( IrpContext,
Fcb,
&LcbForUpdate,
&ParentScb,
TRUE );
ClearFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
//
// We have two cases:
//
// Resident: We are looking for the case where the
// valid data length is less than the file size.
// In this case we shrink the attribute.
//
// NonResident: We are looking for unused clusters
// past the end of the file.
//
// We skip the following if we had any previous errors.
//
if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
//
// We don't need to truncate if the file size is 0.
//
if (Scb->Header.AllocationSize.QuadPart != 0) {
VCN StartingCluster;
VCN EndingCluster;
//
// **** Do we need to give up the Vcb for this
// call.
//
StartingCluster = LlClustersFromBytes( Vcb, Scb->Header.FileSize.QuadPart );
EndingCluster = LlClustersFromBytes( Vcb, Scb->Header.AllocationSize.QuadPart );
//
// If there are clusters to delete, we do so now.
//
if (EndingCluster != StartingCluster) {
try {
NtfsDeleteAllocation( IrpContext,
FileObject,
Scb,
StartingCluster,
MAXLONGLONG,
TRUE,
TRUE );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
}
}
LocalTruncateSize = Scb->Header.FileSize.QuadPart;
TruncateSize = &LocalTruncateSize;
}
//
// This is the resident case.
//
} else {
//
// Check if the file size length is less than
// the allocated size.
//
if (QuadAlign( Scb->Header.FileSize.LowPart ) < Scb->Header.AllocationSize.LowPart) {
//
// We need to lookup the attribute and change
// the attribute value. We can point to
// the attribute itself as the changing
// value.
//
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
NtfsInitializeAttributeContext( &AttrContext );
CleanupAttrContext = TRUE;
try {
NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
NtfsChangeAttributeValue( IrpContext,
Fcb,
Scb->Header.FileSize.LowPart,
NULL,
0,
TRUE,
TRUE,
FALSE,
FALSE,
&AttrContext );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
}
//
// Remember the smaller allocation size
//
Scb->Header.AllocationSize.LowPart = QuadAlign(Scb->Header.FileSize.LowPart);
Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
}
}
NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
}
}
}
//
// 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
// except for CANT_WAIT and LOG_FILE_FULL.
//
if (!FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
(Scb->NonCachedCleanupCount != 0) &&
(Scb->CleanupCount == (Scb->NonCachedCleanupCount + 1)) &&
(Scb->CompressionUnit == 0) &&
(Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
(Scb->NonpagedScb->SegmentObject.ImageSectionObject == NULL) &&
MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject, NULL )) {
//
// Flush and purge the stream.
//
NtfsFlushAndPurgeScb( IrpContext,
Scb,
NULL );
//
// Ignore any errors in this path.
//
IrpContext->ExceptionStatus = STATUS_SUCCESS;
}
if (AddToDelayQueue &&
!FlagOn( Scb->ScbState, SCB_STATE_DELAY_CLOSE ) &&
NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount &&
Fcb->CloseCount == 1) {
SetFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
} else {
ClearFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
}
//
// If the Fcb is bad, we will truncate the cache to size zero.
//
} else {
//
// Now set the FO_CLEANUP_COMPLETE flag.
//
SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
TruncateSize = (PLONGLONG)&Li0;
}
break;
default :
NtfsBugCheck( TypeOfOpen, 0, 0 );
}
//
// If any of the Fcb Info flags are set we call the routine
// to update the duplicated information in the parent directories.
// We need to check here in case none of the flags are set but
// we want to update last access time.
//
if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
} else if (!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
NtfsCheckLastAccess( IrpContext, Fcb );
}
}
//
// We check if we have to the standard information attribute.
// We can only update attributes on mounted volumes.
//
if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO ) &&
(Status == STATUS_SUCCESS) &&
!FlagOn( Scb->ScbState, SCB_STATE_VOLUME_DISMOUNTED )) {
ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
ASSERT( TypeOfOpen != UserVolumeOpen );
try {
NtfsUpdateStandardInformation( IrpContext, Fcb );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
}
//
// Now update the duplicate information as well for volumes that are still mounted.
//
if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
//
// We shouldn't try to write the duplicate info to a dismounted volume.
//
UpdateDuplicateInfo = FALSE;
} else if (FlagOn( Fcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS ) ||
((LcbForUpdate != NULL) &&
FlagOn( LcbForUpdate->InfoFlags, FCB_INFO_DUPLICATE_FLAGS ))) {
ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &LcbForUpdate, &ParentScb, TRUE );
//
// Now update the duplicate info.
//
try {
NtfsUpdateDuplicateInfo( IrpContext, Fcb, LcbForUpdate, ParentScb );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
UpdateDuplicateInfo = TRUE;
}
//
// If we have modified the Info structure or security, we report this
// to the dir-notify package (except for OpenById cases).
//
if (!OpenById) {
ULONG FilterMatch;
//
// Check whether we need to report on file changes.
//
if ((Vcb->NotifyCount != 0) &&
(UpdateDuplicateInfo || FlagOn( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY ))) {
//
// We map the Fcb info flags into the dir notify flags.
//
FilterMatch = NtfsBuildDirNotifyFilter( IrpContext,
(Fcb->InfoFlags |
(LcbForUpdate ? LcbForUpdate->InfoFlags : 0) ));
//
// If the filter match is non-zero, that means we also need to do a
// dir notify call.
//
if (FilterMatch != 0) {
NtfsReportDirNotify( IrpContext,
Vcb,
&Ccb->FullFileName,
Ccb->LastFileNameOffset,
NULL,
((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
Ccb->Lcb != NULL &&
Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
&Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
NULL),
FilterMatch,
FILE_ACTION_MODIFIED,
ParentFcb );
}
}
ClearFlag( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY );
//
// If this is a named stream with changes then report them as well.
//
if ((Scb->AttributeName.Length != 0) &&
NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
if ((Vcb->NotifyCount != 0) &&
FlagOn( Scb->ScbState,
SCB_STATE_NOTIFY_REMOVE_STREAM |
SCB_STATE_NOTIFY_RESIZE_STREAM |
SCB_STATE_NOTIFY_MODIFY_STREAM )) {
ULONG Action;
FilterMatch = 0;
//
// Start by checking for a delete.
//
if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_REMOVE_STREAM )) {
FilterMatch = FILE_NOTIFY_CHANGE_STREAM_NAME;
Action = FILE_ACTION_REMOVED_STREAM;
} else {
//
// Check if the file size changed.
//
if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_RESIZE_STREAM )) {
FilterMatch = FILE_NOTIFY_CHANGE_STREAM_SIZE;
}
//
// Now check if the stream data was modified.
//
if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_MODIFY_STREAM )) {
SetFlag( FilterMatch, FILE_NOTIFY_CHANGE_STREAM_WRITE );
}
Action = FILE_ACTION_MODIFIED_STREAM;
}
NtfsReportDirNotify( IrpContext,
Vcb,
&Ccb->FullFileName,
Ccb->LastFileNameOffset,
&Scb->AttributeName,
((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
Ccb->Lcb != NULL &&
Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Buffer != NULL) ?
&Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
NULL),
FilterMatch,
Action,
ParentFcb );
}
ClearFlag( Scb->ScbState,
SCB_STATE_NOTIFY_ADD_STREAM |
SCB_STATE_NOTIFY_REMOVE_STREAM |
SCB_STATE_NOTIFY_RESIZE_STREAM |
SCB_STATE_NOTIFY_MODIFY_STREAM );
}
}
if (UpdateDuplicateInfo) {
NtfsUpdateLcbDuplicateInfo( Fcb, LcbForUpdate );
Fcb->InfoFlags = 0;
}
//
// Always clear the update standard information flag.
//
ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
//
// Let's give up the parent Fcb if we have acquired it. This will
// prevent deadlocks in any uninitialize code below.
//
if (AcquiredParentScb) {
NtfsReleaseScb( IrpContext, ParentScb );
AcquiredParentScb = FALSE;
}
//
// Uninitialize the cache map if this file has been cached or we are
// trying to delete.
//
if ((FileObject->PrivateCacheMap != NULL) || (TruncateSize != NULL)) {
CcUninitializeCacheMap( FileObject, (PLARGE_INTEGER)TruncateSize, NULL );
}
//
// Check that the non-cached handle count is consistent.
//
ASSERT( !FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) ||
(Scb->NonCachedCleanupCount != 0 ));
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
CleanupAttrContext = FALSE;
}
//
// Now decrement the cleanup counts.
//
NtfsDecrementCleanupCounts( Scb,
LcbForCounts,
BooleanFlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ));
//
// We remove the share access from the Scb.
//
IoRemoveShareAccess( FileObject, &Scb->ShareAccess );
//
// Modify the delete counts in the Fcb.
//
if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_FILE )) {
Fcb->FcbDeleteFile -= 1;
ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_FILE );
}
if (FlagOn( Ccb->Flags, CCB_FLAG_DENY_DELETE )) {
Fcb->FcbDenyDelete -= 1;
ClearFlag( Ccb->Flags, CCB_FLAG_DENY_DELETE );
}
//
// Since this request has completed we can adjust the total link count
// in the Fcb.
//
Fcb->TotalLinks -= TotalLinkAdj;
#ifdef _CAIRO_
//
// Release the quota control block. This does not have to be done
// here however, it allows us to free up the quota control block
// before the fcb is removed from the table. This keeps the assert
// about quota table empty from triggering in
// NtfsClearAndVerifyQuotaIndex.
//
if (NtfsPerformQuotaOperation(Fcb) &&
FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
NtfsDereferenceQuotaControlBlock( Vcb,
&Fcb->QuotaControl );
}
#endif // _CAIRO_
} finally {
DebugUnwind( NtfsCommonCleanup );
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE );
//
// Release any resources held.
//
NtfsReleaseVcb( IrpContext, Vcb );
//
// We clear the file object pointer in the Ccb.
// This prevents us from trying to access this in a
// rename operation.
//
SetFlag( Ccb->Flags, CCB_FLAG_CLEANUP );
if (AcquiredScb) {
NtfsReleaseScb( IrpContext, Scb );
}
if (CleanupAttrContext) {
NtfsCleanupAttributeContext( &AttrContext );
}
if (NamePair.Long.Buffer != NamePair.LongBuffer) {
NtfsFreePool(NamePair.Long.Buffer);
}
if (!AbnormalTermination()) {
NtfsCompleteRequest( &IrpContext, &Irp, Status );
}
//
// And return to our caller
//
DebugTrace( -1, Dbg, ("NtfsCommonCleanup -> %08lx\n", Status) );
}
return Status;
}
#ifdef _CAIRO_
VOID
NtfsContractQuotaToFileSize (
IN PIRP_CONTEXT IrpContext,
IN PSCB Scb
)
/*++
Routine Description:
This routine converts the quota charged for a stream from allocation size
to file size. This should only be called for cleanup.
Arguments:
Scb - Supplies a pointer to the being changed.
Return Value:
None.
--*/
{
LONGLONG Delta;
NTSTATUS Status;
PAGED_CODE();
ASSERT( IrpContext->MajorFunction == IRP_MJ_CLEANUP );
ASSERT(!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_QUOTA_DISABLE ));
try {
ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
ASSERT( FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA ));
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
Delta = (LONG) Scb->Header.FileSize.LowPart -
NtfsResidentStreamQuota( Scb->Vcb );
} else {
Delta = Scb->Header.FileSize.QuadPart -
Scb->Header.AllocationSize.QuadPart;
}
if (Delta != 0) {
NtfsUpdateFileQuota( IrpContext,
Scb->Fcb,
&Delta,
TRUE,
FALSE );
}
ClearFlag( Scb->ScbState, SCB_STATE_QUOTA_ENLARGED );
} except( (((Status = GetExceptionCode()) == STATUS_LOG_FILE_FULL) ||
(Status == STATUS_CANT_WAIT) ||
!FsRtlIsNtstatusExpected( Status ))
? EXCEPTION_CONTINUE_SEARCH
: EXCEPTION_EXECUTE_HANDLER ) {
NOTHING;
}
}
#endif // _CAIRO_