/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    cleanup.c

Abstract:

    This module implements the file cleanup routine for MSFS called by the
    dispatch driver.

Author:

    Manny Weiser (mannyw)    23-Jan-1991

Revision History:

--*/

#include "mailslot.h"

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_CLEANUP)

//
//  local procedure prototypes
//

NTSTATUS
MsCommonCleanup (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    );

NTSTATUS
MsCleanupCcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PCCB Ccb
    );

NTSTATUS
MsCleanupFcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PFCB Fcb
    );

NTSTATUS
MsCleanupRootDcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PROOT_DCB RootDcb,
    IN PROOT_DCB_CCB Ccb
    );

NTSTATUS
MsCleanupVcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PVCB Vcb
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, MsCleanupCcb )
#pragma alloc_text( PAGE, MsCleanupFcb )
#pragma alloc_text( PAGE, MsCleanupRootDcb )
#pragma alloc_text( PAGE, MsCleanupVcb )
#pragma alloc_text( PAGE, MsCommonCleanup )
#pragma alloc_text( PAGE, MsFsdCleanup )
#pragma alloc_text( PAGE, MsCancelTimer )
#endif

NTSTATUS
MsFsdCleanup (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtCleanupFile API calls.

Arguments:

    MsfsDeviceObject - Supplies the device object to use.

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The Fsd status for the Irp

--*/

{
    NTSTATUS status;

    PAGED_CODE();
    DebugTrace(+1, Dbg, "MsFsdCleanup\n", 0);

    //
    // Call the common cleanup routine.
    //

    FsRtlEnterFileSystem();

    status = MsCommonCleanup( MsfsDeviceObject, Irp );

    FsRtlExitFileSystem();

    //
    // Return to our caller.
    //

    DebugTrace(-1, Dbg, "MsFsdCleanup -> %08lx\n", status );
    return status;
}

NTSTATUS
MsCommonCleanup (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for cleaning up a file.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - the return status for the operation

--*/

{
    PIO_STACK_LOCATION irpSp;
    NTSTATUS status;
    NODE_TYPE_CODE nodeTypeCode;
    PVOID fsContext, fsContext2;
    PVCB vcb;

    PAGED_CODE();
    irpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "MsFsdCleanup\n", 0);
    DebugTrace( 0, Dbg, "MsfsDeviceObject = %08lx\n", (ULONG)MsfsDeviceObject);
    DebugTrace( 0, Dbg, "Irp              = %08lx\n", (ULONG)Irp);
    DebugTrace( 0, Dbg, "FileObject       = %08lx\n", (ULONG)irpSp->FileObject);



    //
    // Get the a referenced pointer to the node. If this is a CCB close and the FCB is already closed
    // then the node type comes back as undefined. We still want to cleanup in this case.
    // Cleanup for the CCB in this case is removing it from the FCB chain and removing share options. We
    // could do without this cleanup but it would look stange to have a corrupted chain in this case
    // (it does not harm as its never traversed again).
    //

    if ((nodeTypeCode = MsDecodeFileObject( irpSp->FileObject,
                                            &fsContext,
                                            &fsContext2 )) == NTC_UNDEFINED) {

        MsReferenceNode( ((PNODE_HEADER)(fsContext)) );
    }

    //
    // Get the VCB we are trying to access.
    //

    vcb = &MsfsDeviceObject->Vcb;

    //
    // Acquire exclusive access to the VCB.
    //

    MsAcquireExclusiveVcb( vcb );

    try {

        //
        // Decide how to handle this IRP.
        //

        switch (NodeType( fsContext ) ) {

        case MSFS_NTC_FCB:       // Cleanup a server handle to a mailslot file

            status = MsCleanupFcb( MsfsDeviceObject,
                                   Irp,
                                   (PFCB)fsContext);

            MsDereferenceFcb( (PFCB)fsContext );
            break;

        case MSFS_NTC_CCB:       // Cleanup a client handle to a mailslot file

            status = MsCleanupCcb( MsfsDeviceObject,
                                   Irp,
                                   (PCCB)fsContext);

            MsDereferenceCcb( (PCCB)fsContext );
            break;

        case MSFS_NTC_VCB:       // Cleanup MSFS

            status = MsCleanupVcb( MsfsDeviceObject,
                                   Irp,
                                   (PVCB)fsContext);

            MsDereferenceVcb( (PVCB)fsContext );
            break;

        case MSFS_NTC_ROOT_DCB:  // Cleanup root directory

            status = MsCleanupRootDcb( MsfsDeviceObject,
                                       Irp,
                                       (PROOT_DCB)fsContext,
                                       (PROOT_DCB_CCB)fsContext2);

            MsDereferenceRootDcb( (PROOT_DCB)fsContext );
            break;

    #ifdef MSDBG
        default:

            //
            // This is not one of ours.
            //

            KeBugCheck( MAILSLOT_FILE_SYSTEM );
            break;
    #endif

        }


    } finally {

        MsReleaseVcb( vcb );

        status = STATUS_SUCCESS;
        MsCompleteRequest( Irp, status );

        DebugTrace(-1, Dbg, "MsCommonCleanup -> %08lx\n", status);

    }

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


NTSTATUS
MsCleanupCcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PCCB Ccb
    )

/*++

Routine Description:

    The routine cleans up a CCB.

Arguments:

    MsfsDeviceObject - A pointer the the mailslot file system device object.

    Irp - Supplies the IRP associated with the cleanup.

    Ccb - Supplies the CCB for the mailslot to clean up.

Return Value:

    NTSTATUS - An appropriate completion status

--*/
{
    NTSTATUS status;
    PFCB fcb;

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

    //
    // Get a pointer to the FCB.
    //

    fcb = Ccb->Fcb;

    //
    // Acquire exclusive access to the FCB
    //

    MsAcquireExclusiveFcb( fcb );

    //
    // Set the CCB to closing and remove this CCB from the active list. This CCB may already be
    // closed if the FCB was closed first so don't check this. We still want the chain maintained.
    //

    Ccb->Header.NodeState = NodeStateClosing;
    RemoveEntryList( &Ccb->CcbLinks );          // Protected by the FCB lock since this is the FCB CCB chain

    MsReleaseFcb( fcb );

    //
    // Cleanup the share access.
    //

    ASSERT (MsIsAcquiredExclusiveVcb(fcb->Vcb));
    IoRemoveShareAccess( Ccb->FileObject, &fcb->ShareAccess );


    //
    // And return to our caller
    //

    status = STATUS_SUCCESS;
    return status;
}

VOID
MsCancelTimer (
    IN PDATA_ENTRY DataEntry
    )

/*++

Routine Description:

    The routine cancels the timer and if possible frees up a work block

Arguments:
    DataEntry - Block that needs to be checked for a timer

Return Value:

    None

--*/
{
    PWORK_CONTEXT WorkContext;
    //
    // There was a timer on this read operation.  Attempt
    // to cancel the operation.  If the cancel operation
    // is successful, then we must cleanup after the operation.
    // If it was unsuccessful the timer DPC will run, and
    // will eventually cleanup.
    //


    WorkContext = DataEntry->TimeoutWorkContext;
    if (WorkContext == NULL) {
       //
       // No timeout for this request, its already been canceled or its running
       //
       return;
    }

    //
    // Nobody else should touch this now. either this routine will free this memory or the
    // timer is running at it will free the memory.
    //
    DataEntry->TimeoutWorkContext = NULL;

    if (KeCancelTimer( &WorkContext->Timer ) ) {

        //
        // Release the reference to the FCB.
        //

        MsDereferenceFcb( WorkContext->Fcb );

        //
        // Free the memory from the work context, the time
        // and the DPC.
        //

        IoFreeWorkItem (WorkContext->WorkItem);
        ExFreePool( WorkContext );

    } else {
        //
        // Time code is active. Break the link between the timer block and the IRP
        //
        WorkContext->Irp = NULL;
    }
}



NTSTATUS
MsCleanupFcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PFCB Fcb
    )

