////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*

 File: Misc.cpp

 Module: UDF File System Driver (Kernel mode execution only)

 Description:
   This file contains some miscellaneous support routines.

*/

#include            "udffs.h"
// define the file specific bug-check id
#define         UDF_BUG_CHECK_ID                UDF_FILE_MISC

#include            <stdio.h>

//CCHAR   DefLetter[] = {""};

/*

 Function: UDFInitializeZones()

 Description:
   Allocates some memory for global zones used to allocate FSD structures.
   Either all memory will be allocated or we will back out gracefully.

 Expected Interrupt Level (for execution) :

  IRQL_PASSIVE_LEVEL

 Return Value: STATUS_SUCCESS/Error

*/
NTSTATUS
UDFInitializeZones(VOID)
{
    NTSTATUS            RC = STATUS_SUCCESS;
    uint32              SizeOfZone = UDFGlobalData.DefaultZoneSizeInNumStructs;
    uint32              SizeOfObjectNameZone = 0;
    uint32              SizeOfCCBZone = 0;
//    uint32              SizeOfFCBZone = 0;
    uint32              SizeOfIrpContextZone = 0;
//    uint32              SizeOfFileInfoZone = 0;

    _SEH2_TRY {

        // initialize the spinlock protecting the zones
        KeInitializeSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock));

        // determine memory requirements
        switch (MmQuerySystemSize()) {
        case MmMediumSystem:
            SizeOfObjectNameZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfCCBZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfIrpContextZone = (4 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
            UDFGlobalData.MaxDelayedCloseCount = 24;
            UDFGlobalData.MinDelayedCloseCount = 6;
            UDFGlobalData.MaxDirDelayedCloseCount = 8;
            UDFGlobalData.MinDirDelayedCloseCount = 2;
            UDFGlobalData.WCacheMaxFrames = 8*4;
            UDFGlobalData.WCacheMaxBlocks = 16*64;
            UDFGlobalData.WCacheBlocksPerFrameSh = 8;
            UDFGlobalData.WCacheFramesToKeepFree = 4;
            break;
        case MmLargeSystem:
            SizeOfObjectNameZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfCCBZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfIrpContextZone = (8 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
            UDFGlobalData.MaxDelayedCloseCount = 72;
            UDFGlobalData.MinDelayedCloseCount = 18;
            UDFGlobalData.MaxDirDelayedCloseCount = 24;
            UDFGlobalData.MinDirDelayedCloseCount = 6;
            UDFGlobalData.WCacheMaxFrames = 2*16*4;
            UDFGlobalData.WCacheMaxBlocks = 2*16*64;
            UDFGlobalData.WCacheBlocksPerFrameSh = 8;
            UDFGlobalData.WCacheFramesToKeepFree = 8;
            break;
        case MmSmallSystem:
        default:
            SizeOfObjectNameZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFObjectName))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfCCBZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFCCB))) + sizeof(ZONE_SEGMENT_HEADER);
            SizeOfIrpContextZone = (2 * SizeOfZone * UDFQuadAlign(sizeof(UDFIrpContext))) + sizeof(ZONE_SEGMENT_HEADER);
            UDFGlobalData.MaxDelayedCloseCount = 8;
            UDFGlobalData.MinDelayedCloseCount = 2;
            UDFGlobalData.MaxDirDelayedCloseCount = 6;
            UDFGlobalData.MinDirDelayedCloseCount = 1;
            UDFGlobalData.WCacheMaxFrames = 8*4/2;
            UDFGlobalData.WCacheMaxBlocks = 16*64/2;
            UDFGlobalData.WCacheBlocksPerFrameSh = 8;
            UDFGlobalData.WCacheFramesToKeepFree = 2;
        }

        // typical NT methodology (at least until *someone* exposed the "difference" between a server and workstation ;-)
        if (MmIsThisAnNtAsSystem()) {
            SizeOfObjectNameZone *= UDF_NTAS_MULTIPLE;
            SizeOfCCBZone *= UDF_NTAS_MULTIPLE;
            SizeOfIrpContextZone *= UDF_NTAS_MULTIPLE;
        }

        // allocate memory for each of the zones and initialize the zones ...
        if (!(UDFGlobalData.ObjectNameZone = DbgAllocatePool(NonPagedPool, SizeOfObjectNameZone))) {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            try_return(RC);
        }

        if (!(UDFGlobalData.CCBZone = DbgAllocatePool(NonPagedPool, SizeOfCCBZone))) {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            try_return(RC);
        }

        if (!(UDFGlobalData.IrpContextZone = DbgAllocatePool(NonPagedPool, SizeOfIrpContextZone))) {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            try_return(RC);
        }

        // initialize each of the zone headers ...
        if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.ObjectNameZoneHeader),
                    UDFQuadAlign(sizeof(UDFObjectName)),
                    UDFGlobalData.ObjectNameZone, SizeOfObjectNameZone))) {
            // failed the initialization, leave ...
            try_return(RC);
        }

        if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.CCBZoneHeader),
                    UDFQuadAlign(sizeof(UDFCCB)),
                    UDFGlobalData.CCBZone,
                    SizeOfCCBZone))) {
            // failed the initialization, leave ...
            try_return(RC);
        }

        if (!NT_SUCCESS(RC = ExInitializeZone(&(UDFGlobalData.IrpContextZoneHeader),
                    UDFQuadAlign(sizeof(UDFIrpContext)),
                    UDFGlobalData.IrpContextZone,
                    SizeOfIrpContextZone))) {
            // failed the initialization, leave ...
            try_return(RC);
        }

try_exit:   NOTHING;

    } _SEH2_FINALLY {
        if (!NT_SUCCESS(RC)) {
            // invoke the destroy routine now ...
            UDFDestroyZones();
        } else {
            // mark the fact that we have allocated zones ...
            UDFSetFlag(UDFGlobalData.UDFFlags, UDF_DATA_FLAGS_ZONES_INITIALIZED);
        }
    } _SEH2_END;

    return(RC);
}


/*************************************************************************
*
* Function: UDFDestroyZones()
*
* Description:
*   Free up the previously allocated memory. NEVER do this once the
*   driver has been successfully loaded.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID UDFDestroyZones(VOID)
{
//    BrutePoint();

    _SEH2_TRY {
        // free up each of the pools
        if(UDFGlobalData.ObjectNameZone) {
            DbgFreePool(UDFGlobalData.ObjectNameZone);
            UDFGlobalData.ObjectNameZone = NULL;
        }
        if(UDFGlobalData.CCBZone) {
            DbgFreePool(UDFGlobalData.CCBZone);
            UDFGlobalData.CCBZone = NULL;
        }
        if(UDFGlobalData.IrpContextZone) {
            DbgFreePool(UDFGlobalData.IrpContextZone);
            UDFGlobalData.IrpContextZone = NULL;
        }

//try_exit: NOTHING;

    } _SEH2_FINALLY {
        UDFGlobalData.UDFFlags &= ~UDF_DATA_FLAGS_ZONES_INITIALIZED;
    } _SEH2_END;

    return;
}


/*************************************************************************
*
* Function: UDFIsIrpTopLevel()
*
* Description:
*   Helps the FSD determine who the "top level" caller is for this
*   request. A request can originate directly from a user process
*   (in which case, the "top level" will be NULL when this routine
*   is invoked), OR the user may have originated either from the NT
*   Cache Manager/VMM ("top level" may be set), or this could be a
*   recursion into our code in which we would have set the "top level"
*   field the last time around.
*
* Expected Interrupt Level (for execution) :
*
*  whatever level a particular dispatch routine is invoked at.
*
* Return Value: TRUE/FALSE (TRUE if top level was NULL when routine invoked)
*
*************************************************************************/
BOOLEAN
__fastcall
UDFIsIrpTopLevel(
    PIRP            Irp)            // the IRP sent to our dispatch routine
{
    if(!IoGetTopLevelIrp()) {
        // OK, so we can set ourselves to become the "top level" component
        IoSetTopLevelIrp(Irp);
        return TRUE;
    }
    return FALSE;
}


