////////////////////////////////////////////////////////////////////
// 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: Devcntrl.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
*   Contains code to handle the "Device IOCTL" dispatch entry point.
*
*************************************************************************/

#include "udffs.h"

#include "CDRW/scsi_port.h"

#define UDF_CURRENT_BUILD 123456789

// define the file specific bug-check id
#ifdef UDF_BUG_CHECK_ID
#undef UDF_BUG_CHECK_ID
#endif
#define         UDF_BUG_CHECK_ID                UDF_FILE_DEVICE_CONTROL

NTSTATUS
UDFGetFileAllocModeFromICB(
    PtrUDFIrpContext PtrIrpContext,
    PIRP             Irp
    );

NTSTATUS
UDFSetFileAllocModeFromICB(
    PtrUDFIrpContext IrpContext,
    PIRP             Irp
    );

NTSTATUS
UDFProcessLicenseKey(
    PtrUDFIrpContext IrpContext,
    PIRP             Irp
    );

/*#if(_WIN32_WINNT < 0x0400)
#define IOCTL_REDIR_QUERY_PATH   CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 99, METHOD_NEITHER, FILE_ANY_ACCESS)

typedef struct _QUERY_PATH_REQUEST {
    ULONG PathNameLength;
    PIO_SECURITY_CONTEXT SecurityContext;
    WCHAR FilePathName[1];
} QUERY_PATH_REQUEST, *PQUERY_PATH_REQUEST;

typedef struct _QUERY_PATH_RESPONSE {
    ULONG LengthAccepted;
} QUERY_PATH_RESPONSE, *PQUERY_PATH_RESPONSE;

#endif*/


