/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    StrucSup.c

Abstract:

    This module implements the Raw in-memory data structure manipulation
    routines

--*/

#include "RawProcs.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RawInitializeVcb)
#endif


NTSTATUS
RawInitializeVcb (
    IN OUT PVCB Vcb,
    IN PDEVICE_OBJECT TargetDeviceObject,
    IN PVPB Vpb
    )

/*++

Routine Description:

    This routine initializes and inserts a new Vcb record into the in-memory
    data structure.  The Vcb record "hangs" off the end of the Volume device
    object and must be allocated by our caller.

Arguments:

    Vcb - Supplies the address of the Vcb record being initialized.

    TargetDeviceObject - Supplies the address of the target device object to
        associate with the Vcb record.

    Vpb - Supplies the address of the Vpb to associate with the Vcb record.

Return Value:

    NTSTATUS for any errors

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    PAGED_CODE();

    //
    //  We start by first zeroing out all of the VCB, this will guarantee
    //  that any stale data is wiped clean
    //

    RtlZeroMemory( Vcb, sizeof(VCB) );

    //
    //  Set the proper node type code and node byte size
    //

    Vcb->NodeTypeCode = RAW_NTC_VCB;
    Vcb->NodeByteSize = sizeof(VCB);

    //
    //  Set the Target Device Object, Vpb, and Vcb State fields
    //

    //
    //  No need to take a extra reference on the Target Device object as
    //  IopMountVolume already has taken a reference.
    //

    Vcb->TargetDeviceObject = TargetDeviceObject;
    Vcb->Vpb = Vpb;

    //
    //  Initialize the Mutex.
    //

    KeInitializeMutex( &Vcb->Mutex, MUTEX_LEVEL_FILESYSTEM_RAW_VCB );

    //
    //  allocate the spare vpb for forced dismount
    //

    Vcb->SpareVpb = ExAllocatePoolWithTag( NonPagedPool, sizeof( VPB ), 'Raw ');
    if (Vcb->SpareVpb == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    //  and return to our caller
    //

    return Status;
}

BOOLEAN
RawCheckForDismount (
    PVCB Vcb,
    BOOLEAN CalledFromCreate
    )

/*++

Routine Description:

    This routine determines if a volume is ready for deletion.  It
    correctly synchronizes with creates en-route to the file system.
    On exit if the vcb is deleted the mutex is released
    
Arguments:

    Vcb - Supplies the value to examine

    CalledFromCreate - Tells us if we should allow 0 or 1 in VpbRefCount

Return Value:

    BOOLEAN - TRUE if the volume was deleted, FALSE otherwise.

--*/

{

    KIRQL SavedIrql;
    ULONG ReferenceCount = 0;
    BOOLEAN DeleteVolume = FALSE;

    //
    //  We must enter with the vcb mutex acquired
    //  

    ASSERT( KeReadStateMutant( &Vcb->Mutex ) == 0 );

    IoAcquireVpbSpinLock( &SavedIrql );

    ReferenceCount = Vcb->Vpb->ReferenceCount;

    {
        PVPB Vpb;

        Vpb = Vcb->Vpb;

        //
        //  If a create is in progress on this volume, don't
        //  delete it.
        //

        if ( ReferenceCount != (ULONG)(CalledFromCreate ? 1 : 0) ) {

            //
            //  Cleanup the vpb on a forced dismount even if we can't delete the vcb if
            //  we haven't already done so
            //   

            if ((Vcb->SpareVpb != NULL) && 
                FlagOn( Vcb->VcbState,  VCB_STATE_FLAG_DISMOUNTED )) {

                //
                //  Setup the spare vpb and put it on the real device
                //  

                RtlZeroMemory( Vcb->SpareVpb, sizeof( VPB ) );

                Vcb->SpareVpb->Type = IO_TYPE_VPB;
                Vcb->SpareVpb->Size = sizeof( VPB );
                Vcb->SpareVpb->RealDevice = Vcb->Vpb->RealDevice;
                Vcb->SpareVpb->DeviceObject = NULL;
                Vcb->SpareVpb->Flags = FlagOn( Vcb->Vpb->Flags, VPB_REMOVE_PENDING );

                Vcb->Vpb->RealDevice->Vpb = Vcb->SpareVpb;

                //
                //  The spare vpb now belongs to the iosubsys and we own the original one
                //  

                Vcb->SpareVpb = NULL;
                Vcb->Vpb->Flags |=  VPB_PERSISTENT;

            }

            DeleteVolume = FALSE;

        } else {

            DeleteVolume = TRUE;

            if ( Vpb->RealDevice->Vpb == Vpb ) {

                Vpb->DeviceObject = NULL;

                Vpb->Flags &= ~VPB_MOUNTED;
            }
        }
    }
    IoReleaseVpbSpinLock( SavedIrql );

    if (DeleteVolume) {

        (VOID)KeReleaseMutex( &Vcb->Mutex, FALSE );

        //
        //  Free the spare vpb if we didn't use it or the original one if 
        //  we did use it and there are no more reference counts. Otherwise i/o
        //  subsystem still has a ref and will free the vpb itself
        // 

        if (Vcb->SpareVpb) {
            ExFreePool( Vcb->SpareVpb );
        } else if (ReferenceCount == 0) {
            ExFreePool( Vcb->Vpb );
        }
        
        ObDereferenceObject( Vcb->TargetDeviceObject );
        IoDeleteDevice( (PDEVICE_OBJECT)CONTAINING_RECORD( Vcb,
                                                           VOLUME_DEVICE_OBJECT,
                                                           Vcb));
    }
    
    return DeleteVolume;
}