/*************************************************************************
*
* Function: UDFExceptionFilter()
*
* Description:
*   This routines allows the driver to determine whether the exception
*   is an "allowed" exception i.e. one we should not-so-quietly consume
*   ourselves, or one which should be propagated onwards in which case
*   we will most likely bring down the machine.
*
*   This routine employs the services of FsRtlIsNtstatusExpected(). This
*   routine returns a BOOLEAN result. A RC of FALSE will cause us to return
*   EXCEPTION_CONTINUE_SEARCH which will probably cause a panic.
*   The FsRtl.. routine returns FALSE iff exception values are (currently) :
*       STATUS_DATATYPE_MISALIGNMENT    ||  STATUS_ACCESS_VIOLATION ||
*       STATUS_ILLEGAL_INSTRUCTION  ||  STATUS_INSTRUCTION_MISALIGNMENT
*
* Expected Interrupt Level (for execution) :
*
*  ?
*
* Return Value: EXCEPTION_EXECUTE_HANDLER/EXECEPTION_CONTINUE_SEARCH
*
*************************************************************************/
long
UDFExceptionFilter(
    PtrUDFIrpContext    PtrIrpContext,
    PEXCEPTION_POINTERS PtrExceptionPointers
    )
{
    long                            ReturnCode = EXCEPTION_EXECUTE_HANDLER;
    NTSTATUS                        ExceptionCode = STATUS_SUCCESS;
#if defined UDF_DBG || defined PRINT_ALWAYS
    ULONG i;

    UDFPrint(("UDFExceptionFilter\n"));
    UDFPrint(("    Ex. Code: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionCode));
    UDFPrint(("    Ex. Addr: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionAddress));
    UDFPrint(("    Ex. Flag: %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionFlags));
    UDFPrint(("    Ex. Pnum: %x\n",PtrExceptionPointers->ExceptionRecord->NumberParameters));
    for(i=0;i<PtrExceptionPointers->ExceptionRecord->NumberParameters;i++) {
        UDFPrint(("       %x\n",PtrExceptionPointers->ExceptionRecord->ExceptionInformation[i]));
    }
#ifdef _X86_
    UDFPrint(("Exception context:\n"));
    if(PtrExceptionPointers->ContextRecord->ContextFlags & CONTEXT_INTEGER) {
        UDFPrint(("EAX=%8.8x   ",PtrExceptionPointers->ContextRecord->Eax));
        UDFPrint(("EBX=%8.8x   ",PtrExceptionPointers->ContextRecord->Ebx));
        UDFPrint(("ECX=%8.8x   ",PtrExceptionPointers->ContextRecord->Ecx));
        UDFPrint(("EDX=%8.8x\n",PtrExceptionPointers->ContextRecord->Edx));

        UDFPrint(("ESI=%8.8x   ",PtrExceptionPointers->ContextRecord->Esi));
        UDFPrint(("EDI=%8.8x   ",PtrExceptionPointers->ContextRecord->Edi));
    }
    if(PtrExceptionPointers->ContextRecord->ContextFlags & CONTEXT_CONTROL) {
        UDFPrint(("EBP=%8.8x   ",PtrExceptionPointers->ContextRecord->Esp));
        UDFPrint(("ESP=%8.8x\n",PtrExceptionPointers->ContextRecord->Ebp));

        UDFPrint(("EIP=%8.8x\n",PtrExceptionPointers->ContextRecord->Eip));
    }
//    UDFPrint(("Flags: %s %s    ",PtrExceptionPointers->ContextRecord->Eip));
#endif //_X86_

#endif // UDF_DBG

    // figure out the exception code
    ExceptionCode = PtrExceptionPointers->ExceptionRecord->ExceptionCode;

    if ((ExceptionCode == STATUS_IN_PAGE_ERROR) && (PtrExceptionPointers->ExceptionRecord->NumberParameters >= 3)) {
        ExceptionCode = PtrExceptionPointers->ExceptionRecord->ExceptionInformation[2];
    }

    if (PtrIrpContext) {
        PtrIrpContext->SavedExceptionCode = ExceptionCode;
        UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_EXCEPTION);
    }

    // check if we should propagate this exception or not
    if (!(FsRtlIsNtstatusExpected(ExceptionCode))) {

        // better free up the IrpContext now ...
        if (PtrIrpContext) {
            UDFPrint(("    UDF Driver internal error\n"));
            BrutePoint();
        } else {
            // we are not ok, propagate this exception.
            //  NOTE: we will bring down the machine ...
            ReturnCode = EXCEPTION_CONTINUE_SEARCH;
        }
    }


    // return the appropriate code
    return(ReturnCode);
} // end UDFExceptionFilter()


/*************************************************************************
*
* Function: UDFExceptionHandler()
*
* Description:
*   One of the routines in the FSD or in the modules we invoked encountered
*   an exception. We have decided that we will "handle" the exception.
*   Therefore we will prevent the machine from a panic ...
*   You can do pretty much anything you choose to in your commercial
*   driver at this point to ensure a graceful exit. In the UDF
*   driver, We shall simply free up the IrpContext (if any), set the
*   error code in the IRP and complete the IRP at this time ...
*
* Expected Interrupt Level (for execution) :
*
*  ?
*
* Return Value: Error code
*
*************************************************************************/
NTSTATUS
UDFExceptionHandler(
    PtrUDFIrpContext PtrIrpContext,
    PIRP             Irp
    )
{
//    NTSTATUS                        RC;
    NTSTATUS            ExceptionCode = STATUS_INSUFFICIENT_RESOURCES;
    PDEVICE_OBJECT      Device;
    PVPB Vpb;
    PETHREAD Thread;

    UDFPrint(("UDFExceptionHandler \n"));

//    ASSERT(Irp);

    if (!Irp) {
        UDFPrint(("  !Irp, return\n"));
        ASSERT(!PtrIrpContext);
        return ExceptionCode;
    }
    // If it was a queued close (or something like this) then we need not
    // completing it because of MUST_SUCCEED requirement.

    if (PtrIrpContext) {
        ExceptionCode = PtrIrpContext->SavedExceptionCode;
        // Free irp context here
//        UDFReleaseIrpContext(PtrIrpContext);
    } else {
        UDFPrint(("  complete Irp and return\n"));
        // must be insufficient resources ...?
        ExceptionCode = STATUS_INSUFFICIENT_RESOURCES;
        Irp->IoStatus.Status = ExceptionCode;
        Irp->IoStatus.Information = 0;
        // complete the IRP
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return ExceptionCode;
    }

    //  Check if we are posting this request.  One of the following must be true
    //  if we are to post a request.
    //
    //      - Status code is STATUS_CANT_WAIT and the request is asynchronous
    //          or we are forcing this to be posted.
    //
    //      - Status code is STATUS_VERIFY_REQUIRED and we are at APC level
    //          or higher.  Can't wait for IO in the verify path in this case.
    //
    //  Set the MORE_PROCESSING flag in the IrpContext to keep if from being
    //  deleted if this is a retryable condition.

    if (ExceptionCode == STATUS_VERIFY_REQUIRED) {
        if (KeGetCurrentIrql() >= APC_LEVEL) {
            UDFPrint(("  use UDFPostRequest()\n"));
            ExceptionCode = UDFPostRequest( PtrIrpContext, Irp );
        }
    }

    //  If we posted the request or our caller will retry then just return here.
    if ((ExceptionCode == STATUS_PENDING) ||
        (ExceptionCode == STATUS_CANT_WAIT)) {

        UDFPrint(("  STATUS_PENDING/STATUS_CANT_WAIT, return\n"));
        return ExceptionCode;
    }

    //  Store this error into the Irp for posting back to the Io system.
    Irp->IoStatus.Status = ExceptionCode;
    if (IoIsErrorUserInduced( ExceptionCode )) {

        //  Check for the various error conditions that can be caused by,
        //  and possibly resolved my the user.
        if (ExceptionCode == STATUS_VERIFY_REQUIRED) {

            //  Now we are at the top level file system entry point.
            //
            //  If we have already posted this request then the device to
            //  verify is in the original thread.  Find this via the Irp.
            Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread );
            IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL );

            //  If there is no device in that location then check in the
            //  current thread.
            if (Device == NULL) {

                Device = IoGetDeviceToVerify( PsGetCurrentThread() );
                IoSetDeviceToVerify( PsGetCurrentThread(), NULL );

                ASSERT( Device != NULL );

                //  Let's not BugCheck just because the driver screwed up.
                if (Device == NULL) {

                    UDFPrint(("  Device == NULL, return\n"));
                    ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR;
                    Irp->IoStatus.Status = ExceptionCode;
                    Irp->IoStatus.Information = 0;
                    // complete the IRP
                    IoCompleteRequest(Irp, IO_NO_INCREMENT);

                    UDFReleaseIrpContext(PtrIrpContext);

                    return ExceptionCode;
                }
            }

            UDFPrint(("  use UDFPerformVerify()\n"));
            //  UDFPerformVerify() will do the right thing with the Irp.
            //  If we return STATUS_CANT_WAIT then the current thread
            //  can retry the request.
            return UDFPerformVerify( PtrIrpContext, Irp, Device );
        }

        //
        //  The other user induced conditions generate an error unless
        //  they have been disabled for this request.
        //

        if (FlagOn( PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_FLAG_DISABLE_POPUPS )) {
  
            UDFPrint(("  DISABLE_POPUPS, complete Irp and return\n"));
            Irp->IoStatus.Status = ExceptionCode;
            Irp->IoStatus.Information = 0;
            // complete the IRP
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            UDFReleaseIrpContext(PtrIrpContext);
            return ExceptionCode;
        } else {

            //  Generate a pop-up
            if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) {

                Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb;
            } else {

                Vpb = NULL;
            }
            //  The device to verify is either in my thread local storage
            //  or that of the thread that owns the Irp.
            Thread = Irp->Tail.Overlay.Thread;
            Device = IoGetDeviceToVerify( Thread );

            if (Device == NULL) {

                Thread = PsGetCurrentThread();
                Device = IoGetDeviceToVerify( Thread );
                ASSERT( Device != NULL );

                //  Let's not BugCheck just because the driver screwed up.
                if (Device == NULL) {
                    UDFPrint(("  Device == NULL, return(2)\n"));
                    Irp->IoStatus.Status = ExceptionCode;
                    Irp->IoStatus.Information = 0;
                    // complete the IRP
                    IoCompleteRequest(Irp, IO_NO_INCREMENT);

                    UDFReleaseIrpContext(PtrIrpContext);

                    return ExceptionCode;
                }
            }

            //  This routine actually causes the pop-up.  It usually
            //  does this by queuing an APC to the callers thread,
            //  but in some cases it will complete the request immediately,
            //  so it is very important to IoMarkIrpPending() first.
            IoMarkIrpPending( Irp );
            IoRaiseHardError( Irp, Vpb, Device );

            //  We will be handing control back to the caller here, so
            //  reset the saved device object.

            UDFPrint(("  use IoSetDeviceToVerify()\n"));
            IoSetDeviceToVerify( Thread, NULL );
            //  The Irp will be completed by Io or resubmitted.  In either
            //  case we must clean up the IrpContext here.

            UDFReleaseIrpContext(PtrIrpContext);
            return STATUS_PENDING;
        }
    }

    // If it was a normal request from IOManager then complete it
    if (Irp) {
        UDFPrint(("  complete Irp\n"));
        // set the error code in the IRP
        Irp->IoStatus.Status = ExceptionCode;
        Irp->IoStatus.Information = 0;
    
        // complete the IRP
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        UDFReleaseIrpContext(PtrIrpContext);
    }

    UDFPrint(("  return from exception handler with code %x\n", ExceptionCode));
    return(ExceptionCode);
} // end UDFExceptionHandler()

/*************************************************************************
*
* Function: UDFLogEvent()
*
* Description:
*   Log a message in the NT Event Log. This is a rather simplistic log
*   methodology since we can potentially utilize the event log to
*   provide a lot of information to the user (and you should too!)
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID
UDFLogEvent(
    NTSTATUS UDFEventLogId,      // the UDF private message id
    NTSTATUS RC)                 // any NT error code we wish to log ...
{
    _SEH2_TRY {

        // Implement a call to IoAllocateErrorLogEntry() followed by a call
        // to IoWriteErrorLogEntry(). You should note that the call to IoWriteErrorLogEntry()
        // will free memory for the entry once the write completes (which in actuality
        // is an asynchronous operation).

    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        // nothing really we can do here, just do not wish to crash ...
        NOTHING;
    } _SEH2_END;

    return;
} // end UDFLogEvent()


/*************************************************************************
*
* Function: UDFAllocateObjectName()
*
* Description:
*   Allocate a new ObjectName structure to represent an open on-disk object.
*   Also initialize the ObjectName structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the ObjectName structure OR NULL.
*
*************************************************************************/
PtrUDFObjectName
UDFAllocateObjectName(VOID)
{
    PtrUDFObjectName            PtrObjectName = NULL;
    BOOLEAN                     AllocatedFromZone = TRUE;
    KIRQL                       CurrentIrql;

    // first, __try to allocate out of the zone
    KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
    if (!ExIsFullZone(&(UDFGlobalData.ObjectNameZoneHeader))) {
        // we have enough memory
        PtrObjectName = (PtrUDFObjectName)ExAllocateFromZone(&(UDFGlobalData.ObjectNameZoneHeader));

        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);

        // if we failed to obtain from the zone, get it directly from the VMM
        PtrObjectName = (PtrUDFObjectName)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFObjectName)));
        AllocatedFromZone = FALSE;
    }

    if (!PtrObjectName) {
        return NULL;
    }

    // zero out the allocated memory block
    RtlZeroMemory(PtrObjectName, UDFQuadAlign(sizeof(UDFObjectName)));

    // set up some fields ...
    PtrObjectName->NodeIdentifier.NodeType  = UDF_NODE_TYPE_OBJECT_NAME;
    PtrObjectName->NodeIdentifier.NodeSize  = UDFQuadAlign(sizeof(UDFObjectName));


    if (!AllocatedFromZone) {
        UDFSetFlag(PtrObjectName->ObjectNameFlags, UDF_OBJ_NAME_NOT_FROM_ZONE);
    }

    return(PtrObjectName);
} // end UDFAllocateObjectName()


/*************************************************************************
*
* Function: UDFReleaseObjectName()
*
* Description:
*   Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID
__fastcall 
UDFReleaseObjectName(
    PtrUDFObjectName PtrObjectName)
{
    KIRQL            CurrentIrql;

    ASSERT(PtrObjectName);

    // give back memory either to the zone or to the VMM
    if (!(PtrObjectName->ObjectNameFlags & UDF_OBJ_NAME_NOT_FROM_ZONE)) {
        // back to the zone
        KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
        ExFreeToZone(&(UDFGlobalData.ObjectNameZoneHeader), PtrObjectName);
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        MyFreePool__(PtrObjectName);
    }

    return;
} // end UDFReleaseObjectName()


/*************************************************************************
*
* Function: UDFAllocateCCB()
*
* Description:
*   Allocate a new CCB structure to represent an open on-disk object.
*   Also initialize the CCB structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the CCB structure OR NULL.
*
*************************************************************************/
PtrUDFCCB
UDFAllocateCCB(VOID)
{
    PtrUDFCCB                   Ccb = NULL;
    BOOLEAN                     AllocatedFromZone = TRUE;
    KIRQL                       CurrentIrql;

    // first, __try to allocate out of the zone
    KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
    if (!ExIsFullZone(&(UDFGlobalData.CCBZoneHeader))) {
        // we have enough memory
        Ccb = (PtrUDFCCB)ExAllocateFromZone(&(UDFGlobalData.CCBZoneHeader));

        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);

        // if we failed to obtain from the zone, get it directly from the VMM
        Ccb = (PtrUDFCCB)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFCCB)));
        AllocatedFromZone = FALSE;