/*************************************************************************
*
* Function: UDFDeviceControl()
*
* Description:
*   The I/O Manager will invoke this routine to handle a Device IOCTL
*   request
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
*   to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS
NTAPI
UDFDeviceControl(
    PDEVICE_OBJECT          DeviceObject,       // the logical volume device object
    PIRP                    Irp)                // I/O Request Packet
{
    NTSTATUS            RC = STATUS_SUCCESS;
    PtrUDFIrpContext    PtrIrpContext = NULL;
    BOOLEAN             AreWeTopLevel = FALSE;

    TmPrint(("UDFDeviceControl: \n"));

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

    // set the top level context
    AreWeTopLevel = UDFIsIrpTopLevel(Irp);
    //ASSERT(!UDFIsFSDevObj(DeviceObject));

    _SEH2_TRY {

        // get an IRP context structure and issue the request
        PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
        if(PtrIrpContext) {
            RC = UDFCommonDeviceControl(PtrIrpContext, Irp);
        } else {
            RC = STATUS_INSUFFICIENT_RESOURCES;
            Irp->IoStatus.Status = RC;
            Irp->IoStatus.Information = 0;
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
        }

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

        RC = UDFExceptionHandler(PtrIrpContext, Irp);

        UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
    } _SEH2_END;

    if (AreWeTopLevel) {
        IoSetTopLevelIrp(NULL);
    }

    FsRtlExitFileSystem();

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


/*************************************************************************
*
* Function: UDFCommonDeviceControl()
*
* Description:
*   The actual work is performed here. This routine may be invoked in one'
*   of the two possible contexts:
*   (a) in the context of a system worker thread
*   (b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS
NTAPI
UDFCommonDeviceControl(
    PtrUDFIrpContext PtrIrpContext,
    PIRP             Irp
    )
{
    NTSTATUS                RC = STATUS_SUCCESS;
    PIO_STACK_LOCATION      IrpSp = NULL;
//    PIO_STACK_LOCATION      PtrNextIoStackLocation = NULL;
    PFILE_OBJECT            FileObject = NULL;
    PtrUDFFCB               Fcb = NULL;
    PtrUDFCCB               Ccb = NULL;
    PVCB                    Vcb = NULL;
    BOOLEAN                 CompleteIrp = FALSE;
    ULONG                   IoControlCode = 0;
//    PVOID                   BufferPointer = NULL;
    BOOLEAN                 AcquiredVcb = FALSE;
    BOOLEAN                 FSDevObj;
    ULONG                   TrackNumber;
    BOOLEAN                 UnsafeIoctl = TRUE;
    UCHAR                   ScsiCommand;
    PPREVENT_MEDIA_REMOVAL_USER_IN Buf = NULL;    // FSD buffer
    PCDB                    Cdb;
    PCHAR                   CdbData;
    PCHAR                   ModeSelectData;

    UDFPrint(("UDFCommonDeviceControl\n"));

    _SEH2_TRY {
        // First, get a pointer to the current I/O stack location
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        ASSERT(IrpSp);

        // Get the IoControlCode value
        IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;

        FileObject = IrpSp->FileObject;
        ASSERT(FileObject);

        FSDevObj = UDFIsFSDevObj(PtrIrpContext->TargetDeviceObject);

        if(FSDevObj) {
            switch (IoControlCode) {
            case IOCTL_UDF_DISABLE_DRIVER:
            case IOCTL_UDF_INVALIDATE_VOLUMES:
            case IOCTL_UDF_SET_NOTIFICATION_EVENT:
#ifndef UDF_READ_ONLY_BUILD
            case IOCTL_UDF_SEND_LICENSE_KEY:
#endif //UDF_READ_ONLY_BUILD
            case IOCTL_UDF_REGISTER_AUTOFORMAT:
                break;
            default:
                UDFPrint(("UDFCommonDeviceControl: STATUS_INVALID_PARAMETER %x for FsDevObj\n", IoControlCode));
                CompleteIrp = TRUE;
                try_return(RC = STATUS_INVALID_PARAMETER);
            }
        } else {
            Ccb = (PtrUDFCCB)(FileObject->FsContext2);
            if(!Ccb) {
                UDFPrint(("  !Ccb\n"));
                goto ioctl_do_default;
            }
            ASSERT(Ccb);
            Fcb = Ccb->Fcb;
            ASSERT(Fcb);

            // Check if the IOCTL is suitable for this type of File
            if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
                // Everything is acceptable for Volume
                Vcb = (PVCB)(Fcb);
            } else {
                Vcb = Fcb->Vcb;
                CompleteIrp = TRUE;
                // For files/disrs only the following are acceptable
                switch (IoControlCode) {
                case IOCTL_UDF_GET_RETRIEVAL_POINTERS:
                case IOCTL_UDF_GET_FILE_ALLOCATION_MODE:
                case IOCTL_UDF_SET_FILE_ALLOCATION_MODE:
                    break;
                default:
                    UDFPrint(("UDFCommonDeviceControl: STATUS_INVALID_PARAMETER %x for File/Dir Obj\n", IoControlCode));
                    try_return(RC = STATUS_INVALID_PARAMETER);
                }
            }
            // check 'safe' IOCTLs
            switch (IoControlCode) {
            case IOCTL_CDROM_RAW_READ:

            case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
            case IOCTL_DISK_GET_DRIVE_GEOMETRY:
            case IOCTL_DISK_GET_PARTITION_INFO:
            case IOCTL_DISK_GET_DRIVE_LAYOUT:

            case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
            case IOCTL_DISK_GET_PARTITION_INFO_EX:
            case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
            case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:

            case IOCTL_STORAGE_CHECK_VERIFY:
            case IOCTL_STORAGE_CHECK_VERIFY2:
            case IOCTL_DISK_CHECK_VERIFY:
            case IOCTL_CDROM_CHECK_VERIFY:

            case IOCTL_CDROM_LOAD_MEDIA:
            case IOCTL_DISK_LOAD_MEDIA:
            case IOCTL_STORAGE_LOAD_MEDIA:
            case IOCTL_STORAGE_LOAD_MEDIA2:

            case IOCTL_CDROM_GET_CONFIGURATION:
            case IOCTL_CDROM_GET_LAST_SESSION:
            case IOCTL_CDROM_READ_TOC:
            case IOCTL_CDROM_READ_TOC_EX:
            case IOCTL_CDROM_PLAY_AUDIO_MSF:
            case IOCTL_CDROM_READ_Q_CHANNEL:
            case IOCTL_CDROM_PAUSE_AUDIO:
            case IOCTL_CDROM_RESUME_AUDIO:
            case IOCTL_CDROM_SEEK_AUDIO_MSF:
            case IOCTL_CDROM_STOP_AUDIO:
            case IOCTL_CDROM_GET_CONTROL:
            case IOCTL_CDROM_GET_VOLUME:
            case IOCTL_CDROM_SET_VOLUME:

            case IOCTL_CDRW_SET_SPEED:
            case IOCTL_CDRW_GET_CAPABILITIES:
            case IOCTL_CDRW_GET_MEDIA_TYPE_EX:
            case IOCTL_CDRW_GET_MEDIA_TYPE:

            case IOCTL_DISK_GET_MEDIA_TYPES:
            case IOCTL_STORAGE_GET_MEDIA_TYPES:
            case IOCTL_STORAGE_GET_MEDIA_TYPES_EX:

            case IOCTL_DISK_IS_WRITABLE:

            case IOCTL_CDRW_GET_WRITE_MODE:
            case IOCTL_CDRW_READ_TRACK_INFO:
            case IOCTL_CDRW_READ_DISC_INFO:
            case IOCTL_CDRW_BUFFER_CAPACITY:
            case IOCTL_CDRW_GET_SIGNATURE:
            case IOCTL_CDRW_TEST_UNIT_READY:
            case IOCTL_CDRW_GET_LAST_ERROR:
            case IOCTL_CDRW_MODE_SENSE:
            case IOCTL_CDRW_LL_READ:
            case IOCTL_CDRW_READ_ATIP:
            case IOCTL_CDRW_READ_CD_TEXT:
            case IOCTL_CDRW_READ_TOC_EX:
            case IOCTL_CDRW_READ_FULL_TOC:
            case IOCTL_CDRW_READ_PMA:
            case IOCTL_CDRW_READ_SESSION_INFO:
            case IOCTL_CDRW_GET_DEVICE_INFO:
            case IOCTL_CDRW_GET_EVENT:

            case IOCTL_DVD_READ_STRUCTURE:

            case IOCTL_CDRW_GET_DEVICE_NAME:
            case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:

            case IOCTL_UDF_GET_RETRIEVAL_POINTERS:
            case IOCTL_UDF_GET_SPEC_RETRIEVAL_POINTERS:
            case IOCTL_UDF_GET_FILE_ALLOCATION_MODE:
            case IOCTL_UDF_GET_VERSION:
            case IOCTL_UDF_IS_VOLUME_JUST_MOUNTED:
            case IOCTL_UDF_SET_OPTIONS:
//            case :

            case FSCTL_IS_VOLUME_DIRTY:

                UnsafeIoctl = FALSE;
                break;
            }

            if(IoControlCode != IOCTL_CDROM_DISK_TYPE) {
                UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
            } else {
                UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
            }
            AcquiredVcb = TRUE;
        }

        UDFPrint(("UDF Irp %x, ctx %x, DevIoCtl %x\n", Irp, PtrIrpContext, IoControlCode));

        // We may wish to allow only   volume open operations.
        switch (IoControlCode) {

        case IOCTL_SCSI_PASS_THROUGH_DIRECT:
        case IOCTL_SCSI_PASS_THROUGH:

            if(!Irp->AssociatedIrp.SystemBuffer)
                goto ioctl_do_default;

            if(IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) {
                Cdb = (PCDB)&(((PSCSI_PASS_THROUGH_DIRECT)(Irp->AssociatedIrp.SystemBuffer))->Cdb);
                CdbData = (PCHAR)(((PSCSI_PASS_THROUGH_DIRECT)(Irp->AssociatedIrp.SystemBuffer))->DataBuffer);
            } else {
                Cdb = (PCDB)&(((PSCSI_PASS_THROUGH)(Irp->AssociatedIrp.SystemBuffer))->Cdb);
                if(((PSCSI_PASS_THROUGH)(Irp->AssociatedIrp.SystemBuffer))->DataBufferOffset) {
                    CdbData = ((PCHAR)Cdb) + 
                              ((PSCSI_PASS_THROUGH)(Irp->AssociatedIrp.SystemBuffer))->DataBufferOffset;
                } else {
                    CdbData = NULL;
                }
            }
            ScsiCommand = Cdb->CDB6.OperationCode;

            if(ScsiCommand == SCSIOP_WRITE_CD) {
                UDFPrint(("Write10, LBA %2.2x%2.2x%2.2x%2.2x\n",
                         Cdb->WRITE_CD.LBA[0],
                         Cdb->WRITE_CD.LBA[1],
                         Cdb->WRITE_CD.LBA[2],
                         Cdb->WRITE_CD.LBA[3]
                         ));
            } else
            if(ScsiCommand == SCSIOP_WRITE12) {
                UDFPrint(("Write12, LBA %2.2x%2.2x%2.2x%2.2x\n",
                         Cdb->CDB12READWRITE.LBA[0],
                         Cdb->CDB12READWRITE.LBA[1],
                         Cdb->CDB12READWRITE.LBA[2],
                         Cdb->CDB12READWRITE.LBA[3]
                         ));
            } else {
            }

            switch(ScsiCommand) {
            case SCSIOP_MODE_SELECT: {
//                PMODE_PARAMETER_HEADER ParamHdr = (PMODE_PARAMETER_HEADER)CdbData;
                ModeSelectData = CdbData+4;
                switch(ModeSelectData[0]) {
                case MODE_PAGE_MRW2:
                case MODE_PAGE_WRITE_PARAMS:
                case MODE_PAGE_MRW:
                    UDFPrint(("Unsafe MODE_SELECT_6 via pass-through (%2.2x)\n", ModeSelectData[0]));
                    goto unsafe_direct_scsi_cmd;
                }
                break; }

            case SCSIOP_MODE_SELECT10: {
//                PMODE_PARAMETER_HEADER10 ParamHdr = (PMODE_PARAMETER_HEADER10)CdbData;
                ModeSelectData = CdbData+8;
                switch(ModeSelectData[0]) {
                case MODE_PAGE_MRW2:
                case MODE_PAGE_WRITE_PARAMS:
                case MODE_PAGE_MRW:
                    UDFPrint(("Unsafe MODE_SELECT_10 via pass-through (%2.2x)\n", ModeSelectData[0]));
                    goto unsafe_direct_scsi_cmd;
                }
                break; }

            case SCSIOP_RESERVE_TRACK:
            case SCSIOP_SEND_CUE_SHEET:
            case SCSIOP_SEND_DVD_STRUCTURE:
            case SCSIOP_CLOSE_TRACK_SESSION:
            case SCSIOP_FORMAT_UNIT:
            case SCSIOP_WRITE6:
            case SCSIOP_WRITE_CD:
            case SCSIOP_BLANK:
            case SCSIOP_WRITE12:
            case SCSIOP_SET_STREAMING:
                UDFPrint(("UDF Direct media modification via pass-through (%2.2x)\n", ScsiCommand));
unsafe_direct_scsi_cmd:
                if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED))
                    goto ioctl_do_default;

                UDFPrint(("Forget this volume\n"));
                // Acquire Vcb resource (Shared -> Exclusive)
                UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount));
                UDFReleaseResource(&(Vcb->VCBResource));

                if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) {
                    UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
                }
#ifdef UDF_DELAYED_CLOSE
                //  Acquire exclusive access to the Vcb.
                UDFCloseAllDelayed(Vcb);
#endif //UDF_DELAYED_CLOSE

                // allocate tmp buffer for FSD calls
                Buf = (PPREVENT_MEDIA_REMOVAL_USER_IN)MyAllocatePool__(NonPagedPool, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN));
                if(!Buf)
                    try_return(RC = STATUS_INSUFFICIENT_RESOURCES);

                UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
                AcquiredVcb = TRUE;
                UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));

                UDFDoDismountSequence(Vcb, Buf, FALSE);
                MyFreePool__(Buf);
                Buf = NULL;
                Vcb->MediaLockCount = 0;

                Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;
                Vcb->WriteSecurity = FALSE;

                // Release the Vcb resource.
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
                // disable Eject Request Waiter if any
                UDFStopEjectWaiter(Vcb);

                // Make sure, that volume will never be quick-remounted
                // It is very important for ChkUdf utility and
                // some CD-recording libraries
                Vcb->SerialNumber--;

                UDFPrint(("Forgotten\n"));

                goto notify_media_change;

            case SCSIOP_START_STOP_UNIT:
            case SCSIOP_DOORLOCK:
            case SCSIOP_DOORUNLOCK:
            case SCSIOP_MEDIUM_REMOVAL:
                UDFPrint(("UDF Medium/Tray control IOCTL via pass-through\n"));
            }
            goto ioctl_do_default;

        case IOCTL_CDRW_BLANK:
        case IOCTL_CDRW_LL_WRITE:
        case IOCTL_CDRW_FORMAT_UNIT:

notify_media_change:
/*            Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
            // Make sure, that volume will never be quick-remounted
            // It is very important for ChkUdf utility and
            // some CD-recording libraries
            Vcb->SerialNumber--;
*/          goto ioctl_do_default;

        case IOCTL_UDF_REGISTER_AUTOFORMAT: {

            UDFPrint(("UDF Register Autoformat\n"));
            if(UDFGlobalData.AutoFormatCount) {
                RC = STATUS_SHARING_VIOLATION;
            } else {
                UDFGlobalData.AutoFormatCount = FileObject;
                RC = STATUS_SUCCESS;
            }
            CompleteIrp = TRUE;
            Irp->IoStatus.Information = 0;
            break;
        }

        case IOCTL_UDF_DISABLE_DRIVER: {

            UDFPrint(("UDF Disable driver\n"));
            IoUnregisterFileSystem(UDFGlobalData.UDFDeviceObject);
            // Now, delete any device objects, etc. we may have created
            if (UDFGlobalData.UDFDeviceObject) {
                IoDeleteDevice(UDFGlobalData.UDFDeviceObject);
                UDFGlobalData.UDFDeviceObject = NULL;
            }

            // free up any memory we might have reserved for zones/lookaside
            //  lists
            if (UDFGlobalData.UDFFlags & UDF_DATA_FLAGS_ZONES_INITIALIZED) {
                UDFDestroyZones();
            }

            // delete the resource we may have initialized
            if (UDFGlobalData.UDFFlags & UDF_DATA_FLAGS_RESOURCE_INITIALIZED) {
                // un-initialize this resource
                UDFDeleteResource(&(UDFGlobalData.GlobalDataResource));
                UDFClearFlag(UDFGlobalData.UDFFlags, UDF_DATA_FLAGS_RESOURCE_INITIALIZED);
            }
            RC = STATUS_SUCCESS;
            CompleteIrp = TRUE;
            Irp->IoStatus.Information = 0;
            break;
        }
        case IOCTL_UDF_INVALIDATE_VOLUMES: {
            UDFPrint(("UDF Invaidate volume\n"));
            if(AcquiredVcb) {
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
            }
            RC = UDFInvalidateVolumes( PtrIrpContext, Irp );
            CompleteIrp = TRUE;
            Irp->IoStatus.Information = 0;
            break;
        }

        case IOCTL_UDF_SET_NOTIFICATION_EVENT:
        {
            if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(HANDLE))
            {
                RC = STATUS_INVALID_PARAMETER;
            }
            else
            {
                HANDLE MountEventHandle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
                if (MountEventHandle)
                {
                    if (!UDFGlobalData.MountEvent)
                    {
                        RC = ObReferenceObjectByHandle(
                            MountEventHandle,
                            0,
                            NULL,
                            UserMode,
                            (PVOID *) &UDFGlobalData.MountEvent,
                            NULL);
                            
                        if (!NT_SUCCESS(RC))
                        {
                            UDFGlobalData.MountEvent = NULL;
                        }
                    }
                    else
                    {
                        RC = STATUS_INVALID_PARAMETER;
                    }
                }
                else
                {
                    if (!UDFGlobalData.MountEvent)
                    {
                        RC = STATUS_INVALID_PARAMETER;
                    }
                    else
                    {
                        ObDereferenceObject(UDFGlobalData.MountEvent);
                        UDFGlobalData.MountEvent = NULL;
                    }
                }
            }

            CompleteIrp = TRUE;
            Irp->IoStatus.Information = 0;
            break;
        }

        case IOCTL_UDF_IS_VOLUME_JUST_MOUNTED:
        {
            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(BOOLEAN))
            {
                RC = STATUS_INVALID_PARAMETER;
            }
            else
            {
                *(PBOOLEAN)Irp->AssociatedIrp.SystemBuffer = Vcb->IsVolumeJustMounted;
                Vcb->IsVolumeJustMounted = FALSE;
            }
            
            CompleteIrp = TRUE;
            Irp->IoStatus.Information = 0;
            break;
        }


        //case FSCTL_GET_RETRIEVAL_POINTERS
        case IOCTL_UDF_GET_RETRIEVAL_POINTERS: {
            UDFPrint(("UDF: Get Retrieval Pointers\n"));
            RC = UDFGetRetrievalPointers( PtrIrpContext, Irp, 0 );
            CompleteIrp = TRUE;
            break;
        }
        case IOCTL_UDF_GET_SPEC_RETRIEVAL_POINTERS: {
            UDFPrint(("UDF: Get Spec Retrieval Pointers\n"));
            PUDF_GET_SPEC_RETRIEVAL_POINTERS_IN SpecRetrPointer;
            SpecRetrPointer = (PUDF_GET_SPEC_RETRIEVAL_POINTERS_IN)(Irp->AssociatedIrp.SystemBuffer);
            RC = UDFGetRetrievalPointers( PtrIrpContext, Irp, SpecRetrPointer->Special );
            CompleteIrp = TRUE;
            break;
        }
        case IOCTL_UDF_GET_FILE_ALLOCATION_MODE: {
            UDFPrint(("UDF: Get File Alloc mode (from ICB)\n"));
            RC = UDFGetFileAllocModeFromICB( PtrIrpContext, Irp );
            CompleteIrp = TRUE;
            break;
        }
