2130 lines
74 KiB
C
2130 lines
74 KiB
C
/*++
|
||
|
||
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_
|
||
|
||
|