//        UDFPrint(("    CCB allocated @%x\n",Ccb));
    }

    if (!Ccb) {
        return NULL;
    }

    // zero out the allocated memory block
    RtlZeroMemory(Ccb, UDFQuadAlign(sizeof(UDFCCB)));

    // set up some fields ...
    Ccb->NodeIdentifier.NodeType = UDF_NODE_TYPE_CCB;
    Ccb->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFCCB));


    if (!AllocatedFromZone) {
        UDFSetFlag(Ccb->CCBFlags, UDF_CCB_NOT_FROM_ZONE);
    }

    UDFPrint(("UDFAllocateCCB: %x\n", Ccb));
    return(Ccb);
} // end UDFAllocateCCB()


/*************************************************************************
*
* Function: UDFReleaseCCB()
*
* Description:
*   Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID
__fastcall 
UDFReleaseCCB(
    PtrUDFCCB Ccb
    )
{
    KIRQL   CurrentIrql;

    ASSERT(Ccb);

    UDFPrint(("UDFReleaseCCB: %x\n", Ccb));
    // give back memory either to the zone or to the VMM
    if(!(Ccb->CCBFlags & UDF_CCB_NOT_FROM_ZONE)) {
        // back to the zone
        KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
        ExFreeToZone(&(UDFGlobalData.CCBZoneHeader), Ccb);
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        MyFreePool__(Ccb);
    }

    return;
} // end UDFReleaseCCB()

/*
  Function: UDFCleanupCCB()                      
                                                 
  Description:                                   
    Cleanup and deallocate a previously allocated structure. 
                                                 
  Expected Interrupt Level (for execution) :     
                                                 
   IRQL_PASSIVE_LEVEL                            
                                                 
  Return Value: None                             

*/
VOID
__fastcall 
UDFCleanUpCCB(
    PtrUDFCCB Ccb)
{
//    ASSERT(Ccb);
    if(!Ccb) return; // probably, we havn't allocated it...
    ASSERT(Ccb->NodeIdentifier.NodeType == UDF_NODE_TYPE_CCB);

    _SEH2_TRY {
        if(Ccb->Fcb) {
            UDFTouch(&(Ccb->Fcb->CcbListResource));
            UDFAcquireResourceExclusive(&(Ccb->Fcb->CcbListResource),TRUE);
            RemoveEntryList(&(Ccb->NextCCB));
            UDFReleaseResource(&(Ccb->Fcb->CcbListResource));
        } else {
            BrutePoint();
        }

        if (Ccb->DirectorySearchPattern) {
            if (Ccb->DirectorySearchPattern->Buffer) {
                MyFreePool__(Ccb->DirectorySearchPattern->Buffer);
                Ccb->DirectorySearchPattern->Buffer = NULL;
            }

            MyFreePool__(Ccb->DirectorySearchPattern);
            Ccb->DirectorySearchPattern = NULL;
        }

        UDFReleaseCCB(Ccb);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;
} // end UDFCleanUpCCB()

/*************************************************************************
*
* Function: UDFAllocateFCB()
*
* Description:
*   Allocate a new FCB structure to represent an open on-disk object.
*   Also initialize the FCB structure to NULL.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the FCB structure OR NULL.
*
*************************************************************************/
PtrUDFFCB
UDFAllocateFCB(VOID)
{
    PtrUDFFCB                   Fcb = NULL;

    Fcb = (PtrUDFFCB)MyAllocatePool__(UDF_FCB_MT, UDFQuadAlign(sizeof(UDFFCB)));

    if (!Fcb) {
        return NULL;
    }

    // zero out the allocated memory block
    RtlZeroMemory(Fcb, UDFQuadAlign(sizeof(UDFFCB)));

    // set up some fields ...
    Fcb->NodeIdentifier.NodeType = UDF_NODE_TYPE_FCB;
    Fcb->NodeIdentifier.NodeSize = UDFQuadAlign(sizeof(UDFFCB));

    UDFPrint(("UDFAllocateFCB: %x\n", Fcb));
    return(Fcb);
} // end UDFAllocateFCB()


/*************************************************************************
*
* Function: UDFReleaseFCB()
*
* Description:
*   Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
/*VOID
UDFReleaseFCB(
    PtrUDFFCB Fcb
    )
{
    ASSERT(Fcb);

    MyFreePool__(Fcb);

    return;
}*/

/*************************************************************************
*
*
*************************************************************************/
VOID
__fastcall 
UDFCleanUpFCB(
    PtrUDFFCB Fcb
    )
{
    UDFPrint(("UDFCleanUpFCB: %x\n", Fcb));
    if(!Fcb) return;

    ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);

    _SEH2_TRY {
        // Deinitialize FCBName field
        if (Fcb->FCBName) {
            if(Fcb->FCBName->ObjectName.Buffer) {
                MyFreePool__(Fcb->FCBName->ObjectName.Buffer);
                Fcb->FCBName->ObjectName.Buffer = NULL;
#ifdef UDF_DBG
                Fcb->FCBName->ObjectName.Length =
                Fcb->FCBName->ObjectName.MaximumLength = 0;
#endif
            }
#ifdef UDF_DBG
            else {
                UDFPrint(("UDF: Fcb has invalid FCBName Buffer\n"));
                BrutePoint();
            }
#endif
            UDFReleaseObjectName(Fcb->FCBName);
            Fcb->FCBName = NULL;
        }
#ifdef UDF_DBG
        else {
            UDFPrint(("UDF: Fcb has invalid FCBName field\n"));
            BrutePoint();
        }
#endif


        // begin transaction {
        UDFTouch(&(Fcb->Vcb->FcbListResource));
        UDFAcquireResourceExclusive(&(Fcb->Vcb->FcbListResource), TRUE);
        // Remove this FCB from list of all FCB in VCB
        RemoveEntryList(&(Fcb->NextFCB));
        UDFReleaseResource(&(Fcb->Vcb->FcbListResource));
        // } end transaction

        if(Fcb->FCBFlags & UDF_FCB_INITIALIZED_CCB_LIST_RESOURCE)
            UDFDeleteResource(&(Fcb->CcbListResource));

        // Free memory
        UDFReleaseFCB(Fcb);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;
} // end UDFCleanUpFCB()

#ifdef UDF_DBG
ULONG IrpContextCounter = 0;
#endif //UDF_DBG

/*************************************************************************
*
* Function: UDFAllocateIrpContext()
*
* Description:
*   The UDF FSD creates an IRP context for each request received. This
*   routine simply allocates (and initializes to NULL) a UDFIrpContext
*   structure.
*   Most of the fields in the context structure are then initialized here.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: A pointer to the IrpContext structure OR NULL.
*
*************************************************************************/
PtrUDFIrpContext
UDFAllocateIrpContext(
    PIRP           Irp,
    PDEVICE_OBJECT PtrTargetDeviceObject
    )
{
    PtrUDFIrpContext            PtrIrpContext = NULL;
    BOOLEAN                     AllocatedFromZone = TRUE;
    KIRQL                       CurrentIrql;
    PIO_STACK_LOCATION          IrpSp = NULL;

    // first, __try to allocate out of the zone
    KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
    if (!ExIsFullZone(&(UDFGlobalData.IrpContextZoneHeader))) {
        // we have enough memory
        PtrIrpContext = (PtrUDFIrpContext)ExAllocateFromZone(&(UDFGlobalData.IrpContextZoneHeader));
  
        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        // release the spinlock
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);

        // if we failed to obtain from the zone, get it directly from the VMM
        PtrIrpContext = (PtrUDFIrpContext)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFIrpContext)));
        AllocatedFromZone = FALSE;
    }

    // if we could not obtain the required memory, bug-check.
    //  Do NOT do this in your commercial driver, instead handle    the error gracefully ...
    if (!PtrIrpContext) {
        return NULL;
    }

#ifdef UDF_DBG
    IrpContextCounter++;
#endif //UDF_DBG

    // zero out the allocated memory block
    RtlZeroMemory(PtrIrpContext, UDFQuadAlign(sizeof(UDFIrpContext)));

    // set up some fields ...
    PtrIrpContext->NodeIdentifier.NodeType  = UDF_NODE_TYPE_IRP_CONTEXT;
    PtrIrpContext->NodeIdentifier.NodeSize  = UDFQuadAlign(sizeof(UDFIrpContext));


    PtrIrpContext->Irp = Irp;
    PtrIrpContext->TargetDeviceObject = PtrTargetDeviceObject;

    // copy over some fields from the IRP and set appropriate flag values
    if (Irp) {
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        ASSERT(IrpSp);

        PtrIrpContext->MajorFunction = IrpSp->MajorFunction;
        PtrIrpContext->MinorFunction = IrpSp->MinorFunction;

        // Often, a FSD cannot honor a request for asynchronous processing
        // of certain critical requests. For example, a "close" request on
        // a file object can typically never be deferred. Therefore, do not
        // be surprised if sometimes our FSD (just like all other FSD
        // implementations on the Windows NT system) has to override the flag
        // below.
        if (IrpSp->FileObject == NULL) {
            PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;
        } else {
            if (IoIsOperationSynchronous(Irp)) {
                PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;
            }
        }
    }

    if (!AllocatedFromZone) {
        UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_NOT_FROM_ZONE);
    }

    // Are we top-level ? This information is used by the dispatching code
    // later (and also by the FSD dispatch routine)
    if (IoGetTopLevelIrp() != Irp) {
        // We are not top-level. Note this fact in the context structure
        UDFSetFlag(PtrIrpContext->IrpContextFlags, UDF_IRP_CONTEXT_NOT_TOP_LEVEL);
    }

    return(PtrIrpContext);
} // end UDFAllocateIrpContext()


