summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/cleanup.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/cleanup.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/cntfs/cleanup.c')
-rw-r--r--private/ntos/cntfs/cleanup.c2129
1 files changed, 2129 insertions, 0 deletions
diff --git a/private/ntos/cntfs/cleanup.c b/private/ntos/cntfs/cleanup.c
new file mode 100644
index 000000000..9cf876c7d
--- /dev/null
+++ b/private/ntos/cntfs/cleanup.c
@@ -0,0 +1,2129 @@
+/*++
+
+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_
+
+