#ifndef UDF_READ_ONLY_BUILD
        case IOCTL_UDF_SET_FILE_ALLOCATION_MODE: {
            UDFPrint(("UDF: Set File Alloc mode\n"));
            RC = UDFSetFileAllocModeFromICB( PtrIrpContext, Irp );
            CompleteIrp = TRUE;
            break;
        }
#endif //UDF_READ_ONLY_BUILD
        case IOCTL_UDF_LOCK_VOLUME_BY_PID:
            if(AcquiredVcb) {
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
            }
            RC = UDFLockVolume( PtrIrpContext, Irp, GetCurrentPID() );
            CompleteIrp = TRUE;
            break;
        case IOCTL_UDF_UNLOCK_VOLUME_BY_PID:
            if(AcquiredVcb) {
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
            }
            RC = UDFUnlockVolume( PtrIrpContext, Irp, GetCurrentPID() );
            CompleteIrp = TRUE;
            break;
#ifndef UDF_READ_ONLY_BUILD
        case IOCTL_UDF_SEND_LICENSE_KEY:
            RC = STATUS_SUCCESS;

            Irp->IoStatus.Information = 0;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            CompleteIrp = TRUE;
            break;
#endif //UDF_READ_ONLY_BUILD
        case IOCTL_UDF_GET_VERSION: {

            PUDF_GET_VERSION_OUT udf_ver;

            UDFPrint(("UDFUserFsCtrlRequest: IOCTL_UDF_GET_VERSION\n"));

            Irp->IoStatus.Information = 0;
            CompleteIrp = TRUE;

            if(!IrpSp->Parameters.DeviceIoControl.OutputBufferLength) {
                UDFPrint(("!OutputBufferLength\n"));
                try_return(RC = STATUS_SUCCESS);
            }
            //  Check the size of the output buffer.
            if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(UDF_GET_VERSION_OUT)) {
                UDFPrint(("OutputBufferLength < %x\n", sizeof(UDF_GET_VERSION_OUT)));
                try_return(RC = STATUS_BUFFER_TOO_SMALL);
            }

            udf_ver = (PUDF_GET_VERSION_OUT)(Irp->AssociatedIrp.SystemBuffer);
            if(!udf_ver) {
                UDFPrint(("!udf_ver\n"));
                try_return(RC = STATUS_INVALID_USER_BUFFER);
            }

            RtlZeroMemory(udf_ver, IrpSp->Parameters.DeviceIoControl.OutputBufferLength);

            udf_ver->header.Length = sizeof(UDF_GET_VERSION_OUT);
            udf_ver->header.DriverVersionMj    = 0x00010005;
            udf_ver->header.DriverVersionMn    = 0x12;
            udf_ver->header.DriverVersionBuild = UDF_CURRENT_BUILD;

            udf_ver->FSVersionMj = Vcb->CurrentUDFRev >> 8;
            udf_ver->FSVersionMn = Vcb->CurrentUDFRev & 0xff;
            udf_ver->FSFlags     = Vcb->UserFSFlags;
            if( ((Vcb->origIntegrityType == INTEGRITY_TYPE_OPEN) &&
                (Vcb->CompatFlags & UDF_VCB_IC_DIRTY_RO)) 
                    ||
               (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) ) {
                UDFPrint(("  UDF_USER_FS_FLAGS_RO\n"));
                udf_ver->FSFlags |= UDF_USER_FS_FLAGS_RO;
            }
            if(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) {
                UDFPrint(("  UDF_USER_FS_FLAGS_OUR_DRIVER\n"));
                udf_ver->FSFlags |= UDF_USER_FS_FLAGS_OUR_DRIVER;
            }
            if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) {
                UDFPrint(("  UDF_USER_FS_FLAGS_RAW\n"));
                udf_ver->FSFlags |= UDF_USER_FS_FLAGS_RAW;
            }
            if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) {
                UDFPrint(("  UDF_USER_FS_FLAGS_MEDIA_RO\n"));
                udf_ver->FSFlags |= UDF_USER_FS_FLAGS_MEDIA_RO;
            }
            if(Vcb->FP_disc) {
                UDFPrint(("  UDF_USER_FS_FLAGS_FP\n"));
                udf_ver->FSFlags |= UDF_USER_FS_FLAGS_FP;
            }
            udf_ver->FSCompatFlags = Vcb->CompatFlags;

            udf_ver->FSCfgVersion = Vcb->CfgVersion;

            Irp->IoStatus.Information = sizeof(UDF_GET_VERSION_OUT);
            RC = STATUS_SUCCESS;
            CompleteIrp = TRUE;

            break; }
        case IOCTL_UDF_SET_OPTIONS: {

            PUDF_SET_OPTIONS_IN udf_opt;
            BOOLEAN PrevVerifyOnWrite;

            UDFPrint(("UDF: IOCTL_UDF_SET_OPTIONS\n"));

            Irp->IoStatus.Information = 0;
            CompleteIrp = TRUE;

            if(IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(UDF_SET_OPTIONS_IN)) {
                UDFPrint(("InputBufferLength < %x\n", sizeof(UDF_SET_OPTIONS_IN)));
                try_return(RC = STATUS_BUFFER_TOO_SMALL);
            }

            udf_opt = (PUDF_SET_OPTIONS_IN)(Irp->AssociatedIrp.SystemBuffer);
            if(!udf_opt) {
                UDFPrint(("!udf_opt\n"));
                try_return(RC = STATUS_INVALID_USER_BUFFER);
            }

            if((udf_opt->header.Flags & UDF_SET_OPTIONS_FLAG_MASK) != UDF_SET_OPTIONS_FLAG_TEMPORARY) {
                UDFPrint(("invalid opt target\n"));
                try_return(RC = STATUS_INVALID_PARAMETER);
            }

            if(AcquiredVcb) {
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
            }
            UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
            AcquiredVcb = TRUE;

            PrevVerifyOnWrite = Vcb->VerifyOnWrite;

            Vcb->Cfg = ((PUCHAR)(udf_opt)) + udf_opt->header.HdrLength;
            Vcb->CfgLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength - offsetof(UDF_SET_OPTIONS_IN, Data);
            UDFReadRegKeys(Vcb, TRUE /*update*/, TRUE /*cfg*/);
            Vcb->Cfg = NULL;
            Vcb->CfgLength = 0;
            Vcb->CfgVersion++;
            //UDFReadRegKeys(Vcb, TRUE /*update*/, TRUE);
            if(PrevVerifyOnWrite != Vcb->VerifyOnWrite) {
                if(Vcb->VerifyOnWrite) {
                    UDFVInit(Vcb);
                } else {
                    WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA);
                    UDFVFlush(Vcb);
                    UDFVRelease(Vcb);
                }
            }

            RC = STATUS_SUCCESS;
            break; }