/*************************************************************************
*
* Function: UDFReleaseIrpContext()
*
* Description:
*   Deallocate a previously allocated structure.
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID
UDFReleaseIrpContext(
    PtrUDFIrpContext    PtrIrpContext)
{
    if(!PtrIrpContext) return;
//    ASSERT(PtrIrpContext);

#ifdef UDF_DBG
    IrpContextCounter--;
#endif //UDF_DBG

    // give back memory either to the zone or to the VMM
    if (!(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_FROM_ZONE)) {
        // back to the zone
        KIRQL                           CurrentIrql;

        KeAcquireSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), &CurrentIrql);
        ExFreeToZone(&(UDFGlobalData.IrpContextZoneHeader), PtrIrpContext);
        KeReleaseSpinLock(&(UDFGlobalData.ZoneAllocationSpinLock), CurrentIrql);
    } else {
        MyFreePool__(PtrIrpContext);
    }

    return;
} // end UDFReleaseIrpContext()


/*************************************************************************
*
* Function: UDFPostRequest()
*
* Description:
*   Queue up a request for deferred processing (in the context of a system
*   worker thread). The caller must have locked the user buffer (if required)
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_PENDING
*
*************************************************************************/
NTSTATUS
UDFPostRequest(
    IN PtrUDFIrpContext PtrIrpContext,
    IN PIRP             Irp
    )
{
    KIRQL SavedIrql;
//    PIO_STACK_LOCATION IrpSp;
    PVCB Vcb;

//    IrpSp = IoGetCurrentIrpStackLocation(Irp);

/*
    if(Vcb->StopOverflowQueue) {
        if(Irp) {
            Irp->IoStatus.Status = STATUS_WRONG_VOLUME;
            Irp->IoStatus.Information = 0;
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }
        UDFReleaseIrpContext(PtrIrpContext);
        return STATUS_WRONG_VOLUME;
    }
*/
    // mark the IRP pending if this is not double post
    if(Irp)
        IoMarkIrpPending(Irp);

    Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
    KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);

    if ( Vcb->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) {

        //  We cannot currently respond to this IRP so we'll just enqueue it
        //  to the overflow queue on the volume.
        //  Note: we just reuse LIST_ITEM field inside WorkQueueItem, this
        //  doesn't matter to regular processing of WorkItems.
        InsertTailList( &(Vcb->OverflowQueue),
                        &(PtrIrpContext->WorkQueueItem.List) );
        Vcb->OverflowQueueCount++;
        KeReleaseSpinLock( &(Vcb->OverflowQueueSpinLock), SavedIrql );

    } else {

        //  We are going to send this Irp to an ex worker thread so up
        //  the count.
        Vcb->PostedRequestCount++;

        KeReleaseSpinLock( &(Vcb->OverflowQueueSpinLock), SavedIrql );

        // queue up the request
        ExInitializeWorkItem(&(PtrIrpContext->WorkQueueItem), UDFCommonDispatch, PtrIrpContext);

        ExQueueWorkItem(&(PtrIrpContext->WorkQueueItem), CriticalWorkQueue);
    //    ExQueueWorkItem(&(PtrIrpContext->WorkQueueItem), DelayedWorkQueue);

    }

    // return status pending
    return STATUS_PENDING;
} // end UDFPostRequest()


/*************************************************************************
*
* Function: UDFCommonDispatch()
*
* Description:
*   The common dispatch routine invoked in the context of a system worker
*   thread. All we do here is pretty much case off the major function
*   code and invoke the appropriate FSD dispatch routine for further
*   processing.
*
* Expected Interrupt Level (for execution) :
*
*   IRQL PASSIVE_LEVEL
*
* Return Value: None
*
*************************************************************************/
VOID
NTAPI
UDFCommonDispatch(
    IN PVOID Context   // actually is a pointer to IRPContext structure
    )
{
    NTSTATUS         RC = STATUS_SUCCESS;
    PtrUDFIrpContext PtrIrpContext = NULL;
    PIRP             Irp = NULL;
    PVCB             Vcb;
    KIRQL            SavedIrql;
    PLIST_ENTRY      Entry;
    BOOLEAN          SpinLock = FALSE;

    // The context must be a pointer to an IrpContext structure
    PtrIrpContext = (PtrUDFIrpContext)Context;

    // Assert that the Context is legitimate
    if ( !PtrIrpContext ||
         (PtrIrpContext->NodeIdentifier.NodeType != UDF_NODE_TYPE_IRP_CONTEXT) ||
         (PtrIrpContext->NodeIdentifier.NodeSize != UDFQuadAlign(sizeof(UDFIrpContext))) /*||
        !(PtrIrpContext->Irp)*/) {
        UDFPrint(("    Invalid Context\n"));
        BrutePoint();
        return;
    }

    Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
    ASSERT(Vcb);

    UDFPrint(("  *** Thr: %x  ThCnt: %x  QCnt: %x  Started!\n", PsGetCurrentThread(), Vcb->PostedRequestCount, Vcb->OverflowQueueCount));

    while(TRUE) {

        UDFPrint(("    Next IRP\n"));
        FsRtlEnterFileSystem();

        //  Get a pointer to the IRP structure
        // in some cases we can get Zero pointer to Irp
        Irp = PtrIrpContext->Irp;
        // Now, check if the FSD was top level when the IRP was originally invoked
        // and set the thread context (for the worker thread) appropriately
        if (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL) {
            // The FSD is not top level for the original request
            // Set a constant value in TLS to reflect this fact
            IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
        } else {
            IoSetTopLevelIrp(Irp);
        }

        // Since the FSD routine will now be invoked in the context of this worker
        // thread, we should inform the FSD that it is perfectly OK to block in
        // the context of this thread
        PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK;

        _SEH2_TRY {

            // Pre-processing has been completed; check the Major Function code value
            // either in the IrpContext (copied from the IRP), or directly from the
            //  IRP itself (we will need a pointer to the stack location to do that),
            //  Then, switch based on the value on the Major Function code
            UDFPrint(("  *** MJ: %x, Thr: %x\n", PtrIrpContext->MajorFunction, PsGetCurrentThread()));
            switch (PtrIrpContext->MajorFunction) {
            case IRP_MJ_CREATE:
                // Invoke the common create routine
                RC = UDFCommonCreate(PtrIrpContext, Irp);
                break;
            case IRP_MJ_READ:
                // Invoke the common read routine
                RC = UDFCommonRead(PtrIrpContext, Irp);
                break;
#ifndef UDF_READ_ONLY_BUILD
            case IRP_MJ_WRITE:
                // Invoke the common write routine
                RC = UDFCommonWrite(PtrIrpContext, Irp);
                break;
#endif //UDF_READ_ONLY_BUILD
            case IRP_MJ_CLEANUP:
                // Invoke the common cleanup routine
                RC = UDFCommonCleanup(PtrIrpContext, Irp);
                break;
            case IRP_MJ_CLOSE:
                // Invoke the common close routine
                RC = UDFCommonClose(PtrIrpContext, Irp);
                break;
            case IRP_MJ_DIRECTORY_CONTROL:
                // Invoke the common directory control routine
                RC = UDFCommonDirControl(PtrIrpContext, Irp);
                break;
            case IRP_MJ_QUERY_INFORMATION:
#ifndef UDF_READ_ONLY_BUILD
            case IRP_MJ_SET_INFORMATION:
#endif //UDF_READ_ONLY_BUILD
                // Invoke the common query/set information routine
                RC = UDFCommonFileInfo(PtrIrpContext, Irp);
                break;
            case IRP_MJ_QUERY_VOLUME_INFORMATION:
                // Invoke the common query volume routine
                RC = UDFCommonQueryVolInfo(PtrIrpContext, Irp);
                break;
#ifndef UDF_READ_ONLY_BUILD
            case IRP_MJ_SET_VOLUME_INFORMATION:
                // Invoke the common query volume routine
                RC = UDFCommonSetVolInfo(PtrIrpContext, Irp);
                break;
#endif //UDF_READ_ONLY_BUILD
#ifdef UDF_HANDLE_EAS
/*            case IRP_MJ_QUERY_EA:
                // Invoke the common query EAs routine
                RC = UDFCommonGetExtendedAttr(PtrIrpContext, Irp);
                break;
            case IRP_MJ_SET_EA:
                // Invoke the common set EAs routine
                RC = UDFCommonSetExtendedAttr(PtrIrpContext, Irp);
                break;*/
#endif // UDF_HANDLE_EAS
#ifdef UDF_ENABLE_SECURITY
            case IRP_MJ_QUERY_SECURITY:
                // Invoke the common query Security routine
                RC = UDFCommonGetSecurity(PtrIrpContext, Irp);
                break;
#ifndef UDF_READ_ONLY_BUILD
            case IRP_MJ_SET_SECURITY:
                // Invoke the common set Security routine
                RC = UDFCommonSetSecurity(PtrIrpContext, Irp);
                break;
#endif //UDF_READ_ONLY_BUILD
#endif // UDF_ENABLE_SECURITY
            // Continue with the remaining possible dispatch routines below ...
            default:
                UDFPrint(("  unhandled *** MJ: %x, Thr: %x\n", PtrIrpContext->MajorFunction, PsGetCurrentThread()));
                // This is the case where we have an invalid major function
                Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
                Irp->IoStatus.Information = 0;
        
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
                // Free up the Irp Context
                UDFReleaseIrpContext(PtrIrpContext);
                break;
            }

            // Note: PtrIrpContext is invalid here
            UDFPrint(("  *** Thr: %x  Done!\n", PsGetCurrentThread()));

        } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {

            RC = UDFExceptionHandler(PtrIrpContext, Irp);

            UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
        }  _SEH2_END;

        // Enable preemption
        FsRtlExitFileSystem();

        // Ensure that the "top-level" field is cleared
        IoSetTopLevelIrp(NULL);

        //  If there are any entries on this volume's overflow queue, service
        //  them.
        if(!Vcb) {
            BrutePoint();
            break;
        }

        KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);
        SpinLock = TRUE;
        if(!Vcb->OverflowQueueCount)
            break;

        Vcb->OverflowQueueCount--;
        Entry = RemoveHeadList(&Vcb->OverflowQueue);
        KeReleaseSpinLock(&(Vcb->OverflowQueueSpinLock), SavedIrql);
        SpinLock = FALSE;

        PtrIrpContext = CONTAINING_RECORD( Entry,
                                        UDFIrpContext,
                                        WorkQueueItem.List );
    }

    if(!SpinLock)
        KeAcquireSpinLock(&(Vcb->OverflowQueueSpinLock), &SavedIrql);
    Vcb->PostedRequestCount--;
    KeReleaseSpinLock(&(Vcb->OverflowQueueSpinLock), SavedIrql);

    UDFPrint(("  *** Thr: %x  ThCnt: %x  QCnt: %x  Terminated!\n", PsGetCurrentThread(), Vcb->PostedRequestCount, Vcb->OverflowQueueCount));

    return;
} // end UDFCommonDispatch()


/*************************************************************************
*
* Function: UDFInitializeVCB()
*
* Description:
*   Perform the initialization for a VCB structure.
*
* Expected Interrupt Level (for execution) :
*
*   IRQL PASSIVE_LEVEL
*
* Return Value: status
*
*************************************************************************/
NTSTATUS
UDFInitializeVCB(
    IN PDEVICE_OBJECT PtrVolumeDeviceObject,
    IN PDEVICE_OBJECT PtrTargetDeviceObject,
    IN PVPB           PtrVPB
    )
{
    NTSTATUS RC = STATUS_SUCCESS;
    PVCB     Vcb = NULL;
    SHORT    i;

    BOOLEAN VCBResourceInit     = FALSE;
    BOOLEAN BitMapResource1Init = FALSE;
    BOOLEAN FcbListResourceInit = FALSE;
    BOOLEAN FileIdResourceInit  = FALSE;
    BOOLEAN DlocResourceInit    = FALSE;
    BOOLEAN DlocResource2Init   = FALSE;
    BOOLEAN FlushResourceInit   = FALSE;
    BOOLEAN PreallocResourceInit= FALSE;
    BOOLEAN IoResourceInit      = FALSE;

    Vcb = (PVCB)(PtrVolumeDeviceObject->DeviceExtension);

    _SEH2_TRY {
    // Zero it out (typically this has already been done by the I/O
    // Manager but it does not hurt to do it again)!
    RtlZeroMemory(Vcb, sizeof(VCB));

    // Initialize the signature fields
    Vcb->NodeIdentifier.NodeType = UDF_NODE_TYPE_VCB;
    Vcb->NodeIdentifier.NodeSize = sizeof(VCB);

    // Initialize the ERESOURCE object.
    RC = UDFInitializeResourceLite(&(Vcb->VCBResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    VCBResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->BitMapResource1));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    BitMapResource1Init = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->FcbListResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    FcbListResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->FileIdResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    FileIdResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->DlocResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    DlocResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->DlocResource2));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    DlocResource2Init = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->FlushResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    FlushResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->PreallocResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    PreallocResourceInit = TRUE;

    RC = UDFInitializeResourceLite(&(Vcb->IoResource));
    if(!NT_SUCCESS(RC))
        try_return(RC);
    IoResourceInit = TRUE;