/*++

Routine Description:

    This routine cleans up an FCB.  All outstanding i/o on the file
    object are completed with an error status.

Arguments:

    MsfsDeviceObject - A pointer the the mailslot file system device object.

    Irp - Supplies the IRP associated with the cleanup.

    Fcb - Supplies the FCB for the mailslot to clean up.

Return Value:

    NTSTATUS - An appropriate completion status

--*/
{
    NTSTATUS status;
    PDATA_QUEUE dataQueue;
    PDATA_ENTRY dataEntry;
    PLIST_ENTRY listEntry;
    PIRP oldIrp;
    PCCB ccb;
    PWORK_CONTEXT workContext;
    PKTIMER timer;

    PAGED_CODE();
    DebugTrace(+1, Dbg, "MsCleanupFcb, Fcb = %08lx\n", (ULONG)Fcb);


    status = STATUS_SUCCESS;

    //
    // Wipe out the name of the FCB from the prefix table and the parent DCB.
    //
    MsRemoveFcbName( Fcb );

    //
    // Acquire exclusive access to the FCB.
    //

    MsAcquireExclusiveFcb( Fcb );


    try {


        //
        // Complete all outstanding I/O on this FCB.
        //

        dataQueue = &Fcb->DataQueue;
        dataQueue->QueueState = -1;

        for (listEntry = MsGetNextDataQueueEntry( dataQueue );
             !MsIsDataQueueEmpty(dataQueue);
             listEntry = MsGetNextDataQueueEntry( dataQueue ) ) {

             //
             // This is an outstanding I/O request on this FCB.
             // Remove it from our queue and complete the request
             // if one is outstanding.
             //

             dataEntry = CONTAINING_RECORD( listEntry, DATA_ENTRY, ListEntry );


             oldIrp = MsRemoveDataQueueEntry( dataQueue, dataEntry );

             if (oldIrp != NULL) {

                 DebugTrace(0, Dbg, "Completing IRP %08lx\n", (ULONG)oldIrp );
                 MsCompleteRequest( oldIrp, STATUS_FILE_FORCED_CLOSED );

             }

        }

        //
        // Now cleanup all the CCB's on this FCB, to ensure that new
        // write IRP will not be processed.
        //


        listEntry = Fcb->Specific.Fcb.CcbQueue.Flink;

        while( listEntry != &Fcb->Specific.Fcb.CcbQueue ) {

            ccb = (PCCB)CONTAINING_RECORD( listEntry, CCB, CcbLinks );

            ccb->Header.NodeState = NodeStateClosing;

            //
            // Get the next CCB on this FCB.
            //

            listEntry = listEntry->Flink;
        }

        //
        // Cleanup the share access.
        //

        ASSERT (MsIsAcquiredExclusiveVcb(Fcb->Vcb));
        IoRemoveShareAccess( Fcb->FileObject, &Fcb->ShareAccess);

        //
        // Mark the FCB closing.
        //

        Fcb->Header.NodeState = NodeStateClosing;

   } finally {

        MsReleaseFcb( Fcb );
        DebugTrace(-1, Dbg, "MsCloseFcb -> %08lx\n", status);
    }

    //
    // Return to the caller.
    //

    return status;

}