#if 0
        case IOCTL_UDF_GET_OPTIONS_VERSION: {

            PUDF_GET_OPTIONS_VERSION_OUT udf_opt_ver;

            UDFPrint(("UDF: IOCTL_UDF_GET_OPTIONS_VERSION\n"));

            Irp->IoStatus.Information = 0;
            CompleteIrp = TRUE;

            if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(UDF_GET_OPTIONS_VERSION_OUT)) {
                UDFPrint(("OutputBufferLength < %x\n", sizeof(UDF_GET_OPTIONS_VERSION_OUT)));
                try_return(RC = STATUS_BUFFER_TOO_SMALL);
            }

            udf_opt_ver = (PUDF_GET_OPTIONS_VERSION_OUT)(Irp->AssociatedIrp.SystemBuffer);
            if(!udf_opt_ver) {
                UDFPrint(("!udf_opt-ver\n"));
                try_return(RC = STATUS_INVALID_USER_BUFFER);
            }
/*
            if(AcquiredVcb) {
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
            }
            UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
            AcquiredVcb = TRUE;
*/
            udf_opt_ver->CfgVersion = Vcb->CfgVersion;
            Irp->IoStatus.Information = sizeof(UDF_GET_OPTIONS_VERSION_OUT);

            RC = STATUS_SUCCESS;
            break; }