//    RC = UDFInitializeResourceLite(&(Vcb->DelayedCloseResource));
//    ASSERT(NT_SUCCESS(RC));

    // Allocate buffer for statistics
    Vcb->Statistics = (PFILE_SYSTEM_STATISTICS)MyAllocatePool__(NonPagedPool, sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors );
    if(!Vcb->Statistics)
        try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
    RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors );
    for (i=0; i < (KeNumberProcessors); i++) {
        Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS;
        Vcb->Statistics[i].Common.Version = 1;
        Vcb->Statistics[i].Common.SizeOfCompleteStructure =
            sizeof(FILE_SYSTEM_STATISTICS);
    }

    // We know the target device object.
    // Note that this is not neccessarily a pointer to the actual
    // physical/virtual device on which the logical volume should
    // be mounted. This is actually a pointer to either the actual
    // (real) device or to any device object that may have been
    // attached to it. Any IRPs that we send down should be sent to this
    // device object. However, the "real" physical/virtual device object
    // on which we perform our mount operation can be determined from the
    // RealDevice field in the VPB sent to us.
    Vcb->TargetDeviceObject = PtrTargetDeviceObject;

    // We also have a pointer to the newly created device object representing
    // this logical volume (remember that this VCB structure is simply an
    // extension of the created device object).
    Vcb->VCBDeviceObject = PtrVolumeDeviceObject;

    // We also have the VPB pointer. This was obtained from the
    // Parameters.MountVolume.Vpb field in the current I/O stack location
    // for the mount IRP.
    Vcb->Vpb = PtrVPB;
    // Target Vcb field in Vcb onto itself. This required for check in
    // open/lock/unlock volume dispatch poits
    Vcb->Vcb=Vcb;

    //  Set the removable media flag based on the real device's
    //  characteristics
    if (PtrVPB->RealDevice->Characteristics & FILE_REMOVABLE_MEDIA) {
        Vcb->VCBFlags |= UDF_VCB_FLAGS_REMOVABLE_MEDIA;
    }

    // Initialize the list anchor (head) for some lists in this VCB.
    InitializeListHead(&(Vcb->NextFCB));
    InitializeListHead(&(Vcb->NextNotifyIRP));
    InitializeListHead(&(Vcb->VolumeOpenListHead));

    //  Initialize the overflow queue for the volume
    Vcb->OverflowQueueCount = 0;
    InitializeListHead(&(Vcb->OverflowQueue));

    Vcb->PostedRequestCount = 0;
    KeInitializeSpinLock(&(Vcb->OverflowQueueSpinLock));

    // Initialize the notify IRP list mutex
    FsRtlNotifyInitializeSync(&(Vcb->NotifyIRPMutex));

    // Intilize NtRequiredFCB for this VCB
    Vcb->NTRequiredFCB = (PtrUDFNTRequiredFCB)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFNTRequiredFCB)));
    if(!Vcb->NTRequiredFCB)
        try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
    RtlZeroMemory(Vcb->NTRequiredFCB, UDFQuadAlign(sizeof(UDFNTRequiredFCB)));

    // Set the initial file size values appropriately. Note that our FSD may
    // wish to guess at the initial amount of information we would like to
    // read from the disk until we have really determined that this a valid
    // logical volume (on disk) that we wish to mount.
    // Vcb->FileSize = Vcb->AllocationSize = ??

    // We do not want to bother with valid data length callbacks
    // from the Cache Manager for the file stream opened for volume metadata
    // information
    Vcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength.QuadPart = 0x7FFFFFFFFFFFFFFFULL;

    Vcb->VolumeLockPID = -1;

    Vcb->VCBOpenCount = 1;

    Vcb->WCacheMaxBlocks        = UDFGlobalData.WCacheMaxBlocks;
    Vcb->WCacheMaxFrames        = UDFGlobalData.WCacheMaxFrames;
    Vcb->WCacheBlocksPerFrameSh = UDFGlobalData.WCacheBlocksPerFrameSh;
    Vcb->WCacheFramesToKeepFree = UDFGlobalData.WCacheFramesToKeepFree;

    // Create a stream file object for this volume.
    //Vcb->PtrStreamFileObject = IoCreateStreamFileObject(NULL,
    //                                            Vcb->Vpb->RealDevice);
    //ASSERT(Vcb->PtrStreamFileObject);

    // Initialize some important fields in the newly created file object.
    //Vcb->PtrStreamFileObject->FsContext = (PVOID)Vcb;      
    //Vcb->PtrStreamFileObject->FsContext2 = NULL;
    //Vcb->PtrStreamFileObject->SectionObjectPointer = &(Vcb->SectionObject);

    //Vcb->PtrStreamFileObject->Vpb = PtrVPB;

    // Link this chap onto the global linked list of all VCB structures.
    // We consider that GlobalDataResource was acquired in past
    UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE);
    InsertTailList(&(UDFGlobalData.VCBQueue), &(Vcb->NextVCB));

    Vcb->TargetDevName.Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool, sizeof(MOUNTDEV_NAME));
    if(!Vcb->TargetDevName.Buffer)
        try_return(RC = STATUS_INSUFFICIENT_RESOURCES);

    RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_DEVICE_NAME /*IOCTL_MOUNTDEV_QUERY_DEVICE_NAME*/, Vcb->TargetDeviceObject,
                    NULL,0,
                    (PVOID)(Vcb->TargetDevName.Buffer),sizeof(MOUNTDEV_NAME),
                    FALSE, NULL);
    if(!NT_SUCCESS(RC)) {

        if(RC == STATUS_BUFFER_OVERFLOW) {
            if(!MyReallocPool__((PCHAR)(Vcb->TargetDevName.Buffer), sizeof(MOUNTDEV_NAME),
                             (PCHAR*)&(Vcb->TargetDevName.Buffer), Vcb->TargetDevName.Buffer[0]+sizeof(MOUNTDEV_NAME)) ) {
                goto Kill_DevName_buffer;
            }

            RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_DEVICE_NAME /*IOCTL_MOUNTDEV_QUERY_DEVICE_NAME*/, Vcb->TargetDeviceObject,
                            NULL,0,
                            (PVOID)(Vcb->TargetDevName.Buffer), Vcb->TargetDevName.Buffer[0]+sizeof(MOUNTDEV_NAME),
                            FALSE, NULL);
            if(!NT_SUCCESS(RC))
                goto Kill_DevName_buffer;

        } else {
Kill_DevName_buffer:
            if(!MyReallocPool__((PCHAR)Vcb->TargetDevName.Buffer, sizeof(MOUNTDEV_NAME),
                                (PCHAR*)&(Vcb->TargetDevName.Buffer), sizeof(REG_NAMELESS_DEV)))
                try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
            RtlCopyMemory(Vcb->TargetDevName.Buffer, REG_NAMELESS_DEV, sizeof(REG_NAMELESS_DEV));
            Vcb->TargetDevName.Length = sizeof(REG_NAMELESS_DEV)-sizeof(WCHAR);
            Vcb->TargetDevName.MaximumLength = sizeof(REG_NAMELESS_DEV);
            goto read_reg;
        }
    }

    Vcb->TargetDevName.MaximumLength =
    (Vcb->TargetDevName.Length = Vcb->TargetDevName.Buffer[0]) + sizeof(WCHAR);
    RtlMoveMemory((PVOID)(Vcb->TargetDevName.Buffer), (PVOID)(Vcb->TargetDevName.Buffer+1), Vcb->TargetDevName.Buffer[0]);
    Vcb->TargetDevName.Buffer[i = (SHORT)(Vcb->TargetDevName.Length/sizeof(WCHAR))] = 0;

    for(;i>=0;i--) {
        if(Vcb->TargetDevName.Buffer[i] == L'\\') {

            Vcb->TargetDevName.Length -= i*sizeof(WCHAR);
            RtlMoveMemory((PVOID)(Vcb->TargetDevName.Buffer), (PVOID)(Vcb->TargetDevName.Buffer+i), Vcb->TargetDevName.Length);
            Vcb->TargetDevName.Buffer[Vcb->TargetDevName.Length/sizeof(WCHAR)] = 0;
            break;
        }
    }

    UDFPrint(("  TargetDevName: %S\n", Vcb->TargetDevName.Buffer));

    // Initialize caching for the stream file object.
    //CcInitializeCacheMap(Vcb->PtrStreamFileObject, (PCC_FILE_SIZES)(&(Vcb->AllocationSize)),
    //                            TRUE,       // We will use pinned access.
    //                            &(UDFGlobalData.CacheMgrCallBacks), Vcb);

read_reg:

    UDFReleaseResource(&(UDFGlobalData.GlobalDataResource));

    // Mark the fact that this VCB structure is initialized.
    Vcb->VCBFlags |= UDF_VCB_FLAGS_VCB_INITIALIZED;

    RC = STATUS_SUCCESS;

try_exit:   NOTHING;

    } _SEH2_FINALLY {
        
        if(!NT_SUCCESS(RC)) {
            if(Vcb->TargetDevName.Buffer)
                MyFreePool__(Vcb->TargetDevName.Buffer);
            if(Vcb->NTRequiredFCB)
                MyFreePool__(Vcb->NTRequiredFCB);
            if(Vcb->Statistics)
                MyFreePool__(Vcb->Statistics);

            if(VCBResourceInit)
                UDFDeleteResource(&(Vcb->VCBResource));
            if(BitMapResource1Init)
                UDFDeleteResource(&(Vcb->BitMapResource1));
            if(FcbListResourceInit)
                UDFDeleteResource(&(Vcb->FcbListResource));
            if(FileIdResourceInit)
                UDFDeleteResource(&(Vcb->FileIdResource));
            if(DlocResourceInit)
                UDFDeleteResource(&(Vcb->DlocResource));
            if(DlocResource2Init)
                UDFDeleteResource(&(Vcb->DlocResource2));
            if(FlushResourceInit)
                UDFDeleteResource(&(Vcb->FlushResource));
            if(PreallocResourceInit)
                UDFDeleteResource(&(Vcb->PreallocResource));
            if(IoResourceInit)
                UDFDeleteResource(&(Vcb->IoResource));
        }
    } _SEH2_END;

    return RC;
} // end UDFInitializeVCB()