NTSTATUS
MsCleanupRootDcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PROOT_DCB RootDcb,
    IN PROOT_DCB_CCB Ccb
    )

/*++

Routine Description:

    This routine cleans up a Root DCB.

Arguments:

    MsfsDeviceObject - A pointer the the mailslot file system device object.

    Irp - Supplies the IRP associated with the cleanup.

    RootDcb - Supplies the root dcb for MSFS.

Return Value:

    NTSTATUS - An appropriate completion status

--*/
{
    NTSTATUS status;
    PIO_STACK_LOCATION irpSp;

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



    status = STATUS_SUCCESS;

    irpSp = IoGetCurrentIrpStackLocation( Irp );

    //
    // Now acquire exclusive access to the Vcb.
    //

    MsAcquireExclusiveVcb( RootDcb->Vcb );

    //
    // clear any active notify requests
    //
    MsFlushNotifyForFile (RootDcb, irpSp->FileObject);

    //
    // Remove share access
    //
    IoRemoveShareAccess( irpSp->FileObject,
                         &RootDcb->ShareAccess );

    //
    // Mark the DCB CCB closing.
    //

    Ccb->Header.NodeState = NodeStateClosing;

    MsReleaseVcb( RootDcb->Vcb );

    DebugTrace(-1, Dbg, "MsCleanupRootDcb -> %08lx\n", status);

    //
    // Return to the caller.
    //

    return status;
}


NTSTATUS
MsCleanupVcb (
    IN PMSFS_DEVICE_OBJECT MsfsDeviceObject,
    IN PIRP Irp,
    IN PVCB Vcb
    )

/*++

Routine Description:

    The routine cleans up a VCB.

Arguments:

    MsfsDeviceObject - A pointer the the mailslot file system device object.

    Irp - Supplies the IRP associated with the cleanup.

    Vcb - Supplies the VCB for MSFS.

Return Value:

    NTSTATUS - An appropriate completion status

--*/

{
    NTSTATUS status;
    PIO_STACK_LOCATION irpSp;

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


    status = STATUS_SUCCESS;

    irpSp = IoGetCurrentIrpStackLocation( Irp );

    //
    //  Now acquire exclusive access to the Vcb
    //

    MsAcquireExclusiveVcb( Vcb );

    IoRemoveShareAccess( irpSp->FileObject,
                             &Vcb->ShareAccess );

    MsReleaseVcb( Vcb );

    DebugTrace(-1, Dbg, "MsCleanupVcb -> %08lx\n", status);

    //
    //  And return to our caller
    //

    return status;
}