#endif //0
        case IOCTL_CDRW_RESET_DRIVER:

            UDFPrint(("UDF: IOCTL_CDRW_RESET_DRIVER\n"));
            Vcb->MediaLockCount = 0;
            Vcb->VCBFlags &= ~UDF_VCB_FLAGS_MEDIA_LOCKED;
            goto ioctl_do_default;

        case FSCTL_ALLOW_EXTENDED_DASD_IO:

            UDFPrint(("UDFUserFsCtrlRequest: FSCTL_ALLOW_EXTENDED_DASD_IO\n"));
            // DASD i/o is always permitted
            // So, no-op this call
            RC = STATUS_SUCCESS;

            Irp->IoStatus.Information = 0;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            CompleteIrp = TRUE;
            break;

        case FSCTL_IS_VOLUME_DIRTY:

            UDFPrint(("UDFUserFsCtrlRequest: FSCTL_IS_VOLUME_DIRTY\n"));
            // DASD i/o is always permitted
            // So, no-op this call
            RC = UDFIsVolumeDirty(PtrIrpContext, Irp);
            CompleteIrp = TRUE;
            break;

        case IOCTL_STORAGE_EJECT_MEDIA:
        case IOCTL_DISK_EJECT_MEDIA:
        case IOCTL_CDROM_EJECT_MEDIA: {

            UDFPrint(("UDF Reset/Eject request\n"));
//            PPREVENT_MEDIA_REMOVAL_USER_IN Buf;

            if(Vcb->EjectWaiter) {
                UDFPrint(("  Vcb->EjectWaiter present\n"));
                Irp->IoStatus.Information = 0;
                Vcb->EjectWaiter->SoftEjectReq = TRUE;
                Vcb->SoftEjectReq = TRUE;
                CompleteIrp = TRUE;
                try_return(RC = STATUS_SUCCESS);
            }
            UDFPrint(("  !Vcb->EjectWaiter\n"));
            goto ioctl_do_default;
/*
            Buf = (PPREVENT_MEDIA_REMOVAL_USER_IN)MyAllocatePool__(NonPagedPool, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN));
            if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
            // Acquire Vcb resource (Shared -> Exclusive)
            UDFReleaseResource(&(Vcb->VCBResource));
            UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);

            Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME;
            Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;

            UDFDoDismountSequence(Vcb, Buf, IoControlCode == IOCTL_CDROM_EJECT_MEDIA);
            // disable Eject Request Waiter if any
            MyFreePool__(Buf);
            // Release the Vcb resource.
            UDFReleaseResource(&(Vcb->VCBResource));
            AcquiredVcb = FALSE;
            UDFStopEjectWaiter(Vcb);
            CompleteIrp = TRUE;
            RC = STATUS_SUCCESS;
            break;*/
        }
        case IOCTL_CDROM_DISK_TYPE: {

            UDFPrint(("UDF Cdrom Disk Type\n"));
            CompleteIrp = TRUE;
            //  Verify the Vcb in this case to detect if the volume has changed.
            Irp->IoStatus.Information = 0;
            RC = UDFVerifyVcb(PtrIrpContext,Vcb);
            if(!NT_SUCCESS(RC))
                try_return(RC);

            //  Check the size of the output buffer.
            if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_DISK_DATA_USER_OUT))
                try_return(RC = STATUS_BUFFER_TOO_SMALL);

            //  Copy the data from the Vcb.
            ((PCDROM_DISK_DATA_USER_OUT)(Irp->AssociatedIrp.SystemBuffer))->DiskData = CDROM_DISK_DATA_TRACK;
            for(TrackNumber=Vcb->FirstTrackNum; TrackNumber<Vcb->LastTrackNum; TrackNumber++) {
                if((Vcb->TrackMap[TrackNumber].TrackParam & Trk_QSubChan_Type_Mask) ==
                    Trk_QSubChan_Type_Audio) {
                    ((PCDROM_DISK_DATA_USER_OUT)(Irp->AssociatedIrp.SystemBuffer))->DiskData |= CDROM_DISK_AUDIO_TRACK;
                    break;
                }
            }

            Irp->IoStatus.Information = sizeof(CDROM_DISK_DATA_USER_OUT);
            RC = STATUS_SUCCESS;
            break;
        }

        case IOCTL_CDRW_LOCK_DOOR:
        case IOCTL_STORAGE_MEDIA_REMOVAL:
        case IOCTL_DISK_MEDIA_REMOVAL:
        case IOCTL_CDROM_MEDIA_REMOVAL: {
            UDFPrint(("UDF Lock/Unlock\n"));
            PPREVENT_MEDIA_REMOVAL_USER_IN buffer; // user supplied buffer
            buffer = (PPREVENT_MEDIA_REMOVAL_USER_IN)(Irp->AssociatedIrp.SystemBuffer);
            if(!buffer) {
                if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) {
                    UDFPrint(("!mounted\n"));
                    goto ioctl_do_default;
                }
                UDFPrint(("abort\n"));
                CompleteIrp = TRUE;
                Irp->IoStatus.Information = 0;
                UnsafeIoctl = FALSE;
                RC = STATUS_INVALID_PARAMETER;
                break;
            }
            if(!buffer->PreventMediaRemoval &&
               !Vcb->MediaLockCount) {

                UDFPrint(("!locked + unlock req\n"));
                if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) {
                    UDFPrint(("!mounted\n"));
                    goto ioctl_do_default;
                }