UDFFSD_MEDIA_TYPE
UDFGetMediaClass(
    PVCB Vcb
    )
{
    switch(Vcb->FsDeviceType) {
    case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
        if(Vcb->VCBFlags & (UDF_VCB_FLAGS_VOLUME_READ_ONLY |
                            UDF_VCB_FLAGS_MEDIA_READ_ONLY))
            return MediaCdrom;
        if(Vcb->CDR_Mode)
            return MediaCdr;
        if((Vcb->MediaType >= MediaType_UnknownSize_CDR) &&
           (Vcb->MediaType < MediaType_UnknownSize_CDRW)) {
            return MediaCdr;
        }
        if((Vcb->MediaType >= MediaType_UnknownSize_CDRW) &&
           (Vcb->MediaType < MediaType_UnknownSize_Unknown)) {
            return MediaCdrw;
        }
        if(Vcb->MediaClassEx == CdMediaClass_CDR) {
            return MediaCdr;
        }
        if(Vcb->MediaClassEx == CdMediaClass_DVDR ||
           Vcb->MediaClassEx == CdMediaClass_DVDpR ||
           Vcb->MediaClassEx == CdMediaClass_HD_DVDR ||
           Vcb->MediaClassEx == CdMediaClass_BDR) {
            return MediaDvdr;
        }
        if(Vcb->MediaClassEx == CdMediaClass_CDRW) {
            return MediaCdrw;
        }
        if(Vcb->MediaClassEx == CdMediaClass_DVDRW ||
           Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
           Vcb->MediaClassEx == CdMediaClass_DVDRAM ||
           Vcb->MediaClassEx == CdMediaClass_HD_DVDRW ||
           Vcb->MediaClassEx == CdMediaClass_HD_DVDRAM ||
           Vcb->MediaClassEx == CdMediaClass_BDRE) {
            return MediaDvdrw;
        }
        // 
        if(Vcb->MediaClassEx == CdMediaClass_CDROM ||
           Vcb->MediaClassEx == CdMediaClass_DVDROM ||
           Vcb->MediaClassEx == CdMediaClass_HD_DVDROM ||
           Vcb->MediaClassEx == CdMediaClass_BDROM) {
            return MediaCdrom;
        }
        return MediaCdrom;
#ifdef UDF_HDD_SUPPORT
    case FILE_DEVICE_DISK_FILE_SYSTEM:
        if(Vcb->TargetDeviceObject->Characteristics & FILE_FLOPPY_DISKETTE)
            return MediaFloppy;
        if(Vcb->TargetDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
            return MediaZip;
        return MediaHdd;
#endif //UDF_HDD_SUPPORT
    }
    return MediaUnknown;
} // end UDFGetMediaClass()

typedef ULONG
(*ptrUDFGetParameter)(
    IN PVCB Vcb, 
    IN PCWSTR Name,
    IN ULONG DefValue
    );

VOID
UDFUpdateCompatOption(
    PVCB Vcb,
    BOOLEAN Update,
    BOOLEAN UseCfg,
    PCWSTR Name,
    ULONG Flag,
    BOOLEAN Default
    )
{
    ptrUDFGetParameter UDFGetParameter = UseCfg ? UDFGetCfgParameter : UDFGetRegParameter;

    if(UDFGetParameter(Vcb, Name, Update ? ((Vcb->CompatFlags & Flag) ? TRUE : FALSE) : Default)) {
        Vcb->CompatFlags |= Flag;
    } else {
        Vcb->CompatFlags &= ~Flag;
    }
} // end UDFUpdateCompatOption()

VOID
UDFReadRegKeys(
    PVCB Vcb,
    BOOLEAN Update,
    BOOLEAN UseCfg
    )
{
    ULONG mult = 1;
    ptrUDFGetParameter UDFGetParameter = UseCfg ? UDFGetCfgParameter : UDFGetRegParameter;
    
    Vcb->DefaultRegName = UDFMediaClassName[(ULONG)UDFGetMediaClass(Vcb)].ClassName;

    // Should we use Extended FE by default ?
    Vcb->UseExtendedFE = (UCHAR)UDFGetParameter(Vcb, REG_USEEXTENDEDFE_NAME,
        Update ? Vcb->UseExtendedFE : FALSE);
    if(Vcb->UseExtendedFE != TRUE) Vcb->UseExtendedFE = FALSE;
    // What type of AllocDescs should we use
    Vcb->DefaultAllocMode = (USHORT)UDFGetParameter(Vcb, REG_DEFALLOCMODE_NAME,
        Update ? Vcb->DefaultAllocMode : ICB_FLAG_AD_SHORT);
    if(Vcb->DefaultAllocMode > ICB_FLAG_AD_LONG) Vcb->DefaultAllocMode = ICB_FLAG_AD_SHORT;
    // Default UID & GID to be set on newly created files
    Vcb->DefaultUID = UDFGetParameter(Vcb, UDF_DEFAULT_UID_NAME, Update ? Vcb->DefaultUID : -1);
    Vcb->DefaultGID = UDFGetParameter(Vcb, UDF_DEFAULT_GID_NAME, Update ? Vcb->DefaultGID : -1);
    // FE allocation charge for plain Dirs
    Vcb->FECharge = UDFGetParameter(Vcb, UDF_FE_CHARGE_NAME, Update ? Vcb->FECharge : 0);
    if(!Vcb->FECharge)
        Vcb->FECharge = UDF_DEFAULT_FE_CHARGE;
    // FE allocation charge for Stream Dirs (SDir)
    Vcb->FEChargeSDir = UDFGetParameter(Vcb, UDF_FE_CHARGE_SDIR_NAME,
        Update ? Vcb->FEChargeSDir : 0);
    if(!Vcb->FEChargeSDir)
        Vcb->FEChargeSDir = UDF_DEFAULT_FE_CHARGE_SDIR;
    // How many Deleted entries should contain Directory to make us
    // start packing it.
    Vcb->PackDirThreshold = UDFGetParameter(Vcb, UDF_DIR_PACK_THRESHOLD_NAME,
        Update ? Vcb->PackDirThreshold : 0);
    if(Vcb->PackDirThreshold == 0xffffffff)
        Vcb->PackDirThreshold = UDF_DEFAULT_DIR_PACK_THRESHOLD;
    // The binary exponent for the number of Pages to be read-ahead'ed
    // This information would be sent to System Cache Manager
    if(!Update) {
        Vcb->SystemCacheGran = (1 << UDFGetParameter(Vcb, UDF_READAHEAD_GRAN_NAME, 0)) * PAGE_SIZE;
        if(!Vcb->SystemCacheGran)
            Vcb->SystemCacheGran = UDF_DEFAULT_READAHEAD_GRAN;
    }
    // Timeouts for FreeSpaceBitMap & TheWholeDirTree flushes
    Vcb->BM_FlushPriod = UDFGetParameter(Vcb, UDF_BM_FLUSH_PERIOD_NAME,
        Update ? Vcb->BM_FlushPriod : 0);
    if(!Vcb->BM_FlushPriod) {
        Vcb->BM_FlushPriod = UDF_DEFAULT_BM_FLUSH_TIMEOUT;
    } else
    if(Vcb->BM_FlushPriod == (ULONG)-1) {
        Vcb->BM_FlushPriod = 0;
    }
    Vcb->Tree_FlushPriod = UDFGetParameter(Vcb, UDF_TREE_FLUSH_PERIOD_NAME,
        Update ? Vcb->Tree_FlushPriod : 0);
    if(!Vcb->Tree_FlushPriod) {
        Vcb->Tree_FlushPriod = UDF_DEFAULT_TREE_FLUSH_TIMEOUT;
    } else
    if(Vcb->Tree_FlushPriod == (ULONG)-1) {
        Vcb->Tree_FlushPriod = 0;
    }
    Vcb->SkipCountLimit = UDFGetParameter(Vcb, UDF_NO_UPDATE_PERIOD_NAME,
        Update ? Vcb->SkipCountLimit : 0);
    if(!Vcb->SkipCountLimit)
        Vcb->SkipCountLimit = -1;

    Vcb->SkipEjectCountLimit = UDFGetParameter(Vcb, UDF_NO_EJECT_PERIOD_NAME,
        Update ? Vcb->SkipEjectCountLimit : 3);

    if(!Update) {
        // How many threads are allowed to sodomize Disc simultaneously on each CPU
        Vcb->ThreadsPerCpu = UDFGetParameter(Vcb, UDF_FSP_THREAD_PER_CPU_NAME,
            Update ? Vcb->ThreadsPerCpu : 2);
        if(Vcb->ThreadsPerCpu < 2)
            Vcb->ThreadsPerCpu = UDF_DEFAULT_FSP_THREAD_PER_CPU;
    }
    // The mimimum FileSize increment when we'll decide not to allocate
    // on-disk space.
    Vcb->SparseThreshold = UDFGetParameter(Vcb, UDF_SPARSE_THRESHOLD_NAME,
        Update ? Vcb->SparseThreshold : 0);
    if(!Vcb->SparseThreshold)
        Vcb->SparseThreshold = UDF_DEFAULT_SPARSE_THRESHOLD;
    // This option is used to VERIFY all the data written. It decreases performance
    Vcb->VerifyOnWrite = UDFGetParameter(Vcb, UDF_VERIFY_ON_WRITE_NAME,
        Update ? Vcb->VerifyOnWrite : FALSE) ? TRUE : FALSE;

#ifndef UDF_READ_ONLY_BUILD
    // Should we update AttrFileTime on Attr changes
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_ATTR, UDF_VCB_IC_UPDATE_ATTR_TIME, FALSE);
    // Should we update ModifyFileTime on Writes changes
    // It also affects ARCHIVE bit setting on write operations
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_MOD, UDF_VCB_IC_UPDATE_MODIFY_TIME, FALSE);
    // Should we update AccessFileTime on Exec & so on.
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_TIMES_ACCS, UDF_VCB_IC_UPDATE_ACCESS_TIME, FALSE);
    // Should we update Archive bit
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_ATTR_ARCH, UDF_VCB_IC_UPDATE_ARCH_BIT, FALSE);
    // Should we update Dir's Times & Attrs on Modify
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_DIR_TIMES_ATTR_W, UDF_VCB_IC_UPDATE_DIR_WRITE, FALSE);
    // Should we update Dir's Times & Attrs on Access
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_UPDATE_DIR_TIMES_ATTR_R, UDF_VCB_IC_UPDATE_DIR_READ, FALSE);
    // Should we allow user to write into Read-Only Directory
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_ALLOW_WRITE_IN_RO_DIR, UDF_VCB_IC_WRITE_IN_RO_DIR, TRUE);
    // Should we allow user to change Access Time for unchanged Directory
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_ALLOW_UPDATE_TIMES_ACCS_UCHG_DIR, UDF_VCB_IC_UPDATE_UCHG_DIR_ACCESS_TIME, FALSE);
#endif //UDF_READ_ONLY_BUILD
    // Should we record Allocation Descriptors in W2k-compatible form
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_W2K_COMPAT_ALLOC_DESCS, UDF_VCB_IC_W2K_COMPAT_ALLOC_DESCS, TRUE);
    // Should we read LONG_ADs with invalid PartitionReferenceNumber (generated by Nero Instant Burner)
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_INSTANT_COMPAT_ALLOC_DESCS, UDF_VCB_IC_INSTANT_COMPAT_ALLOC_DESCS, TRUE);
    // Should we make a copy of VolumeLabel in LVD
    // usually only PVD is updated
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_W2K_COMPAT_VLABEL, UDF_VCB_IC_W2K_COMPAT_VLABEL, TRUE);
    // Should we handle or ignore HW_RO flag
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_HANDLE_HW_RO, UDF_VCB_IC_HW_RO, FALSE);
    // Should we handle or ignore SOFT_RO flag
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_HANDLE_SOFT_RO, UDF_VCB_IC_SOFT_RO, TRUE);

    // Check if we should generate UDF-style or OS-style DOS-names
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_OS_NATIVE_DOS_NAME, UDF_VCB_IC_OS_NATIVE_DOS_NAME, FALSE);
#ifndef UDF_READ_ONLY_BUILD
    // should we force FO_WRITE_THROUGH on removable media
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_FORCE_WRITE_THROUGH_NAME, UDF_VCB_IC_FORCE_WRITE_THROUGH, 
                          (Vcb->TargetDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) ? TRUE : FALSE
                         );
#endif //UDF_READ_ONLY_BUILD
    // Should we ignore FO_SEQUENTIAL_ONLY
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_IGNORE_SEQUENTIAL_IO, UDF_VCB_IC_IGNORE_SEQUENTIAL_IO, FALSE);
// Force Read-only mounts
#ifndef UDF_READ_ONLY_BUILD
    UDFUpdateCompatOption(Vcb, Update, UseCfg, UDF_FORCE_HW_RO, UDF_VCB_IC_FORCE_HW_RO, FALSE);
