Beispiel #1
0
NTSTATUS
FatCommonClose (
    IN PVCB Vcb,
    IN PFCB Fcb,
    IN PCCB Ccb,
    IN TYPE_OF_OPEN TypeOfOpen,
    IN BOOLEAN Wait,
    IN BOOLEAN TopLevel,
    OUT PBOOLEAN VcbDeleted OPTIONAL
    )

/*++

Routine Description:

    This is the common routine for closing a file/directory called by both
    the fsd and fsp threads.

    Close is invoked whenever the last reference to a file object is deleted.
    Cleanup is invoked when the last handle to a file object is closed, and
    is called before close.

    The function of close is to completely tear down and remove the fcb/dcb/ccb
    structures associated with the file object.

Arguments:

    Fcb - Supplies the file to process.

    Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE
        then we must try to acquire the Vcb anyway.

    TopLevel - If this is TRUE this is a top level request.

    VcbDeleted - Returns whether the VCB was deleted by this call.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PDCB ParentDcb;
    BOOLEAN RecursiveClose;
    BOOLEAN LocalVcbDeleted;
    IRP_CONTEXT IrpContext;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);

    //
    //  Initailize the callers variable, if needed.
    //

    LocalVcbDeleted = FALSE;

    if (ARGUMENT_PRESENT( VcbDeleted )) {

        *VcbDeleted = LocalVcbDeleted;
    }

    //
    //  Special case the unopened file object
    //

    if (TypeOfOpen == UnopenedFileObject) {

        DebugTrace(0, Dbg, "Close unopened file object\n", 0);

        Status = STATUS_SUCCESS;

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
        return Status;
    }

    //
    //  Set up our stack IrpContext.
    //

    RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );

    IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT;
    IrpContext.NodeByteSize = sizeof( IrpContext );
    IrpContext.MajorFunction = IRP_MJ_CLOSE;
    IrpContext.Vcb = Vcb;
    
    if (Wait) {

        SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
    }

    //
    //  Acquire exclusive access to the Vcb and enqueue the irp if we didn't
    //  get access.
    //

#pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" )
    if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {

        return STATUS_PENDING;
    }

    //
    //  The following test makes sure that we don't blow away an Fcb if we
    //  are trying to do a Supersede/Overwrite open above us.  This test
    //  does not apply for the EA file.
    //

    if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) &&
        Vcb->EaFcb != Fcb) {

        ExReleaseResourceLite( &Vcb->Resource );

        return STATUS_PENDING;
    }

    //
    //  Setting the following flag prevents recursive closes of directory file
    //  objects, which are handled in a special case loop.
    //

    if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {

        RecursiveClose = TRUE;

    } else {


        SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
        RecursiveClose = FALSE;

        //
        //  Since we are at the top of the close chain, we need to add
        //  a reference to the VCB.  This will keep it from going away
        //  on us until we are ready to check for a dismount below.
        //

        Vcb->OpenFileCount += 1;
    }

    try {

        //
        //  Case on the type of open that we are trying to close.
        //

        switch (TypeOfOpen) {

        case VirtualVolumeFile:

            DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
            InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case UserVolumeOpen:

            DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);

            Vcb->DirectAccessOpenCount -= 1;
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }

            FatDeleteCcb( &IrpContext, &Ccb );

            try_return( Status = STATUS_SUCCESS );
            break;

        case EaFile:

            DebugTrace(0, Dbg, "Close EaFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );
            InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case DirectoryFile:

            DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);

            InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount );

            //
            //  Remove this internal open from the count.
            //

            InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) );

            //
            //  If this is the root directory, it is a residual open
            //  as well.
            //

            if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) {

                InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) );
            }

            //
            //  If this is a recursive close, just return here.
            //

            if ( RecursiveClose ) {

                try_return( Status = STATUS_SUCCESS );

            } else {

                break;
            }

        case UserDirectoryOpen:
        case UserFileOpen:

            DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);

            //
            //  Uninitialize the cache map if we no longer need to use it
            //

            if ((NodeType(Fcb) == FAT_NTC_DCB) &&
                IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) &&
                (Fcb->OpenCount == 1) &&
                (Fcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                //
                //  Dereference the directory file.  This may cause a close
                //  Irp to be processed, so we need to do this before we destory
                //  the Fcb.
                //

                Fcb->Specific.Dcb.DirectoryFile = NULL;
                ObDereferenceObject( DirectoryFileObject );
            }

            Fcb->OpenCount -= 1;            
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
           
            FatDeleteCcb( &IrpContext, &Ccb );

            break;

        default:

#pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." )
            FatBugCheck( TypeOfOpen, 0, 0 );
        }

        //
        //  At this point we've cleaned up any on-disk structure that needs
        //  to be done, and we can now update the in-memory structures.
        //  Now if this is an unreferenced FCB or if it is
        //  an unreferenced DCB (not the root) then we can remove
        //  the fcb and set our ParentDcb to non null.
        //

        if (((NodeType(Fcb) == FAT_NTC_FCB) &&
             (Fcb->OpenCount == 0))

                ||

             ((NodeType(Fcb) == FAT_NTC_DCB) &&
              (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) &&
              (Fcb->OpenCount == 0) &&
              (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {

            ParentDcb = Fcb->ParentDcb;

            SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

            FatDeleteFcb( &IrpContext, &Fcb );

            //
            //  Uninitialize our parent's cache map if we no longer need
            //  to use it.
            //

            while ((NodeType(ParentDcb) == FAT_NTC_DCB) &&
                   IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) &&
                   (ParentDcb->OpenCount == 0) &&
                   (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject;

                DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                ParentDcb->Specific.Dcb.DirectoryFile = NULL;

                ObDereferenceObject( DirectoryFileObject );

                //
                //  Now, if the ObDereferenceObject() caused the final close
                //  to come in, then blow away the Fcb and continue up,
                //  otherwise wait for Mm to to dereference its file objects
                //  and stop here..
                //

                if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {

                    PDCB CurrentDcb;

                    CurrentDcb = ParentDcb;
                    ParentDcb = CurrentDcb->ParentDcb;

                    SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

                    FatDeleteFcb( &IrpContext, &CurrentDcb );

                } else {

                    break;
                }
            }
        }

        Status = STATUS_SUCCESS;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCommonClose );

        //
        //  We are done processing the close.  If we are the top of the close
        //  chain, see if the VCB can go away.  We have biased the open count by
        //  one, so we need to take that into account.
        //

        if (!RecursiveClose) {

            //
            //  See if there is only one open left.  If so, it is ours.  We only want
            //  to check for a dismount if a dismount is not already in progress.
            //  We also only do this if the Vcb condition is not VcbGood and the 
            //  caller can handle the VCB going away. This is determined by whether
            //  they passed in the VcbDeleted argument. This request also needs
            //  to be top level.
            //

            if (Vcb->OpenFileCount == 1 &&
                Vcb->VcbCondition != VcbGood &&
                !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) &&
                ARGUMENT_PRESENT( VcbDeleted ) &&
                TopLevel) {

                //
                //  We need the global lock, which must be acquired before the
                //  VCB.  Since we already have the VCB, we have to drop and
                //  reaquire here.  Note that we always want to wait from this
                //  point on.  Note that the VCB cannot go away, since we have
                //  biased the open file count.
                //

                FatReleaseVcb( &IrpContext,
                               Vcb );

                SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );

#pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" )
                FatAcquireExclusiveGlobal( &IrpContext );

                FatAcquireExclusiveVcb( &IrpContext,
                                        Vcb );

                //
                //  We have our locks in the correct order.  Remove our
                //  extra open and check for a dismount.  Note that if
                //  something changed while we dropped the lock, it will
                //  not matter, since the dismount code does the correct
                //  checks to make sure the volume can really go away.
                //

                Vcb->OpenFileCount -= 1;

                LocalVcbDeleted = FatCheckForDismount( &IrpContext,
                                                       Vcb,
                                                       FALSE );

                FatReleaseGlobal( &IrpContext );

                //
                //  Let the caller know what happened, if they want this information.
                //

                if (ARGUMENT_PRESENT( VcbDeleted )) {

                    *VcbDeleted = LocalVcbDeleted;
                }

            } else {

                //
                //  The volume cannot go away now.  Just remove our extra reference.
                //

                Vcb->OpenFileCount -= 1;
            }

            //
            //  If the VCB is still around, clear our recursion flag.
            //

            if (!LocalVcbDeleted) {

                ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS );
            }
        }

        //
        //  Only release the VCB if it did not go away.
        //

        if (!LocalVcbDeleted) {

            FatReleaseVcb( &IrpContext, Vcb );
        }

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
    }

    return Status;
}
Beispiel #2
0
NTSTATUS
FatCommonClose (
    IN PVCB Vcb,
    IN PFCB Fcb,
    IN PCCB Ccb,
    IN TYPE_OF_OPEN TypeOfOpen,
    IN BOOLEAN Wait,
    OUT PBOOLEAN VcbDeleted OPTIONAL
    )

/*++

Routine Description:

    This is the common routine for closing a file/directory called by both
    the fsd and fsp threads.

    Close is invoked whenever the last reference to a file object is deleted.
    Cleanup is invoked when the last handle to a file object is closed, and
    is called before close.

    The function of close is to completely tear down and remove the fcb/dcb/ccb
    structures associated with the file object.

Arguments:

    Fcb - Supplies the file to process.

    Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE
        then we must try to acquire the Vcb anyway.

    VcbDeleted - Returns whether the VCB was deleted by this call.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PDCB ParentDcb;
    BOOLEAN RecursiveClose;
    BOOLEAN LocalVcbDeleted;
    IRP_CONTEXT IrpContext;

#if __NDAS_FAT_SECONDARY__

	BOOLEAN						volDoResourceAcquired = FALSE;

	BOOLEAN						send2Primary = FALSE;
	BOOLEAN						volDoCcb = FALSE;

	_U64						primaryFileHandle;
	PVOLUME_DEVICE_OBJECT		volDo = CONTAINING_RECORD( Vcb, VOLUME_DEVICE_OBJECT, Vcb );
	BOOLEAN						volDoSessionResourceAcquired = FALSE;

	PSECONDARY_REQUEST			secondaryRequest = NULL;

	PNDFS_REQUEST_HEADER		ndfsRequestHeader;
	PNDFS_WINXP_REQUEST_HEADER	ndfsWinxpRequestHeader;
	PNDFS_WINXP_REPLY_HEADER	ndfsWinxpReplytHeader;

	LARGE_INTEGER				timeOut;

#endif

    PAGED_CODE();

    DebugTrace(+1, Dbg, "FatCommonClose...\n", 0);

    //
    //  Initailize the callers variable, if needed.
    //

    LocalVcbDeleted = FALSE;

    if (ARGUMENT_PRESENT( VcbDeleted )) {

        *VcbDeleted = LocalVcbDeleted;
    }

    //
    //  Special case the unopened file object
    //

    if (TypeOfOpen == UnopenedFileObject) {

        DebugTrace(0, Dbg, "Close unopened file object\n", 0);

        Status = STATUS_SUCCESS;

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
        return Status;
    }

    //
    //  Set up our stack IrpContext.
    //

    RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );

    IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT;
    IrpContext.NodeByteSize = sizeof( IrpContext );
    IrpContext.MajorFunction = IRP_MJ_CLOSE;
    IrpContext.Vcb = Vcb;
    
    if (Wait) {

        SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
    }

#if __NDAS_FAT_SECONDARY__

	if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY))
		SetFlag( IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT );

	if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) {

		if (!FlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT)) {

			return STATUS_PENDING;
		}

		volDoResourceAcquired = 
			SecondaryAcquireResourceSharedStarveExclusiveLite( &IrpContext, 
															   &volDo->Resource, 
															   BooleanFlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT) );

		if (volDoResourceAcquired == FALSE) {

			ASSERT( FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ); // It's not always garented						continue;
			return STATUS_PENDING;							
		}
	}

	if (volDo->NetdiskEnableMode == NETDISK_SECONDARY &&
		!FlagOn(volDo->NetdiskPartitionInformation.Flags, NETDISK_PARTITION_INFORMATION_FLAG_INDIRECT)) {

		if (TypeOfOpen == VirtualVolumeFile || TypeOfOpen == DirectoryFile) {

			SetFlag( IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT );
		}
	}

#endif

    //
    //  Acquire exclusive access to the Vcb and enqueue the irp if we didn't
    //  get access.
    //
#if __NDAS_FAT_SECONDARY__

	if (!(FlagOn(IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT) ?  
		  ExAcquireResourceExclusiveLite( &Vcb->SecondaryResource, Wait ) : ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait ))) {

		if (volDoResourceAcquired) {

			ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) );
			SecondaryReleaseResourceLite( NULL, &volDo->Resource );
		}

        return STATUS_PENDING;
    }

#else

    if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) {

        return STATUS_PENDING;
    }

#endif

    //
    //  The following test makes sure that we don't blow away an Fcb if we
    //  are trying to do a Supersede/Overwrite open above us.  This test
    //  does not apply for the EA file.
    //

    if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) &&
        Vcb->EaFcb != Fcb) {

#if __NDAS_FAT_SECONDARY__

	 	if (FlagOn(IrpContext.NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT))  
			ExReleaseResourceLite( &Vcb->SecondaryResource );
		else
			ExReleaseResourceLite( &Vcb->Resource );

		if (volDoResourceAcquired) {

			ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) );
			SecondaryReleaseResourceLite( NULL, &volDo->Resource );
		}

#else
        ExReleaseResourceLite( &Vcb->Resource );
#endif

        return STATUS_PENDING;
    }

    //
    //  Setting the following flag prevents recursive closes of directory file
    //  objects, which are handled in a special case loop.
    //

    if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) {

        RecursiveClose = TRUE;

    } else {

        SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
        RecursiveClose = FALSE;

        //
        //  Since we are at the top of the close chain, we need to add
        //  a reference to the VCB.  This will keep it from going away
        //  on us until we are ready to check for a dismount below.
        //

        Vcb->OpenFileCount += 1;
    }

    try {

        //
        //  Case on the type of open that we are trying to close.
        //

        switch (TypeOfOpen) {

        case VirtualVolumeFile:

            DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( &(Vcb->InternalOpenCount) );
            InterlockedDecrement( &(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case UserVolumeOpen:

            DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0);

#if __NDAS_FAT_SECONDARY__

			if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) {

				volDoCcb = TRUE;

				if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) {

					if ( FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_CORRUPTED) )
						Fcb->CorruptedCcbCloseCount --;

					send2Primary = FALSE;
			
				} else
					send2Primary = TRUE;
				
				primaryFileHandle = Ccb->PrimaryFileHandle;

				ExAcquireFastMutex( &volDo->Secondary->RecoveryCcbQMutex );
				RemoveEntryList( &Ccb->ListEntry );
				ExReleaseFastMutex( &volDo->Secondary->RecoveryCcbQMutex );

				InitializeListHead( &Ccb->ListEntry );

				Ccb->FileObject = NULL;
				if (Ccb->Buffer)
					ExFreePool( Ccb->Buffer );

				Ccb->FileObject = NULL;

				InterlockedDecrement( &Vcb->SecondaryOpenFileCount );
			
			} else {

				Vcb->DirectAccessOpenCount -= 1;
				Vcb->OpenFileCount -= 1;
				if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
			}

			if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_OPEN_BY_PRIMARY_SESSION)) {
				
				InterlockedDecrement( &Vcb->PrimaryOpenFileCount );					
			}

			FatDeleteCcb( &IrpContext, &Ccb );

			if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) {

				InterlockedDecrement( &Fcb->OpenCount );
				if (Fcb->OpenCount == 0) {

					ExAcquireFastMutex( &volDo->Secondary->FcbQMutex );
					RemoveEntryList( &Fcb->ListEntry );
					InitializeListHead( &Fcb->ListEntry );
					ExReleaseFastMutex( &volDo->Secondary->FcbQMutex );

					Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
					FatDeleteFcb( &IrpContext, &Fcb );
					Secondary_Dereference( volDo->Secondary );
				}
			
			} else {

				try_return( Status = STATUS_SUCCESS );
			}
#else
            Vcb->DirectAccessOpenCount -= 1;
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }

            FatDeleteCcb( &IrpContext, &Ccb );

            try_return( Status = STATUS_SUCCESS );
#endif

            break;

        case EaFile:

            DebugTrace(0, Dbg, "Close EaFile\n", 0);

            //
            //  Remove this internal, residual open from the count.
            //

            InterlockedDecrement( &(Vcb->InternalOpenCount) );
            InterlockedDecrement( &(Vcb->ResidualOpenCount) );

            try_return( Status = STATUS_SUCCESS );
            break;

        case DirectoryFile:

            DebugTrace(0, Dbg, "Close DirectoryFile\n", 0);

            InterlockedDecrement( &Fcb->Specific.Dcb.DirectoryFileOpenCount );

            //
            //  Remove this internal open from the count.
            //

            InterlockedDecrement( &(Vcb->InternalOpenCount) );

            //
            //  If this is the root directory, it is a residual open
            //  as well.
            //

            if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) {

                InterlockedDecrement( &(Vcb->ResidualOpenCount) );
            }

            //
            //  If this is a recursive close, just return here.
            //

            if ( RecursiveClose ) {

                try_return( Status = STATUS_SUCCESS );

            } else {

                break;
            }

        case UserDirectoryOpen:
        case UserFileOpen:

            DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0);

            //
            //  Uninitialize the cache map if we no longer need to use it
            //

            if ((NodeType(Fcb) == FAT_NTC_DCB) &&
                IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) &&
                (Fcb->OpenCount == 1) &&
                (Fcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                //
                //  Dereference the directory file.  This may cause a close
                //  Irp to be processed, so we need to do this before we destory
                //  the Fcb.
                //

                Fcb->Specific.Dcb.DirectoryFile = NULL;
                ObDereferenceObject( DirectoryFileObject );
            }

#if __NDAS_FAT__
			if (TypeOfOpen == UserFileOpen) {

				ExAcquireFastMutex( &Fcb->NonPaged->CcbQMutex );
				Ccb->FileObject = NULL;
				Ccb->Fcb = NULL;
				RemoveEntryList( &Ccb->FcbListEntry );
				InitializeListHead( &Ccb->FcbListEntry );
				ExReleaseFastMutex( &Fcb->NonPaged->CcbQMutex );
			}
#endif

            Fcb->OpenCount -= 1;

#if __NDAS_FAT_SECONDARY__

			if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) {

				volDoCcb = TRUE;

				if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) {

					if ( FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_CORRUPTED) )
						Fcb->CorruptedCcbCloseCount --;

					send2Primary = FALSE;
			
				} else
					send2Primary = TRUE;
				
				primaryFileHandle = Ccb->PrimaryFileHandle;

				ExAcquireFastMutex( &volDo->Secondary->RecoveryCcbQMutex );
				RemoveEntryList( &Ccb->ListEntry );
				ExReleaseFastMutex( &volDo->Secondary->RecoveryCcbQMutex );

				InitializeListHead( &Ccb->ListEntry );

				Ccb->FileObject = NULL;
				if (Ccb->Buffer)
					ExFreePool( Ccb->Buffer );

				InterlockedDecrement( &Vcb->SecondaryOpenFileCount );

				Ccb->FileObject = NULL;
			
			} else {

				Vcb->OpenFileCount -= 1;
				if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }
			}

			if (FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_OPEN_BY_PRIMARY_SESSION)) {

				InterlockedDecrement( &Vcb->PrimaryOpenFileCount );					
			}

			FatDeleteCcb( &IrpContext, &Ccb );

			if (Fcb && FlagOn(Fcb->NdasFatFlags, ND_FAT_FCB_FLAG_SECONDARY)) {

				if (Fcb->OpenCount == 0) {

					ExAcquireFastMutex( &volDo->Secondary->FcbQMutex );
					RemoveEntryList( &Fcb->ListEntry );
					InitializeListHead( &Fcb->ListEntry );
					ExReleaseFastMutex( &volDo->Secondary->FcbQMutex );

					Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
					FatDeleteFcb( &IrpContext, &Fcb );
					Secondary_Dereference( volDo->Secondary );
				}			
			} 
#else
			
            Fcb->OpenCount -= 1;
            Vcb->OpenFileCount -= 1;
            if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; }

            FatDeleteCcb( &IrpContext, &Ccb );

#endif

            break;

        default:

            FatBugCheck( TypeOfOpen, 0, 0 );
        }

#if __NDAS_FAT_SECONDARY__

		if (send2Primary) {
		
			Status = STATUS_SUCCESS;

			volDoSessionResourceAcquired 
				= SecondaryAcquireResourceExclusiveLite( &IrpContext, 
														 &volDo->SessionResource, 
														 BooleanFlagOn(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT) );

			if (FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) || 
				FlagOn(volDo->Secondary->Flags, SECONDARY_FLAG_RECONNECTING)) {

				Status = STATUS_SUCCESS;
				leave;
			}

			secondaryRequest = AllocateWinxpSecondaryRequest( volDo->Secondary, IRP_MJ_CLOSE, 0 );

			if (secondaryRequest == NULL) {
	
				ASSERT( FALSE );
				leave;
			}

			ndfsRequestHeader = &secondaryRequest->NdfsRequestHeader;

			INITIALIZE_NDFS_REQUEST_HEADER( ndfsRequestHeader, NDFS_COMMAND_EXECUTE, volDo->Secondary, IRP_MJ_CLOSE, 0 );

			ndfsWinxpRequestHeader = (PNDFS_WINXP_REQUEST_HEADER)(ndfsRequestHeader+1);
			ASSERT( ndfsWinxpRequestHeader == (PNDFS_WINXP_REQUEST_HEADER)secondaryRequest->NdfsRequestData );
				
			//ndfsWinxpRequestHeader->IrpTag   = (_U32)Fcb;
			ndfsWinxpRequestHeader->IrpMajorFunction = IRP_MJ_CLOSE;
			ndfsWinxpRequestHeader->IrpMinorFunction = 0;

			ndfsWinxpRequestHeader->FileHandle = primaryFileHandle;

			ndfsWinxpRequestHeader->IrpFlags   = 0;
			ndfsWinxpRequestHeader->IrpSpFlags = 0;

			secondaryRequest->RequestType = SECONDARY_REQ_SEND_MESSAGE;
			QueueingSecondaryRequest( volDo->Secondary, secondaryRequest );

			timeOut.QuadPart = -NDASFAT_TIME_OUT;
			Status = KeWaitForSingleObject( &secondaryRequest->CompleteEvent, Executive, KernelMode, FALSE, &timeOut );
			
			KeClearEvent( &secondaryRequest->CompleteEvent );

			if (Status != STATUS_SUCCESS) {

				ASSERT( NDASFAT_BUG );
			}
		
			if (secondaryRequest->ExecuteStatus == STATUS_SUCCESS) {

				ndfsWinxpReplytHeader = (PNDFS_WINXP_REPLY_HEADER)secondaryRequest->NdfsReplyData;
				ASSERT( ndfsWinxpReplytHeader->Status == STATUS_SUCCESS );
				leave;
			}

			ASSERT( secondaryRequest->ExecuteStatus != STATUS_SUCCESS );
		}

		if (volDoCcb == TRUE) {

			Status = STATUS_SUCCESS;
			leave;
		}

#endif

        //
        //  At this point we've cleaned up any on-disk structure that needs
        //  to be done, and we can now update the in-memory structures.
        //  Now if this is an unreferenced FCB or if it is
        //  an unreferenced DCB (not the root) then we can remove
        //  the fcb and set our ParentDcb to non null.
        //

        if (((NodeType(Fcb) == FAT_NTC_FCB) &&
             (Fcb->OpenCount == 0))

                ||

             ((NodeType(Fcb) == FAT_NTC_DCB) &&
              (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) &&
              (Fcb->OpenCount == 0) &&
              (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) {

            ParentDcb = Fcb->ParentDcb;

            SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

            FatDeleteFcb( &IrpContext, &Fcb );

            //
            //  Uninitialize our parent's cache map if we no longer need
            //  to use it.
            //

            while ((NodeType(ParentDcb) == FAT_NTC_DCB) &&
                   IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) &&
                   (ParentDcb->OpenCount == 0) &&
                   (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject;

                DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                ParentDcb->Specific.Dcb.DirectoryFile = NULL;

                ObDereferenceObject( DirectoryFileObject );

                //
                //  Now, if the ObDereferenceObject() caused the final close
                //  to come in, then blow away the Fcb and continue up,
                //  otherwise wait for Mm to to dereference its file objects
                //  and stop here..
                //

                if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) {

                    PDCB CurrentDcb;

                    CurrentDcb = ParentDcb;
                    ParentDcb = CurrentDcb->ParentDcb;

                    SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );

                    FatDeleteFcb( &IrpContext, &CurrentDcb );

                } else {

                    break;
                }
            }
        }

        Status = STATUS_SUCCESS;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCommonClose );

#if __NDAS_FAT_SECONDARY__

		if (secondaryRequest)
			DereferenceSecondaryRequest( secondaryRequest );

		if (volDoSessionResourceAcquired) {

			SecondaryReleaseResourceLite( &IrpContext, &volDo->SessionResource );		
		}

		if (volDoResourceAcquired) {

			ASSERT( ExIsResourceAcquiredSharedLite(&volDo->Resource) );
			SecondaryReleaseResourceLite( NULL, &volDo->Resource );
		}

#endif

        //
        //  We are done processing the close.  If we are the top of the close
        //  chain, see if the VCB can go away.  We have biased the open count by
        //  one, so we need to take that into account.
        //

        if (!RecursiveClose) {

            //
            //  See if there is only one open left.  If so, it is ours.  We only want
            //  to check for a dismount if a dismount is not already in progress.
            //  We also only do this if the caller can handle the VCB going away.
            //  This is determined by whether they passed in the VcbDeleted argument.
            //

            if (Vcb->OpenFileCount == 1 &&
                !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS )
                && ARGUMENT_PRESENT( VcbDeleted )) {

                //
                //  We need the global lock, which must be acquired before the
                //  VCB.  Since we already have the VCB, we have to drop and
                //  reaquire here.  Note that we always want to wait from this
                //  point on.  Note that the VCB cannot go away, since we have
                //  biased the open file count.
                //

                FatReleaseVcb( &IrpContext,
                               Vcb );

                SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );

                FatAcquireExclusiveGlobal( &IrpContext );

                FatAcquireExclusiveVcb( &IrpContext,
                                        Vcb );

                //
                //  We have our locks in the correct order.  Remove our
                //  extra open and check for a dismount.  Note that if
                //  something changed while we dropped the lock, it will
                //  not matter, since the dismount code does the correct
                //  checks to make sure the volume can really go away.
                //

                Vcb->OpenFileCount -= 1;

                LocalVcbDeleted = FatCheckForDismount( &IrpContext,
                                                       Vcb,
                                                       FALSE );

                FatReleaseGlobal( &IrpContext );

                //
                //  Let the caller know what happened, if they want this information.
                //

                if (ARGUMENT_PRESENT( VcbDeleted )) {

                    *VcbDeleted = LocalVcbDeleted;
                }

            } else {

                //
                //  The volume cannot go away now.  Just remove our extra reference.
                //

                Vcb->OpenFileCount -= 1;
            }

            //
            //  If the VCB is still around, clear our recursion flag.
            //

            if (!LocalVcbDeleted) {

                ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS );
            }
        }

        //
        //  Only release the VCB if it did not go away.
        //

        if (!LocalVcbDeleted) {

            FatReleaseVcb( &IrpContext, Vcb );
        }

        DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status);
    }

    return Status;
}
Beispiel #3
0
NTSTATUS
NdasFatCommonFlushBuffers (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for flushing a buffer.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;

    PIO_STACK_LOCATION IrpSp;

    PFILE_OBJECT FileObject;

    TYPE_OF_OPEN TypeOfOpen;
    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    BOOLEAN VcbAcquired = FALSE;
    BOOLEAN FcbAcquired = FALSE;

    PDIRENT Dirent;
    PBCB DirentBcb = NULL;

	PVOLUME_DEVICE_OBJECT		volDo = CONTAINING_RECORD( IrpContext->Vcb, VOLUME_DEVICE_OBJECT, Vcb );
	BOOLEAN						secondarySessionResourceAcquired = FALSE;

	PSECONDARY_REQUEST			secondaryRequest = NULL;

	PNDFS_REQUEST_HEADER		ndfsRequestHeader;
	PNDFS_WINXP_REQUEST_HEADER	ndfsWinxpRequestHeader;
	PNDFS_WINXP_REPLY_HEADER	ndfsWinxpReplytHeader;

	LARGE_INTEGER				timeOut;


    PAGED_CODE();

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0);
    DebugTrace( 0, Dbg, "Irp           = %08lx\n", Irp);
    DebugTrace( 0, Dbg, "->FileObject  = %08lx\n", IrpSp->FileObject);

    //
    //  Extract and decode the file object
    //

    FileObject = IrpSp->FileObject;
    TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );

    //
    //  CcFlushCache is always synchronous, so if we can't wait enqueue
    //  the irp to the Fsp.
    //

    if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {

        Status = FatFsdPostRequest( IrpContext, Irp );

        DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status );
        return Status;
    }

    Status = STATUS_SUCCESS;

    try {

		if (!FlagOn(Ccb->NdasFatFlags, ND_FAT_CCB_FLAG_UNOPENED)) {

			do {
			
				secondarySessionResourceAcquired 
					= SecondaryAcquireResourceExclusiveLite( IrpContext, 
															 &volDo->SessionResource, 
															 BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );

				if (FlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ) {

					PrintIrp( Dbg2, "SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED", NULL, IrpContext->OriginatingIrp );
					NDASFAT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
					SetFlag( IrpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_DONT_POST_REQUEST );
					FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );	
				}

				secondaryRequest = AllocateWinxpSecondaryRequest( volDo->Secondary, IRP_MJ_FLUSH_BUFFERS, 0 );

				if (secondaryRequest == NULL) {
	
					Status = STATUS_INSUFFICIENT_RESOURCES;
					break;
				}

				ndfsRequestHeader = &secondaryRequest->NdfsRequestHeader;

				INITIALIZE_NDFS_REQUEST_HEADER( ndfsRequestHeader, NDFS_COMMAND_EXECUTE, volDo->Secondary, IRP_MJ_FLUSH_BUFFERS, 0 );

				ndfsWinxpRequestHeader = (PNDFS_WINXP_REQUEST_HEADER)(ndfsRequestHeader+1);
				ASSERT( ndfsWinxpRequestHeader == (PNDFS_WINXP_REQUEST_HEADER)secondaryRequest->NdfsRequestData );

				INITIALIZE_NDFS_WINXP_REQUEST_HEADER( ndfsWinxpRequestHeader, 
													  IrpContext->OriginatingIrp, 
													  IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp), 
													  Ccb->PrimaryFileHandle );
				
				ASSERT( !ExIsResourceAcquiredSharedLite(&IrpContext->Vcb->Resource) );	

				secondaryRequest->RequestType = SECONDARY_REQ_SEND_MESSAGE;
				QueueingSecondaryRequest( volDo->Secondary, secondaryRequest );

				timeOut.QuadPart = -NDASFAT_TIME_OUT;		
				Status = KeWaitForSingleObject( &secondaryRequest->CompleteEvent, Executive, KernelMode, FALSE, &timeOut );
			
				if (Status != STATUS_SUCCESS) {

					ASSERT( NDASFAT_BUG );
					break;
				}

				KeClearEvent (&secondaryRequest->CompleteEvent);

				if (BooleanFlagOn(volDo->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) {

					NDASFAT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
					SetFlag( IrpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_DONT_POST_REQUEST );
					FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
				}

				if (secondaryRequest->ExecuteStatus == STATUS_SUCCESS) {

					ndfsWinxpReplytHeader = (PNDFS_WINXP_REPLY_HEADER)secondaryRequest->NdfsReplyData;
					ASSERT(ndfsWinxpReplytHeader->Status == STATUS_SUCCESS);
				}

				if (secondaryRequest) {

					DereferenceSecondaryRequest( secondaryRequest );
					secondaryRequest = NULL;
				}

				if ( secondarySessionResourceAcquired == TRUE ) {
					
					SecondaryReleaseResourceLite( IrpContext, &volDo->SessionResource );		
					secondarySessionResourceAcquired = FALSE;
				}

				break;

			} while(0);
		} 

		Status = STATUS_SUCCESS;

        //
        //  Case on the type of open that we are trying to flush
        //

        switch (TypeOfOpen) {

        case VirtualVolumeFile:
        case EaFile:
        case DirectoryFile:

            DebugTrace(0, Dbg, "Flush that does nothing\n", 0);
            break;

        case UserFileOpen:

            DebugTrace(0, Dbg, "Flush User File Open\n", 0);

            (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );

            FcbAcquired = TRUE;

            FatVerifyFcb( IrpContext, Fcb );

            //
            //  If the file is cached then flush its cache
            //

            Status = FatFlushFile( IrpContext, Fcb, Flush );

            //
            //  Also update and flush the file's dirent in the parent directory if the
            //  file flush worked.
            //

            if (NT_SUCCESS( Status )) {

                //
                //  Insure that we get the filesize to disk correctly.  This is
                //  benign if it was already good.
                //
                //  (why do we need to do this?)
                //

                SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED);

#if 0
                FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
#endif                
                
                //
                //  Flush the volume file to get any allocation information
                //  updates to disk.
                //

                if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) {

                    Status = FatFlushFat( IrpContext, Vcb );

                    ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT);
                }

                //
                //  Set the write through bit so that these modifications
                //  will be completed with the request.
                //

                SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
            }

            break;

        case UserDirectoryOpen:

            //
            //  If the user had opened the root directory then we'll
            //  oblige by flushing the volume.
            //

            if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) {

                DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0);
                break;
            }

        case UserVolumeOpen:

            DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0);

            //
            //  Acquire exclusive access to the Vcb.
            //

            {
                BOOLEAN Finished;
                Finished = FatAcquireExclusiveVcb( IrpContext, Vcb );
                ASSERT( Finished );
            }

            VcbAcquired = TRUE;

            //
            //  Mark the volume clean and then flush the volume file,
            //  and then all directories
            //

            Status = FatFlushVolume( IrpContext, Vcb, Flush );

            //
            //  If the volume was dirty, do the processing that the delayed
            //  callback would have done.
            //

            if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {

                //
                //  Cancel any pending clean volumes.
                //

                (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
                (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );

                //
                //  The volume is now clean, note it.
                //

                if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {

                    FatMarkVolume( IrpContext, Vcb, VolumeClean );
                    ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
                }

                //
                //  Unlock the volume if it is removable.
                //

                if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
                    !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {

                    FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
                }
            }

            break;

        default:

            FatBugCheck( TypeOfOpen, 0, 0 );
        }

        FatUnpinBcb( IrpContext, DirentBcb );

        FatUnpinRepinnedBcbs( IrpContext );

    } finally {

        DebugUnwind( FatCommonFlushBuffers );

		if (secondaryRequest)
			DereferenceSecondaryRequest( secondaryRequest );

		if (secondarySessionResourceAcquired) {

			SecondaryReleaseResourceLite( IrpContext, &volDo->SessionResource );		
		}

        FatUnpinBcb( IrpContext, DirentBcb );

        if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }

        if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }

        //
        //  If this is a normal termination then pass the request on
        //  to the target device object.
        //

        if (!AbnormalTermination()) {

            NTSTATUS DriverStatus;
            PIO_STACK_LOCATION NextIrpSp;

            //
            //  Get the next stack location, and copy over the stack location
            //

            NextIrpSp = IoGetNextIrpStackLocation( Irp );

            *NextIrpSp = *IrpSp;

            //
            //  Set up the completion routine
            //

            IoSetCompletionRoutine( Irp,
                                    FatFlushCompletionRoutine,
                                    ULongToPtr( Status ),
                                    TRUE,
                                    TRUE,
                                    TRUE );

            //
            //  Send the request.
            //

            DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp);

            Status = (DriverStatus == STATUS_INVALID_DEVICE_REQUEST) ?
                     Status : DriverStatus;

            //
            //  Free the IrpContext and return to the caller.
            //

            FatCompleteRequest( IrpContext, FatNull, STATUS_SUCCESS );
        }

        DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status);
    }

    return Status;
}
Beispiel #4
0
NTSTATUS
FatPnpSurpriseRemove (
    PIRP_CONTEXT IrpContext,
    PIRP Irp,
    PVCB Vcb
    )

/*++

Routine Description:

    This routine handles the PnP surprise remove operation.  This is another
    type of notification that the underlying storage device for the volume we
    have is gone, and is excellent indication that the volume will never reappear.
    The filesystem is responsible for initiation or completion the dismount.
    
    For the most part, only "real" drivers care about the distinction of a
    surprise remove, which is a result of our noticing that a user (usually)
    physically reached into the machine and pulled something out.
    
    Surprise will be followed by a Remove when all references have been shut down.

Arguments:

    Irp - Supplies the Irp to process
    
    Vcb - Supplies the volume being removed.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    KEVENT Event;
    BOOLEAN VcbDeleted;
    
    //
    //  SURPRISE - a device was physically yanked away without
    //  any warning.  This means external forces.
    //
    
    FatAcquireExclusiveGlobal( IrpContext );
    FatAcquireExclusiveVcb( IrpContext, Vcb );
        
    //
    //  We need to pass this down before starting the dismount, which
    //  could disconnect us immediately from the stack.
    //
    
    //
    //  Get the next stack location, and copy over the stack location
    //

    IoCopyCurrentIrpStackLocationToNext( Irp );

    //
    //  Set up the completion routine
    //

    KeInitializeEvent( &Event, NotificationEvent, FALSE );
    IoSetCompletionRoutine( Irp,
                            FatPnpCompletionRoutine,
                            &Event,
                            TRUE,
                            TRUE,
                            TRUE );

    //
    //  Send the request and wait.
    //

    Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);

    if (Status == STATUS_PENDING) {

        KeWaitForSingleObject( &Event,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL );

        Status = Irp->IoStatus.Status;
    }
    
    try {
        
        //
        //  Knock as many files down for this volume as we can.
        //

        FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );

        //
        //  Now make our dismount happen.  This may not vaporize the
        //  Vcb, of course, since there could be any number of handles
        //  outstanding since this is an out of band notification.
        //

        VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );

    } finally {
        
        //
        //  Release the Vcb if it could still remain.
        //

        if (!VcbDeleted) {

            FatReleaseVcb( IrpContext, Vcb );
        }

        FatReleaseGlobal( IrpContext );
    }
    
    //
    //  Cleanup our IrpContext and complete the IRP.
    //

    FatCompleteRequest( IrpContext, Irp, Status );

    return Status;
}
Beispiel #5
0
NTSTATUS
FatPnpCancelRemove (
    PIRP_CONTEXT IrpContext,
    PIRP Irp,
    PVCB Vcb
    )

/*++

Routine Description:

    This routine handles the PnP cancel remove operation.  This is our
    notification that a previously proposed remove (query) was eventually
    vetoed by a component.  The filesystem is responsible for cleaning up
    and getting ready for more IO.
    
Arguments:

    Irp - Supplies the Irp to process
    
    Vcb - Supplies the volume being removed.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;

    //
    //  CANCEL - a previous QUERY has been rescinded as a result
    //  of someone vetoing.  Since PnP cannot figure out who may
    //  have gotten the QUERY (think about it: stacked drivers),
    //  we must expect to deal with getting a CANCEL without having
    //  seen the QUERY.
    //
    //  For FAT, this is quite easy.  In fact, we can't get a
    //  CANCEL if the underlying drivers succeeded the QUERY since
    //  we disconnect the Vpb on our dismount initiation.  This is
    //  actually pretty important because if PnP could get to us
    //  after the disconnect we'd be thoroughly unsynchronized
    //  with respect to the Vcb getting torn apart - merely referencing
    //  the volume device object is insufficient to keep us intact.
    //
    
    FatAcquireExclusiveVcb( IrpContext, Vcb );
    
    //
    //  Unlock the volume.  This is benign if we never had seen
    //  a QUERY.
    //

    Status = FatUnlockVolumeInternal( IrpContext, Vcb, NULL );

    try {
        
        //
        //  Send the request.  The underlying driver will complete the
        //  IRP.  Since we don't need to be in the way, simply ellide
        //  ourselves out of the IRP stack.
        //

        IoSkipCurrentIrpStackLocation( Irp );

        Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
    } 
    finally {
        
        FatReleaseVcb( IrpContext, Vcb );
    }
    
    FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );

    return Status;
}
Beispiel #6
0
NTSTATUS
FatPnpRemove (
    PIRP_CONTEXT IrpContext,
    PIRP Irp,
    PVCB Vcb
    )

/*++

Routine Description:

    This routine handles the PnP remove operation.  This is our notification
    that the underlying storage device for the volume we have is gone, and
    an excellent indication that the volume will never reappear. The filesystem
    is responsible for initiation or completion of the dismount.
    
Arguments:

    Irp - Supplies the Irp to process
    
    Vcb - Supplies the volume being removed.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    KEVENT Event;
    BOOLEAN VcbDeleted;
    
    //
    //  REMOVE - a storage device is now gone.  We either got
    //  QUERY'd and said yes OR got a SURPRISE OR a storage
    //  stack failed to spin back up from a sleep/stop state
    //  (the only case in which this will be the first warning).
    //
    //  Note that it is entirely unlikely that we will be around
    //  for a REMOVE in the first two cases, as we try to intiate
    //  dismount.
    //
    
    //
    //  Acquire the global resource so that we can try to vaporize
    //  the volume, and the vcb resource itself.
    //

    FatAcquireExclusiveGlobal( IrpContext );
    FatAcquireExclusiveVcb( IrpContext, Vcb );

    //
    //  The device will be going away.  Remove our lock (benign
    //  if we never had it).
    //

    (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL );
    
    //
    //  We need to pass this down before starting the dismount, which
    //  could disconnect us immediately from the stack.
    //
    
    //
    //  Get the next stack location, and copy over the stack location
    //

    IoCopyCurrentIrpStackLocationToNext( Irp );

    //
    //  Set up the completion routine
    //

    KeInitializeEvent( &Event, NotificationEvent, FALSE );
    IoSetCompletionRoutine( Irp,
                            FatPnpCompletionRoutine,
                            &Event,
                            TRUE,
                            TRUE,
                            TRUE );

    //
    //  Send the request and wait.
    //

    Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);

    if (Status == STATUS_PENDING) {

        KeWaitForSingleObject( &Event,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL );

        Status = Irp->IoStatus.Status;
    }

    try {
        
        //
        //  Knock as many files down for this volume as we can.
        //

        FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );

        //
        //  Now make our dismount happen.  This may not vaporize the
        //  Vcb, of course, since there could be any number of handles
        //  outstanding if we were not preceeded by a QUERY.
        //
        //  PnP will take care of disconnecting this stack if we
        //  couldn't get off of it immediately.
        //

        VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );

    } finally {
        
        //
        //  Release the Vcb if it could still remain.
        //

        if (!VcbDeleted) {

            FatReleaseVcb( IrpContext, Vcb );
        }

        FatReleaseGlobal( IrpContext );
    }

    //
    //  Cleanup our IrpContext and complete the IRP.
    //

    FatCompleteRequest( IrpContext, Irp, Status );

    return Status;
}
Beispiel #7
0
NTSTATUS
FatPnpQueryRemove (
    PIRP_CONTEXT IrpContext,
    PIRP Irp,
    PVCB Vcb
    )

/*++

Routine Description:

    This routine handles the PnP query remove operation.  The filesystem
    is responsible for answering whether there are any reasons it sees
    that the volume can not go away (and the device removed).  Initiation
    of the dismount begins when we answer yes to this question.
    
    Query will be followed by a Cancel or Remove.

Arguments:

    Irp - Supplies the Irp to process
    
    Vcb - Supplies the volume being queried.

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    KEVENT Event;
    BOOLEAN VcbDeleted = FALSE;
    BOOLEAN GlobalHeld = FALSE;

    //
    //  Having said yes to a QUERY, any communication with the
    //  underlying storage stack is undefined (and may block)
    //  until the bounding CANCEL or REMOVE is sent.
    //

    //
    //  Acquire the global resource so that we can try to vaporize
    //  the volume, and the vcb resource itself.
    //
    
    FatAcquireExclusiveVcb( IrpContext, Vcb );

    try {
        
        Status = FatLockVolumeInternal( IrpContext, Vcb, NULL );

        //
        //  Reacquire the resources in the right order.
        //

        FatReleaseVcb( IrpContext, Vcb );
        FatAcquireExclusiveGlobal( IrpContext );
        GlobalHeld = TRUE;
        FatAcquireExclusiveVcb( IrpContext, Vcb );

        if (NT_SUCCESS( Status )) {

            //
            //  With the volume held locked, note that we must finalize as much
            //  as possible right now.
            //

            FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush );

            //
            //  We need to pass this down before starting the dismount, which
            //  could disconnect us immediately from the stack.
            //

            //
            //  Get the next stack location, and copy over the stack location
            //

            IoCopyCurrentIrpStackLocationToNext( Irp );

            //
            //  Set up the completion routine
            //

            KeInitializeEvent( &Event, NotificationEvent, FALSE );
            IoSetCompletionRoutine( Irp,
                                    FatPnpCompletionRoutine,
                                    &Event,
                                    TRUE,
                                    TRUE,
                                    TRUE );

            //
            //  Send the request and wait.
            //

            Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);

            if (Status == STATUS_PENDING) {

                KeWaitForSingleObject( &Event,
                                       Executive,
                                       KernelMode,
                                       FALSE,
                                       NULL );

                Status = Irp->IoStatus.Status;
            }

            //
            //  Now if no one below us failed already, initiate the dismount
            //  on this volume, make it go away.  PnP needs to see our internal
            //  streams close and drop their references to the target device.
            //
            //  Since we were able to lock the volume, we are guaranteed to
            //  move this volume into dismount state and disconnect it from
            //  the underlying storage stack.  The force on our part is actually
            //  unnecesary, though complete.
            //
            //  What is not strictly guaranteed, though, is that the closes
            //  for the metadata streams take effect synchronously underneath
            //  of this call.  This would leave references on the target device
            //  even though we are disconnected!
            //

            if (NT_SUCCESS( Status )) {

                VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );

                ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad );

            }
        }

    } finally {
        
        //
        //  Release the Vcb if it could still remain.
        //

        if (!VcbDeleted) {

            FatReleaseVcb( IrpContext, Vcb );
        }

        if (GlobalHeld) {
            
            FatReleaseGlobal( IrpContext );
        }
    }
    
    //
    //  Cleanup our IrpContext and complete the IRP if neccesary.
    //

    FatCompleteRequest( IrpContext, Irp, Status );

    return Status;
}
Beispiel #8
0
VOID
SecondaryTryClose (
	IN PSECONDARY	Secondary
	)
{
	PIRP_CONTEXT	irpContext;

	BOOLEAN			secondaryResourceAcquired = FALSE;
	BOOLEAN			acquiredVcb = FALSE;
	BOOLEAN			wait;

	irpContext = FatAllocateIrpContext();

	if (irpContext == NULL) {

		return;
	}

	DebugTrace2( 0, Dbg, ("Secondary_TryCloseFiles start\n") );

	try {

		RtlZeroMemory( irpContext, sizeof(IRP_CONTEXT) );
		SetFlag( irpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
		SetFlag( irpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT );
		irpContext->Vcb = &Secondary->VolDo->Vcb;

		secondaryResourceAcquired = SecondaryAcquireResourceSharedLite( irpContext, 
																		&Secondary->VolDo->Resource, 
																		FALSE );
		if (secondaryResourceAcquired == FALSE) {

			leave;
		}

		wait = BooleanFlagOn( irpContext->Flags, IRP_CONTEXT_FLAG_WAIT );

		acquiredVcb = FatAcquireExclusiveVcb( irpContext, irpContext->Vcb );

		if (acquiredVcb == FALSE) {

			leave;
		}

		FsRtlEnterFileSystem();
		FatFspClose( &Secondary->VolDo->Vcb );
		FsRtlExitFileSystem();

		DebugTrace2( 0, Dbg, ("Secondary_TryCloseFiles FatFspClose, Secondary->VolDo->Vcb.SecondaryOpenCount = %d\n", 
							    Secondary->VolDo->Vcb.SecondaryOpenFileCount) );

		SetFlag( irpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_TRY_CLOSE_FILES );

		Secondary_TryCloseFilExts( Secondary );

	} finally {

		ClearFlag( irpContext->NdasFatFlags, NDAS_FAT_IRP_CONTEXT_FLAG_TRY_CLOSE_FILES );

		if (acquiredVcb) {

			FatReleaseVcb( irpContext, irpContext->Vcb );
		}

		DebugTrace2( 0, Dbg, ("Secondary_TryCloseFiles exit\n") );

		ExAcquireFastMutex( &Secondary->FastMutex );		
		Secondary->TryCloseActive = FALSE;
		ExReleaseFastMutex( &Secondary->FastMutex );

		if (secondaryResourceAcquired)
			SecondaryReleaseResourceLite( NULL, &Secondary->VolDo->Resource );

		Secondary_Dereference(Secondary);

		FatFreeIrpContext(irpContext);
	}

	return;
}
Beispiel #9
0
NTSTATUS
FatCommonSetVolumeInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for setting Volume Information called by both
    the fsd and fsp threads.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;

    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;
    TYPE_OF_OPEN TypeOfOpen;

    ULONG Length;
    FS_INFORMATION_CLASS FsInformationClass;
    PVOID Buffer;

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonSetVolumeInfo...\n", 0);
    DebugTrace( 0, Dbg, "Irp                  = %08lx\n", Irp );
    DebugTrace( 0, Dbg, "->Length             = %08lx\n", IrpSp->Parameters.SetVolume.Length);
    DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass);
    DebugTrace( 0, Dbg, "->Buffer             = %08lx\n", Irp->AssociatedIrp.SystemBuffer);

    //
    //  Reference our input parameters to make things easier
    //

    Length = IrpSp->Parameters.SetVolume.Length;
    FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass;
    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  Decode the file object to get the Vcb
    //

    TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );

    if (TypeOfOpen != UserVolumeOpen) {

        FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n", 0);

        return STATUS_ACCESS_DENIED;
    }

    //
    //  Acquire exclusive access to the Vcb and enqueue the Irp if we didn't
    //  get access
    //

    if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {

        DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);

        Status = FatFsdPostRequest( IrpContext, Irp );

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status );
        return Status;
    }

    try {

        //
        //  Make sure the vcb is in a usable condition.  This will raise
        //  and error condition if the volume is unusable
        //
        //  Also verify the Root Dcb since we need info from there.
        //

        FatVerifyFcb( IrpContext, Vcb->RootDcb );

        //
        //  Based on the information class we'll do different actions.  Each
        //  of the procedures that we're calling performs the action if
        //  possible and returns true if it successful and false if it couldn't
        //  wait for any I/O to complete.
        //

        switch (FsInformationClass) {

        case FileFsLabelInformation:

            Status = FatSetFsLabelInfo( IrpContext, Vcb, Buffer );
            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        FatUnpinRepinnedBcbs( IrpContext );

    } finally {

        DebugUnwind( FatCommonSetVolumeInfo );

        FatReleaseVcb( IrpContext, Vcb );

        if (!AbnormalTermination()) {

            FatCompleteRequest( IrpContext, Irp, Status );
        }

        DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status);
    }

    return Status;
}
Beispiel #10
0
/* Last handle to a file object is closed */
NTSTATUS
NTAPI
FatiCleanup(PFAT_IRP_CONTEXT IrpContext, PIRP Irp)
{
    PIO_STACK_LOCATION IrpSp;
    PFILE_OBJECT FileObject;
    TYPE_OF_OPEN TypeOfOpen;
    PSHARE_ACCESS ShareAccess;
    BOOLEAN SendUnlockNotification = FALSE;
    PLARGE_INTEGER TruncateSize = NULL;
    //LARGE_INTEGER LocalTruncateSize;
    BOOLEAN AcquiredVcb = FALSE, AcquiredFcb = FALSE;
    NTSTATUS Status;
    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DPRINT("FatiCleanup\n");
    DPRINT("\tIrp           = %p\n", Irp);
    DPRINT("\t->FileObject  = %p\n", IrpSp->FileObject);

    FileObject = IrpSp->FileObject;
    TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &Fcb, &Ccb);

    if (TypeOfOpen == UnopenedFileObject)
    {
        DPRINT1("Unopened File Object\n");

        FatCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
        return STATUS_SUCCESS;
    }

    if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ))
    {
        /* Just flush the file */

        if (FlagOn(Vcb->State, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
            FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
            !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED) &&
            (TypeOfOpen == UserFileOpen))
        {
            //Status = FatFlushFile(IrpContext, Fcb, Flush);
            //if (!NT_SUCCESS(Status)) FatNormalizeAndRaiseStatus(IrpContext, Status);
            UNIMPLEMENTED;
        }

        FatCompleteRequest(IrpContext, Irp, STATUS_SUCCESS);
        return STATUS_SUCCESS;
    }

    if (TypeOfOpen == UserFileOpen ||
        TypeOfOpen == UserDirectoryOpen)
    {
        ASSERT(Fcb != NULL);

        (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);

        AcquiredFcb = TRUE;

        /* Set FCB flags according to DELETE_ON_CLOSE */
        if (FlagOn(Ccb->Flags, CCB_DELETE_ON_CLOSE))
        {
            ASSERT(FatNodeType(Fcb) != FAT_NTC_ROOT_DCB);

            SetFlag(Fcb->State, FCB_STATE_DELETE_ON_CLOSE);

            /* Issue a notification */
            if (TypeOfOpen == UserDirectoryOpen)
            {
                FsRtlNotifyFullChangeDirectory(Vcb->NotifySync,
                                               &Vcb->NotifyList,
                                               FileObject->FsContext,
                                               NULL,
                                               FALSE,
                                               FALSE,
                                               0,
                                               NULL,
                                               NULL,
                                               NULL);
            }
        }

        /* If file should be deleted, acquire locks */
        if ((Fcb->UncleanCount == 1) &&
            FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) &&
            (Fcb->Condition != FcbBad) &&
            !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
        {
            FatReleaseFcb(IrpContext, Fcb);
            AcquiredFcb = FALSE;

            (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb);
            AcquiredVcb = TRUE;

            (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);
            AcquiredFcb = TRUE;
        }
    }

    /* Acquire VCB lock if it was a volume open */
    if (TypeOfOpen == UserVolumeOpen)
    {
        (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb);
        AcquiredVcb = TRUE;
    }

    /* Cleanup all notifications */
    if (TypeOfOpen == UserDirectoryOpen)
    {
        FsRtlNotifyCleanup(Vcb->NotifySync,
                           &Vcb->NotifyList,
                           Ccb);
    }

    if (Fcb)
    {
        //TODO: FatVerifyFcb
    }

    switch (TypeOfOpen)
    {
    case DirectoryFile:
    case VirtualVolumeFile:
        DPRINT1("Cleanup VirtualVolumeFile/DirectoryFile\n");
        ShareAccess = NULL;
        break;

    case UserVolumeOpen:
        DPRINT("Cleanup UserVolumeOpen\n");

        if (FlagOn(Ccb->Flags, CCB_COMPLETE_DISMOUNT))
        {
            FatCheckForDismount( IrpContext, Vcb, TRUE );
        } else if (FileObject->WriteAccess &&
            FlagOn(FileObject->Flags, FO_FILE_MODIFIED))
        {
            UNIMPLEMENTED;
        }

        /* Release the volume and send notification */
        if (FlagOn(Vcb->State, VCB_STATE_FLAG_LOCKED) &&
            (Vcb->FileObjectWithVcbLocked == FileObject))
        {
            UNIMPLEMENTED;
            SendUnlockNotification = TRUE;
        }

        ShareAccess = &Vcb->ShareAccess;
        break;

    case EaFile:
        DPRINT1("Cleanup EaFileObject\n");
        ShareAccess = NULL;
        break;

    case UserDirectoryOpen:
        DPRINT("Cleanup UserDirectoryOpen\n");

        ShareAccess = &Fcb->ShareAccess;

        /* Should it be a delayed close? */
        if ((Fcb->UncleanCount == 1) &&
            (Fcb->OpenCount == 1) &&
            (Fcb->Dcb.DirectoryFileOpenCount == 0) &&
            !FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) &&
            Fcb->Condition == FcbGood)
        {
            /* Yes, a delayed one */
            SetFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
        }

        if (VcbGood == Vcb->Condition)
        {
            //FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
            //TODO: Actually update dirent
        }

        if ((Fcb->UncleanCount == 1) &&
            (FatNodeType(Fcb) == FAT_NTC_DCB) &&
            (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE)) &&
            (Fcb->Condition != FcbBad) &&
            !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
        {
            UNIMPLEMENTED;
        }

        /*  Decrement unclean counter */
        ASSERT(Fcb->UncleanCount != 0);
        Fcb->UncleanCount--;
        break;

    case UserFileOpen:
        DPRINT("Cleanup UserFileOpen\n");

        ShareAccess = &Fcb->ShareAccess;

        /* Should it be a delayed close? */
        if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) &&
            (FileObject->SectionObjectPointer->ImageSectionObject == NULL) &&
            (Fcb->UncleanCount == 1) &&
            (Fcb->OpenCount == 1) &&
            !FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) &&
            Fcb->Condition == FcbGood)
        {
            /* Yes, a delayed one */
            //SetFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
            DPRINT1("Setting a delay on close for some reason for FCB %p, FF handle %p, file name '%wZ'\n", Fcb, Fcb->FatHandle, &Fcb->FullFileName);
        }

        /* Unlock all file locks */
        FsRtlFastUnlockAll(&Fcb->Fcb.Lock,
                           FileObject,
                           IoGetRequestorProcess(Irp),
                           NULL);

        if (Vcb->Condition == VcbGood)
        {
            if (Fcb->Condition != FcbBad)
            {
                //FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
                // TODO: Update on-disk structures
            }

            if (Fcb->UncleanCount == 1 &&
                Fcb->Condition != FcbBad)
            {
                //DELETE_CONTEXT DeleteContext;

                /* Should this file be deleted on close? */
                if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) &&
                    !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
                {
                    UNIMPLEMENTED;
                }
                else
                {
                    if (!FlagOn(Fcb->State, FCB_STATE_PAGEFILE) &&
                        (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart))
                    {
#if 0
                        ULONG ValidDataLength;

                        ValidDataLength = Fcb->Header.ValidDataLength.LowPart;

                        if (ValidDataLength < Fcb->ValidDataToDisk) {
                            ValidDataLength = Fcb->ValidDataToDisk;
                        }

                        if (ValidDataLength < Fcb->Header.FileSize.LowPart)
                        {
                            FatZeroData( IrpContext,
                                Vcb,
                                FileObject,
                                ValidDataLength,
                                Fcb->Header.FileSize.LowPart -
                                ValidDataLength );

                            Fcb->ValidDataToDisk =
                                Fcb->Header.ValidDataLength.LowPart =
                                Fcb->Header.FileSize.LowPart;

                            if (CcIsFileCached(FileObject))
                            {
                                CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize);
                            }
                        }
#endif
                        DPRINT1("Zeroing out data is not implemented\n");
                    }
                }

                /* Should the file be truncated on close? */
                if (FlagOn(Fcb->State, FCB_STATE_TRUNCATE_ON_CLOSE))
                {
                    if (Vcb->Condition == VcbGood)
                    {
                        // TODO: Actually truncate the file allocation
                        UNIMPLEMENTED;
                    }

                    /* Remove truncation flag */
                    Fcb->State &= ~FCB_STATE_TRUNCATE_ON_CLOSE;
                }

                /* Check again if it should be deleted */
                if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE) &&
                    Fcb->Header.AllocationSize.LowPart == 0)
                {
                    FatNotifyReportChange(IrpContext,
                                          Vcb,
                                          Fcb,
                                          FILE_NOTIFY_CHANGE_FILE_NAME,
                                          FILE_ACTION_REMOVED);
                }

                /* Remove the entry from the splay table if the file was deleted */
                if (FlagOn(Fcb->State, FCB_STATE_DELETE_ON_CLOSE))
                {
                    FatRemoveNames(IrpContext, Fcb);
                }
            }
        }

        ASSERT(Fcb->UncleanCount != 0);
        Fcb->UncleanCount--;
        if (!FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED))
        {
            ASSERT(Fcb->NonCachedUncleanCount != 0);
            Fcb->NonCachedUncleanCount--;
        }

        if (FlagOn(FileObject->Flags, FO_CACHE_SUPPORTED) &&
            (Fcb->NonCachedUncleanCount != 0) &&
            (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) &&
            (Fcb->SectionObjectPointers.DataSectionObject != NULL))
        {
            CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);

            /* Acquire and release PagingIo to get in sync with lazy writer */
            ExAcquireResourceExclusiveLite(Fcb->Header.PagingIoResource, TRUE);
            ExReleaseResourceLite(Fcb->Header.PagingIoResource);

            CcPurgeCacheSection(&Fcb->SectionObjectPointers,
                                NULL,
                                0,
                                FALSE);
        }

        if (Fcb->Condition == FcbBad)
        {
            //TruncateSize = &FatLargeZero;
            UNIMPLEMENTED;
        }

        /*  Cleanup the cache map */
        CcUninitializeCacheMap(FileObject, TruncateSize, NULL);
        break;

    default:
        KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)TypeOfOpen, 0, 0);
    }

    /* Cleanup the share access */

    if (ShareAccess)
    {
        DPRINT("Cleaning up the share access\n");
        IoRemoveShareAccess(FileObject, ShareAccess);
    }

    if (TypeOfOpen == UserFileOpen)
    {
        /* Update oplocks */
        FsRtlCheckOplock(&Fcb->Fcb.Oplock,
                         Irp,
                         IrpContext,
                         NULL,
                         NULL);

        Fcb->Header.IsFastIoPossible = FatIsFastIoPossible(Fcb);
    }

    /* Set the FO_CLEANUP_COMPLETE flag */
    SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE);

    Status = STATUS_SUCCESS;

    // TODO: Unpin repinned BCBs
    //FatUnpinRepinnedBcbs(IrpContext);

    /* Flush the volume if necessary */
    if (FlagOn(Vcb->State, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
        !FlagOn(Vcb->State, VCB_STATE_FLAG_WRITE_PROTECTED))
    {
        UNIMPLEMENTED;
    }

    /* Cleanup */
    if (AcquiredFcb) FatReleaseFcb(IrpContext, Fcb);
    if (AcquiredVcb) FatReleaseVcb(IrpContext, Vcb);

    /* Send volume notification */
    if (SendUnlockNotification)
        FsRtlNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);

    return Status;
}