#if 0
                // allocate tmp buffer for FSD calls
                Buf = (PPREVENT_MEDIA_REMOVAL_USER_IN)MyAllocatePool__(NonPagedPool, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN));
                if(!Buf)
                    try_return(RC = STATUS_INSUFFICIENT_RESOURCES);

                // Acquire Vcb resource (Shared -> Exclusive)
                UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount));
                UDFReleaseResource(&(Vcb->VCBResource));

#ifdef UDF_DELAYED_CLOSE
                //  Acquire exclusive access to the Vcb.
                UDFCloseAllDelayed(Vcb);
#endif //UDF_DELAYED_CLOSE

                UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
                UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));

                UDFDoDismountSequence(Vcb, Buf, FALSE);
                MyFreePool__(Buf);
                Buf = NULL;
                Vcb->MediaLockCount = 0;
                // Release the Vcb resource.
                UDFReleaseResource(&(Vcb->VCBResource));
                AcquiredVcb = FALSE;
                // disable Eject Request Waiter if any
                UDFStopEjectWaiter(Vcb);
#else
                // just ignore
#endif
ignore_lock:
                UDFPrint(("ignore lock/unlock\n"));
                CompleteIrp = TRUE;
                Irp->IoStatus.Information = 0;
                RC = STATUS_SUCCESS;
                break;
            }
            if(buffer->PreventMediaRemoval) {
                UDFPrint(("lock req\n"));
                Vcb->MediaLockCount++;
                Vcb->VCBFlags |= UDF_VCB_FLAGS_MEDIA_LOCKED;
                UnsafeIoctl = FALSE;
            } else {
                UDFPrint(("unlock req\n"));
                if(Vcb->MediaLockCount) {
                    UDFPrint(("lock count %d\n", Vcb->MediaLockCount));
                    UnsafeIoctl = FALSE;
                    Vcb->MediaLockCount--;
                }
            }
            if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) {
                UDFPrint(("!mounted\n"));
                goto ioctl_do_default;
            }
            goto ignore_lock;
        }
        default:

            UDFPrint(("default processing Irp %x, ctx %x, DevIoCtl %x\n", Irp, PtrIrpContext, IoControlCode));