#else //UDF_READ_ONLY_BUILD
    Vcb->CompatFlags |= UDF_VCB_IC_FORCE_HW_RO;
#endif //UDF_READ_ONLY_BUILD
    // Check if we should send FLUSH request for File/Dir down to
    // underlaying driver
    if(UDFGetParameter(Vcb, UDF_FLUSH_MEDIA,Update ? Vcb->FlushMedia : FALSE)) {
        Vcb->FlushMedia = TRUE;
    } else {
        Vcb->FlushMedia = FALSE;
    }
    // compare data from packet with data to be writen there
    // before physical writing
    if(!UDFGetParameter(Vcb, UDF_COMPARE_BEFORE_WRITE, Update ? Vcb->DoNotCompareBeforeWrite : FALSE)) {
        Vcb->DoNotCompareBeforeWrite = TRUE;
    } else {
        Vcb->DoNotCompareBeforeWrite = FALSE;
    }
    if(!Update)  {
        if(UDFGetParameter(Vcb, UDF_CHAINED_IO, TRUE)) {
            Vcb->CacheChainedIo = TRUE;
        }

        if(UDFGetParameter(Vcb, UDF_FORCE_MOUNT_ALL, FALSE)) {
            Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
        }
        // Should we show Blank.Cd file on damaged/unformatted,
        // but UDF-compatible disks
        Vcb->ShowBlankCd = (UCHAR)UDFGetParameter(Vcb, UDF_SHOW_BLANK_CD, FALSE);
        if(Vcb->ShowBlankCd) {
            Vcb->CompatFlags |= UDF_VCB_IC_SHOW_BLANK_CD;
            if(Vcb->ShowBlankCd > 2) {
                Vcb->ShowBlankCd = 2;
            }
        }
        // Should we wait util CD device return from
        // Becoming Ready state
        if(UDFGetParameter(Vcb, UDF_WAIT_CD_SPINUP, TRUE)) {
            Vcb->CompatFlags |= UDF_VCB_IC_WAIT_CD_SPINUP;
        }
        // Should we remenber bad VDS locations during mount
        // Caching will improve mount performance on bad disks, but
        // will degrade mauntability of unreliable discs
        if(UDFGetParameter(Vcb, UDF_CACHE_BAD_VDS, TRUE)) {
            Vcb->CompatFlags |= UDF_VCB_IC_CACHE_BAD_VDS;
        }

        // Set partitially damaged volume mount mode
        Vcb->PartitialDamagedVolumeAction = (UCHAR)UDFGetParameter(Vcb, UDF_PART_DAMAGED_BEHAVIOR, UDF_PART_DAMAGED_RW);
        if(Vcb->PartitialDamagedVolumeAction > 2) {
            Vcb->PartitialDamagedVolumeAction = UDF_PART_DAMAGED_RW;
        }

        // Set partitially damaged volume mount mode
        Vcb->NoFreeRelocationSpaceVolumeAction = (UCHAR)UDFGetParameter(Vcb, UDF_NO_SPARE_BEHAVIOR, UDF_PART_DAMAGED_RW);
        if(Vcb->NoFreeRelocationSpaceVolumeAction > 1) {
            Vcb->NoFreeRelocationSpaceVolumeAction = UDF_PART_DAMAGED_RW;
        }

        // Set dirty volume mount mode
        if(UDFGetParameter(Vcb, UDF_DIRTY_VOLUME_BEHAVIOR, UDF_PART_DAMAGED_RO)) {
            Vcb->CompatFlags |= UDF_VCB_IC_DIRTY_RO;
        }

        mult = UDFGetParameter(Vcb, UDF_CACHE_SIZE_MULTIPLIER, 1);
        if(!mult) mult = 1;
        Vcb->WCacheMaxBlocks *= mult;
        Vcb->WCacheMaxFrames *= mult;

        if(UDFGetParameter(Vcb, UDF_USE_EJECT_BUTTON, TRUE)) {
            Vcb->UseEvent = TRUE;
        }
    }
    return;
} // end UDFReadRegKeys()

ULONG
UDFGetRegParameter(
    IN PVCB Vcb, 
    IN PCWSTR Name,
    IN ULONG DefValue
    )
{
    return UDFRegCheckParameterValue(&(UDFGlobalData.SavedRegPath),
                                     Name,
                                     Vcb ? &(Vcb->TargetDevName) : NULL,
                                     Vcb ? Vcb->DefaultRegName : NULL,
                                     DefValue);
} // end UDFGetRegParameter()

ULONG
UDFGetCfgParameter(
    IN PVCB Vcb, 
    IN PCWSTR Name,
    IN ULONG DefValue
    )
{
    ULONG len;
    CHAR NameA[128];
    ULONG ret_val=0;
    CHAR a;
    BOOLEAN wait_name=TRUE;
    BOOLEAN wait_val=FALSE;
    BOOLEAN wait_nl=FALSE;
    ULONG radix=10;
    ULONG i;

    PUCHAR Cfg    = Vcb->Cfg;
    ULONG  Length = Vcb->CfgLength;

    if(!Cfg || !Length)
        return DefValue;

    len = wcslen(Name);
    if(len >= sizeof(NameA))
        return DefValue;
    sprintf(NameA, "%S", Name);

    for(i=0; i<Length; i++) {
        a=Cfg[i];
        switch(a) {
        case '\n':
        case '\r':
        case ',':
            if(wait_val)
                return DefValue;
            continue;
        case ';':
        case '#':
        case '[': // ignore sections for now, treat as comment
            if(!wait_name)
                return DefValue;
            wait_nl = TRUE;
            continue;
        case '=':
            if(!wait_val)
                return DefValue;
            continue;
        case ' ':
        case '\t':
            continue;
        default:
            if(wait_nl)
                continue;
        }
        if(wait_name) {
            if(i+len+2 > Length)
                return DefValue;
            if(RtlCompareMemory(Cfg+i, NameA, len) == len) {
                a=Cfg[i+len];
                switch(a) {
                case '\n':
                case '\r':
                case ',':
                case ';':
                case '#':
                    return DefValue;
                case '=':
                case ' ':
                case '\t':
                    break;
                default:
                    wait_nl = TRUE;
                    wait_val = FALSE;
                    i+=len;
                    continue;
                }
                wait_name = FALSE;
                wait_nl = FALSE;
                wait_val = TRUE;
                i+=len;
                
            } else {
                wait_nl = TRUE;
            }
            continue;
        }
        if(wait_val) {
            if(i+3 > Length) {
                if(a=='0' && Cfg[i+1]=='x') {
                    i+=2;
                    radix=16;
                }
            }
            if(i >= Length) {
                return DefValue;
            }
            while(i<Length) {
                a=Cfg[i];
                switch(a) {
                case '\n':
                case '\r':
                case ' ':
                case '\t':
                case ',':
                case ';':
                case '#':
                    if(wait_val)
                        return DefValue;
                    return ret_val;
                }
                if(a >= '0' && a <= '9') {
                    a -= '0';
                } else {
                    if(radix != 16)
                        return DefValue;
                    if(a >= 'a' && a <= 'f') {
                        a -= 'a';
                    } else
                    if(a >= 'A' && a <= 'F') {
                        a -= 'A';
                    } else {
                        return DefValue;
                    }
                    a += 0x0a;
                }
                ret_val = ret_val*radix + a;
                wait_val = FALSE;
                i++;
            }
            return ret_val;
        }
    }
    return DefValue;

} // end UDFGetCfgParameter()