ioctl_do_default:

            // make sure volume is Sync'ed BEFORE sending unsafe IOCTL
            if(Vcb && UnsafeIoctl) {
                UDFFlushLogicalVolume(NULL, NULL, Vcb, 0);
                UDFPrint(("  sync'ed\n"));
            }
            // Invoke the lower level driver in the chain.
            //PtrNextIoStackLocation = IoGetNextIrpStackLocation(Irp);
            //*PtrNextIoStackLocation = *IrpSp;
            IoSkipCurrentIrpStackLocation(Irp);
/*
            // Set a completion routine.
            IoSetCompletionRoutine(Irp, UDFDevIoctlCompletion, PtrIrpContext, TRUE, TRUE, TRUE);
            // Send the request.
*/
            RC = IoCallDriver(Vcb->TargetDeviceObject, Irp);
            if(!CompleteIrp) {
                // since now we do not use IoSetCompletionRoutine()
                UDFReleaseIrpContext(PtrIrpContext);
            }
            break;
        }

        if(Vcb && UnsafeIoctl) {
            UDFPrint(("  set UnsafeIoctl\n"));
            Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
        }

try_exit: NOTHING;

    } _SEH2_FINALLY {

        if(AcquiredVcb) {
            UDFReleaseResource(&(Vcb->VCBResource));
            AcquiredVcb = FALSE;
        }

        if(Buf) {
            MyFreePool__(Buf);
        }

        if (!_SEH2_AbnormalTermination() &&
            CompleteIrp) {
            UDFPrint(("  complete Irp %x, ctx %x, status %x, iolen %x\n",
                Irp, PtrIrpContext, RC, Irp->IoStatus.Information));
            Irp->IoStatus.Status = RC;
            // complete the IRP
            IoCompleteRequest(Irp, IO_DISK_INCREMENT);
            // Release the IRP context
            UDFReleaseIrpContext(PtrIrpContext);
        }
    } _SEH2_END;

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


/*************************************************************************
*
* Function: UDFDevIoctlCompletion()
*
* Description:
*   Completion routine.
*   
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS
*
*************************************************************************/
NTSTATUS
NTAPI
UDFDevIoctlCompletion(
   PDEVICE_OBJECT          PtrDeviceObject,
   PIRP                    Irp,
   VOID                    *Context)
{
/*    PIO_STACK_LOCATION      IrpSp = NULL;
    ULONG                   IoControlCode = 0;*/
    PtrUDFIrpContext       PtrIrpContext = (PtrUDFIrpContext)Context;

    UDFPrint(("UDFDevIoctlCompletion Irp %x, ctx %x\n", Irp, Context));
    if (Irp->PendingReturned) {
        UDFPrint(("  IoMarkIrpPending\n"));
        IoMarkIrpPending(Irp);
    }

    UDFReleaseIrpContext(PtrIrpContext);
/*    if(Irp->IoStatus.Status == STATUS_SUCCESS) {
        IrpSp = IoGetCurrentIrpStackLocation(Irp);
        IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;

        switch(IoControlCode) {
        case IOCTL_CDRW_RESET_DRIVER: {
            Vcb->MediaLockCount = 0;
        }
        }
    }*/

    return STATUS_SUCCESS;
} // end UDFDevIoctlCompletion()