VOID
UDFReleaseVCB(
    PVCB  Vcb
    )
{
    LARGE_INTEGER delay;
    UDFPrint(("UDFReleaseVCB\n"));

    delay.QuadPart = -500000; // 0.05 sec
    while(Vcb->PostedRequestCount) {
        UDFPrint(("UDFReleaseVCB: PostedRequestCount = %d\n", Vcb->PostedRequestCount));
        // spin until all queues IRPs are processed
        KeDelayExecutionThread(KernelMode, FALSE, &delay);
        delay.QuadPart -= 500000; // grow delay 0.05 sec
    }

    _SEH2_TRY {
        UDFPrint(("UDF: Flushing buffers\n"));
        UDFVRelease(Vcb);
        WCacheFlushAll__(&(Vcb->FastCache),Vcb);
        WCacheRelease__(&(Vcb->FastCache));

    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;

#ifdef UDF_DBG
    _SEH2_TRY {
        if (!ExIsResourceAcquiredShared(&UDFGlobalData.GlobalDataResource)) {
            UDFPrint(("UDF: attempt to access to not protected data\n"));
            UDFPrint(("UDF: UDFGlobalData\n"));
            BrutePoint();
        }
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;
#endif

    _SEH2_TRY {
        RemoveEntryList(&(Vcb->NextVCB));
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;

/*    _SEH2_TRY {
        if(Vcb->VCBFlags & UDF_VCB_FLAGS_STOP_WAITER_EVENT)
            KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, NULL);
            Vcb->VCBFlags &= ~UDF_VCB_FLAGS_STOP_WAITER_EVENT;
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    }*/

    _SEH2_TRY {
        UDFPrint(("UDF: Delete resources\n"));
        UDFDeleteResource(&(Vcb->VCBResource));
        UDFDeleteResource(&(Vcb->BitMapResource1));
        UDFDeleteResource(&(Vcb->FcbListResource));
        UDFDeleteResource(&(Vcb->FileIdResource));
        UDFDeleteResource(&(Vcb->DlocResource));
        UDFDeleteResource(&(Vcb->DlocResource2));
        UDFDeleteResource(&(Vcb->FlushResource));
        UDFDeleteResource(&(Vcb->PreallocResource));
        UDFDeleteResource(&(Vcb->IoResource));
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;

    _SEH2_TRY {
        UDFPrint(("UDF: Cleanup VCB\n"));
        ASSERT(IsListEmpty(&(Vcb->NextNotifyIRP)));
        FsRtlNotifyUninitializeSync(&(Vcb->NotifyIRPMutex));
        UDFCleanupVCB(Vcb);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;

    _SEH2_TRY {
        UDFPrint(("UDF: Delete DO\n"));
        IoDeleteDevice(Vcb->VCBDeviceObject);
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        BrutePoint();
    } _SEH2_END;

} // end UDFReleaseVCB()

/*
    Read DWORD from Registry
*/
ULONG
UDFRegCheckParameterValue(
    IN PUNICODE_STRING RegistryPath,
    IN PCWSTR Name,
    IN PUNICODE_STRING PtrVolumePath,
    IN PCWSTR DefaultPath,
    IN ULONG DefValue
    )
{
    NTSTATUS          status;

    ULONG             val = DefValue;

    UNICODE_STRING    paramStr;
    UNICODE_STRING    defaultParamStr;
    UNICODE_STRING    paramPathUnknownStr;

    UNICODE_STRING    paramSuffix;
    UNICODE_STRING    paramPath;
    UNICODE_STRING    paramPathUnknown;
    UNICODE_STRING    paramDevPath;
    UNICODE_STRING    defaultParamPath;

    _SEH2_TRY {

        paramPath.Buffer = NULL;
        paramDevPath.Buffer = NULL;
        paramPathUnknown.Buffer = NULL;
        defaultParamPath.Buffer = NULL;

        // First append \Parameters to the passed in registry path
        // Note, RtlInitUnicodeString doesn't allocate memory
        RtlInitUnicodeString(&paramStr, L"\\Parameters");
        RtlInitUnicodeString(&paramPath, NULL);

        RtlInitUnicodeString(&paramPathUnknownStr, REG_DEFAULT_UNKNOWN);
        RtlInitUnicodeString(&paramPathUnknown, NULL);
        
        paramPathUnknown.MaximumLength = RegistryPath->Length + paramPathUnknownStr.Length + paramStr.Length + sizeof(WCHAR);
        paramPath.MaximumLength = RegistryPath->Length + paramStr.Length + sizeof(WCHAR);

        paramPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramPath.MaximumLength);
        if(!paramPath.Buffer) {
            UDFPrint(("UDFCheckRegValue: couldn't allocate paramPath\n"));
            try_return(val = DefValue);
        }
        paramPathUnknown.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramPathUnknown.MaximumLength);
        if(!paramPathUnknown.Buffer) {
            UDFPrint(("UDFCheckRegValue: couldn't allocate paramPathUnknown\n"));
            try_return(val = DefValue);
        }

        RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength);
        status = RtlAppendUnicodeToString(&paramPath, RegistryPath->Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        status = RtlAppendUnicodeToString(&paramPath, paramStr.Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        UDFPrint(("UDFCheckRegValue: (1) |%S|\n", paramPath.Buffer));

        RtlZeroMemory(paramPathUnknown.Buffer, paramPathUnknown.MaximumLength);
        status = RtlAppendUnicodeToString(&paramPathUnknown, RegistryPath->Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        status = RtlAppendUnicodeToString(&paramPathUnknown, paramStr.Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        status = RtlAppendUnicodeToString(&paramPathUnknown, paramPathUnknownStr.Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        UDFPrint(("UDFCheckRegValue: (2) |%S|\n", paramPathUnknown.Buffer));

        // First append \Parameters\Default_XXX to the passed in registry path
        if(DefaultPath) {
            RtlInitUnicodeString(&defaultParamStr, DefaultPath);
            RtlInitUnicodeString(&defaultParamPath, NULL);
            defaultParamPath.MaximumLength = paramPath.Length + defaultParamStr.Length + sizeof(WCHAR);
            defaultParamPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, defaultParamPath.MaximumLength);
            if(!defaultParamPath.Buffer) {
                UDFPrint(("UDFCheckRegValue: couldn't allocate defaultParamPath\n"));
                try_return(val = DefValue);
            }

            RtlZeroMemory(defaultParamPath.Buffer, defaultParamPath.MaximumLength);
            status = RtlAppendUnicodeToString(&defaultParamPath, paramPath.Buffer);
            if(!NT_SUCCESS(status)) {
                try_return(val = DefValue);
            }
            status = RtlAppendUnicodeToString(&defaultParamPath, defaultParamStr.Buffer);
            if(!NT_SUCCESS(status)) {
                try_return(val = DefValue);
            }
            UDFPrint(("UDFCheckRegValue: (3) |%S|\n", defaultParamPath.Buffer));
        }

        if(PtrVolumePath) {
            paramSuffix = *PtrVolumePath;
        } else {
            RtlInitUnicodeString(&paramSuffix, NULL);
        }

        RtlInitUnicodeString(&paramDevPath, NULL);
        // now build the device specific path
        paramDevPath.MaximumLength = paramPath.Length + paramSuffix.Length + sizeof(WCHAR);
        paramDevPath.Buffer = (PWCH)MyAllocatePool__(PagedPool, paramDevPath.MaximumLength);
        if(!paramDevPath.Buffer) {
            try_return(val = DefValue);
        }

        RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength);
        status = RtlAppendUnicodeToString(&paramDevPath, paramPath.Buffer);
        if(!NT_SUCCESS(status)) {
            try_return(val = DefValue);
        }
        if(paramSuffix.Buffer) {
            status = RtlAppendUnicodeToString(&paramDevPath, paramSuffix.Buffer);
            if(!NT_SUCCESS(status)) {
                try_return(val = DefValue);
            }
        }

        UDFPrint(( " Parameter = %ws\n", Name));

        {
            HKEY hk = NULL;
            status = RegTGetKeyHandle(NULL, RegistryPath->Buffer, &hk);
            if(NT_SUCCESS(status)) {
                RegTCloseKeyHandle(hk);
            }
        }


        // *** Read GLOBAL_DEFAULTS from
        // "\DwUdf\Parameters_Unknown\"

        status = RegTGetDwordValue(NULL, paramPath.Buffer, Name, &val);

        // *** Read DEV_CLASS_SPEC_DEFAULTS (if any) from
        // "\DwUdf\Parameters_%DevClass%\"

        if(DefaultPath) {
            status = RegTGetDwordValue(NULL, defaultParamPath.Buffer, Name, &val);
        }

        // *** Read DEV_SPEC_PARAMS from (if device supports GetDevName)
        // "\DwUdf\Parameters\%DevName%\"

        status = RegTGetDwordValue(NULL, paramDevPath.Buffer, Name, &val);

try_exit:   NOTHING;

    } _SEH2_FINALLY {

        if(DefaultPath && defaultParamPath.Buffer) {
            MyFreePool__(defaultParamPath.Buffer);
        }
        if(paramPath.Buffer) {
            MyFreePool__(paramPath.Buffer);
        }
        if(paramDevPath.Buffer) {
            MyFreePool__(paramDevPath.Buffer);
        }
        if(paramPathUnknown.Buffer) {
            MyFreePool__(paramPathUnknown.Buffer);
        }
    } _SEH2_END;

    UDFPrint(( "UDFCheckRegValue: %ws for drive %s is %x\n\n", Name, PtrVolumePath, val));
    return val;
} // end UDFRegCheckParameterValue()

/*
Routine Description:
    This routine is called to initialize an IrpContext for the current
    UDFFS request.  The IrpContext is on the stack and we need to initialize
    it for the current request.  The request is a close operation.

Arguments:

    IrpContext - IrpContext to initialize.

    IrpContextLite - source for initialization

Return Value:

    None

*/
VOID
UDFInitializeIrpContextFromLite(
    OUT PtrUDFIrpContext    *IrpContext,
    IN PtrUDFIrpContextLite IrpContextLite
    )
{
    (*IrpContext) = UDFAllocateIrpContext(NULL, IrpContextLite->RealDevice);
    //  Zero and then initialize the structure.

    //  Major/Minor Function codes
    (*IrpContext)->MajorFunction = IRP_MJ_CLOSE;
    (*IrpContext)->Fcb = IrpContextLite->Fcb;
    (*IrpContext)->TreeLength = IrpContextLite->TreeLength;
    (*IrpContext)->IrpContextFlags |= (IrpContextLite->IrpContextFlags & ~UDF_IRP_CONTEXT_NOT_FROM_ZONE);

    //  Set the wait parameter
    UDFSetFlag( (*IrpContext)->IrpContextFlags, UDF_IRP_CONTEXT_CAN_BLOCK );

    return;
} // end UDFInitializeIrpContextFromLite()

/*
Routine Description:
    This routine is called to initialize an IrpContext for the current
    UDFFS request.  The IrpContext is on the stack and we need to initialize
    it for the current request.  The request is a close operation.

Arguments:

    IrpContext - IrpContext to initialize.

    IrpContextLite - source for initialization

Return Value:

    None

*/
NTSTATUS
UDFInitializeIrpContextLite(
    OUT PtrUDFIrpContextLite *IrpContextLite,
    IN PtrUDFIrpContext    IrpContext,
    IN PtrUDFFCB           Fcb
    )
{
    PtrUDFIrpContextLite LocalIrpContextLite = (PtrUDFIrpContextLite)MyAllocatePool__(NonPagedPool,sizeof(UDFIrpContextLite));
    if(!LocalIrpContextLite)
        return STATUS_INSUFFICIENT_RESOURCES;
    //  Zero and then initialize the structure.
    RtlZeroMemory( LocalIrpContextLite, sizeof( UDFIrpContextLite ));

    LocalIrpContextLite->NodeIdentifier.NodeType  = UDF_NODE_TYPE_IRP_CONTEXT_LITE;
    LocalIrpContextLite->NodeIdentifier.NodeSize  = sizeof(UDFIrpContextLite);

    LocalIrpContextLite->Fcb = Fcb;
    LocalIrpContextLite->TreeLength = IrpContext->TreeLength;
    //  Copy RealDevice for workque algorithms.
    LocalIrpContextLite->RealDevice = IrpContext->TargetDeviceObject;
    LocalIrpContextLite->IrpContextFlags = IrpContext->IrpContextFlags;
    *IrpContextLite = LocalIrpContextLite;

    return STATUS_SUCCESS;
} // end UDFInitializeIrpContextLite()

NTSTATUS
NTAPI
UDFQuerySetEA(
    PDEVICE_OBJECT DeviceObject,       // the logical volume device object
    PIRP           Irp                 // I/O Request Packet
    )
{
    NTSTATUS         RC = STATUS_SUCCESS;
//    PtrUDFIrpContext PtrIrpContext = NULL;
    BOOLEAN          AreWeTopLevel = FALSE;

    UDFPrint(("UDFQuerySetEA: \n"));

    FsRtlEnterFileSystem();
    ASSERT(DeviceObject);
    ASSERT(Irp);

    // set the top level context
    AreWeTopLevel = UDFIsIrpTopLevel(Irp);

    RC = STATUS_EAS_NOT_SUPPORTED;
    Irp->IoStatus.Status = RC;
    Irp->IoStatus.Information = 0;
    // complete the IRP
    IoCompleteRequest(Irp, IO_DISK_INCREMENT);

    if(AreWeTopLevel) {
        IoSetTopLevelIrp(NULL);
    }

    FsRtlExitFileSystem();

    return(RC);
} // end UDFQuerySetEA()

ULONG
UDFIsResourceAcquired(
    IN PERESOURCE Resource
    )
{
    ULONG ReAcqRes =
        ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
        (ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
    return ReAcqRes;
} // end UDFIsResourceAcquired()

BOOLEAN
UDFAcquireResourceExclusiveWithCheck(
    IN PERESOURCE Resource
    )
{
    ULONG ReAcqRes =
        ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
        (ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
    if(ReAcqRes) {
        UDFPrint(("UDFAcquireResourceExclusiveWithCheck: ReAcqRes, %x\n", ReAcqRes));
    } else {
//        BrutePoint();
    }

    if(ReAcqRes == 1) {
        // OK
    } else
    if(ReAcqRes == 2) {
        UDFPrint(("UDFAcquireResourceExclusiveWithCheck: !!! Shared !!!\n"));
        //BrutePoint();
    } else {
        UDFAcquireResourceExclusive(Resource, TRUE);
        return TRUE;
    }
    return FALSE;
} // end UDFAcquireResourceExclusiveWithCheck()

BOOLEAN
UDFAcquireResourceSharedWithCheck(
    IN PERESOURCE Resource
    )
{
    ULONG ReAcqRes =
        ExIsResourceAcquiredExclusiveLite(Resource) ? 1 :
        (ExIsResourceAcquiredSharedLite(Resource) ? 2 : 0);
    if(ReAcqRes) {
        UDFPrint(("UDFAcquireResourceSharedWithCheck: ReAcqRes, %x\n", ReAcqRes));
/*    } else {
        BrutePoint();*/
    }

    if(ReAcqRes == 2) {
        // OK
    } else
    if(ReAcqRes == 1) {
        UDFPrint(("UDFAcquireResourceSharedWithCheck: Exclusive\n"));
        //BrutePoint();
    } else {
        UDFAcquireResourceShared(Resource, TRUE);
        return TRUE;
    }
    return FALSE;
} // end UDFAcquireResourceSharedWithCheck()

NTSTATUS
UDFWCacheErrorHandler(
    IN PVOID Context,
    IN PWCACHE_ERROR_CONTEXT ErrorInfo
    )
{
    InterlockedIncrement((PLONG)&(((PVCB)Context)->IoErrorCounter));
    return ErrorInfo->Status;
}

#include "Include/misc_common.cpp"
#include "Include/regtools.cpp"