/*************************************************************************
*
* Function: UDFHandleQueryPath()
*
* Description:
*   Handle the MUP request.
*   
* Expected Interrupt Level (for execution) :
*
*  IRQL_PASSIVE_LEVEL
*
* Return Value: STATUS_SUCCESS
*
*************************************************************************/
/*NTSTATUS UDFHandleQueryPath(
VOID           *BufferPointer)
{
    NTSTATUS                    RC = STATUS_SUCCESS;
    PQUERY_PATH_REQUEST RequestBuffer = (PQUERY_PATH_REQUEST)BufferPointer;
    PQUERY_PATH_RESPONSE    ReplyBuffer = (PQUERY_PATH_RESPONSE)BufferPointer;
    ULONG                       LengthOfNameToBeMatched = RequestBuffer->PathNameLength;
    ULONG                       LengthOfMatchedName = 0;
    WCHAR                *NameToBeMatched = RequestBuffer->FilePathName;

    UDFPrint(("UDFHandleQueryPath\n"));
    // So here we are. Simply check the name supplied.
    // We can use whatever algorithm we like to determine whether the
    // sent in name is acceptable.
    // The first character in the name is always a "\"
    // If we like the name sent in (probably, we will like a subset
    // of the name), set the matching length value in LengthOfMatchedName.

    // if (FoundMatch) {
    //      ReplyBuffer->LengthAccepted = LengthOfMatchedName;
    // } else {
    //      RC = STATUS_OBJECT_NAME_NOT_FOUND;
    // }

    return(RC);
}*/

NTSTATUS
UDFGetFileAllocModeFromICB(
    PtrUDFIrpContext IrpContext,
    PIRP             Irp
    )
{
    PEXTENDED_IO_STACK_LOCATION IrpSp =
        (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation( Irp );

//    PVCB Vcb;
    PtrUDFFCB Fcb;
    PtrUDFCCB Ccb;
    PUDF_GET_FILE_ALLOCATION_MODE_OUT OutputBuffer;

    UDFPrint(("UDFGetFileAllocModeFromICB\n"));

    // Decode the file object, the only type of opens we accept are
    // user volume opens.
    Ccb = (PtrUDFCCB)(IrpSp->FileObject->FsContext2);
    Fcb = Ccb->Fcb;
//    Vcb = Fcb->Vcb;

    Irp->IoStatus.Information = 0;
    if(IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(UDF_GET_FILE_ALLOCATION_MODE_OUT))
        return STATUS_BUFFER_TOO_SMALL;

    OutputBuffer = (PUDF_GET_FILE_ALLOCATION_MODE_OUT)(Irp->AssociatedIrp.SystemBuffer);
    if(!OutputBuffer)
        return STATUS_INVALID_USER_BUFFER;

    OutputBuffer->AllocMode = UDFGetFileICBAllocMode__(Fcb->FileInfo);
    Irp->IoStatus.Information = sizeof(UDF_GET_FILE_ALLOCATION_MODE_OUT);

    return STATUS_SUCCESS;
} // end UDFGetFileAllocModeFromICB()

#ifndef UDF_READ_ONLY_BUILD
NTSTATUS
UDFSetFileAllocModeFromICB(
    PtrUDFIrpContext IrpContext,
    PIRP             Irp
    )
{
    PEXTENDED_IO_STACK_LOCATION IrpSp =
        (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation( Irp );

    PVCB Vcb;
    PtrUDFFCB Fcb;
    PtrUDFCCB Ccb;
    PUDF_SET_FILE_ALLOCATION_MODE_IN InputBuffer;
    NTSTATUS RC;
    UCHAR AllocMode;

    UDFPrint(("UDFSetFileAllocModeFromICB\n"));

    Ccb = (PtrUDFCCB)(IrpSp->FileObject->FsContext2);
    Fcb = Ccb->Fcb;
    Vcb = Fcb->Vcb;

    Irp->IoStatus.Information = 0;
    if(IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(UDF_SET_FILE_ALLOCATION_MODE_IN))
        return STATUS_BUFFER_TOO_SMALL;

    InputBuffer = (PUDF_SET_FILE_ALLOCATION_MODE_IN)(Irp->AssociatedIrp.SystemBuffer);
    if(!InputBuffer)
        return STATUS_INVALID_USER_BUFFER;

    UDFFlushAFile(Fcb, Ccb, &(Irp->IoStatus), 0);
    RC = Irp->IoStatus.Status;
    if(!NT_SUCCESS(RC))
        return RC;

    if(InputBuffer->AllocMode != ICB_FLAG_AD_IN_ICB) {
        AllocMode = UDFGetFileICBAllocMode__(Fcb->FileInfo);
        if(AllocMode == ICB_FLAG_AD_IN_ICB) {
            RC = UDFConvertFEToNonInICB(Vcb, Fcb->FileInfo, InputBuffer->AllocMode);
        } else
        if(AllocMode != InputBuffer->AllocMode) {
            RC = STATUS_INVALID_PARAMETER;
        } else {
            RC = STATUS_SUCCESS;
        }
    } else {
        RC = STATUS_INVALID_PARAMETER;
    }

    return STATUS_SUCCESS;
} // end UDFSetFileAllocModeFromICB()
#endif //UDF_READ_ONLY_BUILD