Sample Code

Windows Driver Samples/ fastfat File System Driver/ C++/ create.c/

/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    Create.c

Abstract:

    This module implements the File Create routine for Fat called by the
    dispatch driver.


--*/

#include "FatProcs.h"

//
//  The Bug check file id for this module
//

#define BugCheckFileId                   (FAT_BUG_CHECK_CREATE)

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_CREATE)


//
//  Macros for incrementing performance counters.
//

#define CollectCreateHitStatistics(VCB) {                                                \
    PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];   \
    Stats->Fat.CreateHits += 1;                                                          \
}

#define CollectCreateStatistics(VCB,STATUS) {                                            \
    PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];   \
    if ((STATUS) == STATUS_SUCCESS) {                                                    \
        Stats->Fat.SuccessfulCreates += 1;                                               \
    } else {                                                                             \
        Stats->Fat.FailedCreates += 1;                                                   \
    }                                                                                    \
}

LUID FatSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 };

//
//  local procedure prototypes
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenVolume (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenRootDcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB Dcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos,
    _Out_ PBOOLEAN OplockPostIrp
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PFCB Fcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos,
    _Out_ PBOOLEAN OplockPostIrp
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenTargetDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PDCB Dcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ BOOLEAN DoesNameExist,
    _In_ BOOLEAN FileNameOpenedDos
    );

_Success_(return.Status == STATUS_SUCCESS)
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Outptr_result_maybenull_ PDCB *Dcb,
    _In_ PDCB ParentDcb,
    _In_ PDIRENT Dirent,
    _In_ ULONG LfnByteOffset,
    _In_ ULONG DirentByteOffset,
    _In_ PUNICODE_STRING Lfn,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN FileNameOpenedDos,
    _In_ BOOLEAN OpenRequiringOplock
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Outptr_result_maybenull_ PFCB *Fcb,
    _In_ PDCB ParentDcb,
    _In_ PDIRENT Dirent,
    _In_ ULONG LfnByteOffset,
    _In_ ULONG DirentByteOffset,
    _In_ PUNICODE_STRING Lfn,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN IsPagingFile,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB ParentDcb,
    _In_ POEM_STRING OemName,
    _In_ PUNICODE_STRING UnicodeName,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB ParentDcb,
    _In_ POEM_STRING OemName,
    _In_ PUNICODE_STRING UnicodeName,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ PUNICODE_STRING LfnBuffer,
    _In_ BOOLEAN IsPagingFile,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN TemporaryFile
    );

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatSupersedeOrOverwriteFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PFCB Fcb,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge
    );

NTSTATUS
FatCheckSystemSecurityAccess (
    _In_ PIRP_CONTEXT IrpContext
    );

NTSTATUS
FatCheckShareAccess (
    _In_ PIRP_CONTEXT IrpContext,
    _In_ PFILE_OBJECT FileObject,
    _In_ PFCB Fcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ ULONG ShareAccess
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCheckShareAccess)
#pragma alloc_text(PAGE, FatCheckSystemSecurityAccess)
#pragma alloc_text(PAGE, FatCommonCreate)
#pragma alloc_text(PAGE, FatCreateNewDirectory)
#pragma alloc_text(PAGE, FatCreateNewFile)
#pragma alloc_text(PAGE, FatFsdCreate)
#pragma alloc_text(PAGE, FatOpenExistingDcb)
#pragma alloc_text(PAGE, FatOpenExistingDirectory)
#pragma alloc_text(PAGE, FatOpenExistingFcb)
#pragma alloc_text(PAGE, FatOpenExistingFile)
#pragma alloc_text(PAGE, FatOpenRootDcb)
#pragma alloc_text(PAGE, FatOpenTargetDirectory)
#pragma alloc_text(PAGE, FatOpenVolume)
#pragma alloc_text(PAGE, FatSupersedeOrOverwriteFile)
#pragma alloc_text(PAGE, FatSetFullNameInFcb)
#endif


_Function_class_(IRP_MJ_CREATE)
_Function_class_(DRIVER_DISPATCH)
NTSTATUS
FatFsdCreate (
    _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtCreateFile and NtOpenFile
    API calls.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the
        file/directory exists that we are trying to open/create

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The Fsd status for the Irp

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

    PAGED_CODE();

    //
    //  If we were called with our file system device object instead of a
    //  volume device object, just complete this request with STATUS_SUCCESS
    //

    if ( FatDeviceIsFatFsdo( VolumeDeviceObject))  {

        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = FILE_OPENED;

        IoCompleteRequest( Irp, IO_DISK_INCREMENT );

        return STATUS_SUCCESS;
    }

    TimerStart(Dbg);

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

    //
    //  Call the common create routine, with block allowed if the operation
    //  is synchronous.
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, TRUE );

        Status = FatCommonCreate( IrpContext, Irp );

    } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

        //
        //  We had some trouble trying to perform the requested
        //  operation, so we'll abort the I/O request with
        //  the error status that we get back from the
        //  execption code
        //

        Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
    }

    if (TopLevel) { IoSetTopLevelIrp( NULL ); }

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

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

    TimerStop(Dbg,"FatFsdCreate");

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


_Requires_lock_held_(_Global_critical_region_)
NTSTATUS
FatCommonCreate (
    _Inout_ PIRP_CONTEXT IrpContext,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

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

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - the return status for the operation

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK Iosb = {0};
    PIO_STACK_LOCATION IrpSp;

    PFILE_OBJECT FileObject;
    PFILE_OBJECT RelatedFileObject;
    UNICODE_STRING FileName;
    ULONG AllocationSize;
    PFILE_FULL_EA_INFORMATION EaBuffer;
    PACCESS_MASK DesiredAccess;
    ULONG Options;
    UCHAR FileAttributes;
    USHORT ShareAccess;
    ULONG EaLength;

    BOOLEAN CreateDirectory;
    BOOLEAN NoIntermediateBuffering;
    BOOLEAN OpenDirectory;
    BOOLEAN IsPagingFile;
    BOOLEAN OpenTargetDirectory;
    BOOLEAN DirectoryFile;
    BOOLEAN NonDirectoryFile;
    BOOLEAN NoEaKnowledge;
    BOOLEAN DeleteOnClose;
    BOOLEAN OpenRequiringOplock;
    BOOLEAN TemporaryFile;
    BOOLEAN FileNameOpenedDos = FALSE;

    ULONG CreateDisposition;

    PVCB Vcb;
    PFCB Fcb = NULL;
    PCCB Ccb;
    PDCB ParentDcb;
    PDCB FinalDcb = NULL;

    UNICODE_STRING FinalName = {0};
    UNICODE_STRING RemainingPart;
    UNICODE_STRING NextRemainingPart = {0};
    UNICODE_STRING UpcasedFinalName;
    WCHAR UpcasedBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];

    OEM_STRING OemFinalName;
    UCHAR OemBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE*2];

    PDIRENT Dirent;
    PBCB DirentBcb = NULL;
    ULONG LfnByteOffset;
    ULONG DirentByteOffset;

    BOOLEAN PostIrp = FALSE;
    BOOLEAN OplockPostIrp = FALSE;
    BOOLEAN TrailingBackslash;
    BOOLEAN FirstLoop = TRUE;

    CCB LocalCcb;
    UNICODE_STRING Lfn;
    WCHAR LfnBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];

    PAGED_CODE();

    //
    //  Get the current IRP stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonCreate\n", 0 );
    DebugTrace( 0, Dbg, "Irp                       = %08lx\n", Irp );
    DebugTrace( 0, Dbg, "->Flags                   = %08lx\n", Irp->Flags );
    DebugTrace( 0, Dbg, "->FileObject              = %08lx\n", IrpSp->FileObject );
    DebugTrace( 0, Dbg, " ->RelatedFileObject      = %08lx\n", IrpSp->FileObject->RelatedFileObject );
    DebugTrace( 0, Dbg, " ->FileName               = %Z\n",    &IrpSp->FileObject->FileName );
    DebugTrace( 0, Dbg, "->AllocationSize.LowPart  = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
    DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
    DebugTrace( 0, Dbg, "->SystemBuffer            = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
    DebugTrace( 0, Dbg, "->DesiredAccess           = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
    DebugTrace( 0, Dbg, "->Options                 = %08lx\n", IrpSp->Parameters.Create.Options );
    DebugTrace( 0, Dbg, "->FileAttributes          = %04x\n",  IrpSp->Parameters.Create.FileAttributes );
    DebugTrace( 0, Dbg, "->ShareAccess             = %04x\n",  IrpSp->Parameters.Create.ShareAccess );
    DebugTrace( 0, Dbg, "->EaLength                = %08lx\n", IrpSp->Parameters.Create.EaLength );

    //
    //  This is here because the Win32 layer can't avoid sending me double
    //  beginning backslashes.
    //

    if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
        (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
        (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {

        IrpSp->FileObject->FileName.Length -= sizeof(WCHAR);

        RtlMoveMemory( &IrpSp->FileObject->FileName.Buffer[0],
                       &IrpSp->FileObject->FileName.Buffer[1],
                       IrpSp->FileObject->FileName.Length );

        //
        //  If there are still two beginning backslashes, the name is bogus.
        //

        if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
            (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
            (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {

            FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID );

            DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_OBJECT_NAME_INVALID\n", 0);
            return STATUS_OBJECT_NAME_INVALID;
        }
    }

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

    NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );

    FileObject        = IrpSp->FileObject;
    FileName          = FileObject->FileName;
    RelatedFileObject = FileObject->RelatedFileObject;
    AllocationSize    = Irp->Overlay.AllocationSize.LowPart;
    EaBuffer          = Irp->AssociatedIrp.SystemBuffer;
    DesiredAccess     = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
    Options           = IrpSp->Parameters.Create.Options;
    FileAttributes    = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL);
    ShareAccess       = IrpSp->Parameters.Create.ShareAccess;
    EaLength          = IrpSp->Parameters.Create.EaLength;


    //
    //  Set up the file object's Vpb pointer in case anything happens.
    //  This will allow us to get a reasonable pop-up.
    //

    if ( RelatedFileObject != NULL ) {
        FileObject->Vpb = RelatedFileObject->Vpb;
    }

    //
    //  Force setting the archive bit in the attributes byte to follow OS/2,
    //  & DOS semantics.  Also mask out any extraneous bits, note that
    //  we can't use the ATTRIBUTE_VALID_FLAGS constant because that has
    //  the control and normal flags set.
    //
    //  Delay setting ARCHIVE in case this is a directory: 2/16/95
    //

    FileAttributes   &= (FILE_ATTRIBUTE_READONLY |
                         FILE_ATTRIBUTE_HIDDEN   |
                         FILE_ATTRIBUTE_SYSTEM   |
                         FILE_ATTRIBUTE_ARCHIVE );

    //
    //  Locate the volume device object and Vcb that we are trying to access
    //

    Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;

    //
    //  Decipher Option flags and values
    //

    //
    //  If this is an open by fileid operation, just fail it explicitly.  FAT's
    //  source of fileids is not reversible for open operations.
    //

    if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) {

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

    DirectoryFile           = BooleanFlagOn( Options, FILE_DIRECTORY_FILE );
    NonDirectoryFile        = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE );
    NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING );
    NoEaKnowledge           = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE );
    DeleteOnClose           = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
#if (NTDDI_VERSION >= NTDDI_WIN7)
    OpenRequiringOplock     = BooleanFlagOn( Options, FILE_OPEN_REQUIRING_OPLOCK );
#else
    OpenRequiringOplock     = FALSE;
#endif

    TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes,
                                   FILE_ATTRIBUTE_TEMPORARY );

    CreateDisposition = (Options >> 24) & 0x000000ff;

    IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE );
    OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );

    CreateDirectory = (BOOLEAN)(DirectoryFile &&
                                ((CreateDisposition == FILE_CREATE) ||
                                 (CreateDisposition == FILE_OPEN_IF)));

    OpenDirectory   = (BOOLEAN)(DirectoryFile &&
                                ((CreateDisposition == FILE_OPEN) ||
                                 (CreateDisposition == FILE_OPEN_IF)));


    //
    //  Make sure the input large integer is valid and that the dir/nondir
    //  indicates a storage type we understand.
    //

    if (Irp->Overlay.AllocationSize.HighPart != 0 ||
        (DirectoryFile && NonDirectoryFile)) {

        FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );

        DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_INVALID_PARAMETER\n", 0);
        return STATUS_INVALID_PARAMETER;
    }

    //
    //  Acquire exclusive access to the vcb, and enqueue the Irp if
    //  we didn't get it.
    //

    if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {

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

        Iosb.Status = FatFsdPostRequest( IrpContext, Irp );

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

    //
    //  Make sure we haven't been called recursively by a filter inside an existing
    //  create request.
    //

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

#pragma prefast( suppress:28159, "this is a serious programming error if it happens" )
        FatBugCheck( 0, 0, 0);
    }

    //
    //  Initialize the DirentBcb to null
    //

    DirentBcb = NULL;

    //
    //  Initialize our temp strings with their stack buffers.
    //

    OemFinalName.Length = 0;
    OemFinalName.MaximumLength = sizeof( OemBuffer);
    OemFinalName.Buffer = (PCHAR)OemBuffer;

    UpcasedFinalName.Length = 0;
    UpcasedFinalName.MaximumLength = sizeof( UpcasedBuffer);
    UpcasedFinalName.Buffer = UpcasedBuffer;

    Lfn.Length = 0;
    Lfn.MaximumLength = sizeof( LfnBuffer);
    Lfn.Buffer = LfnBuffer;

    try {

        //
        //  Make sure the vcb is in a usable condition.  This will raise
        //  and error condition if the volume is unusable
        //

        FatVerifyVcb( IrpContext, Vcb );

        //
        //  If the Vcb is locked then we cannot open another file
        //

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

            DebugTrace(0, Dbg, "Volume is locked\n", 0);

            Status = STATUS_ACCESS_DENIED;
            if (Vcb->VcbCondition != VcbGood) {

                Status = STATUS_VOLUME_DISMOUNTED;
            }
            try_return( Iosb.Status = Status );
        }

        //
        //  Don't allow the DELETE_ON_CLOSE option if the volume is
        //  write-protected.
        //

        if (DeleteOnClose && FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {

            //
            //  Set the real device for the pop-up info, and set the verify
            //  bit in the device object, so that we will force a verify
            //  in case the user put the correct media back in.
            //

            IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
                                          Vcb->Vpb->RealDevice );

            SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);

            FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
        }

        //
        //  If this is a fat32 volume, EA's are not supported.
        //

        if (EaBuffer != NULL) {

            try_return( Iosb.Status = STATUS_EAS_NOT_SUPPORTED );
        }

        //
        //  Check if we are opening the volume and not a file/directory.
        //  We are opening the volume if the name is empty and there
        //  isn't a related file object.  If there is a related file object
        //  then it is the Vcb itself.
        //

        if (FileName.Length == 0) {

            PVCB DecodeVcb = NULL;

            if (RelatedFileObject == NULL ||
                FatDecodeFileObject( RelatedFileObject,
                                     &DecodeVcb,
                                     &Fcb,
                                     &Ccb ) == UserVolumeOpen) {

                NT_ASSERT( RelatedFileObject == NULL || Vcb == DecodeVcb );

                //
                //  Check if we were to open a directory
                //

                if (DirectoryFile) {

                    DebugTrace(0, Dbg, "Cannot open volume as a directory\n", 0);

                    try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
                }

                //
                //  Can't open the TargetDirectory of the DASD volume.
                //

                if (OpenTargetDirectory) {

                    try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
                }

                DebugTrace(0, Dbg, "Opening the volume, Vcb = %08lx\n", Vcb);

                CollectCreateHitStatistics(Vcb);

                Iosb = FatOpenVolume( IrpContext,
                                      FileObject,
                                      Vcb,
                                      DesiredAccess,
                                      ShareAccess,
                                      CreateDisposition );

                Irp->IoStatus.Information = Iosb.Information;
                try_return( Iosb.Status );
            }
        }

        //
        //  If there is a related file object then this is a relative open.
        //  The related file object is the directory to start our search at.
        //  Return an error if it is not a directory.
        //

        if (RelatedFileObject != NULL) {

            PVCB RelatedVcb;
            PDCB RelatedDcb;
            PCCB RelatedCcb;
            TYPE_OF_OPEN TypeOfOpen;

            TypeOfOpen = FatDecodeFileObject( RelatedFileObject,
                                              &RelatedVcb,
                                              &RelatedDcb,
                                              &RelatedCcb );

            if (TypeOfOpen != UserFileOpen &&
                TypeOfOpen != UserDirectoryOpen) {

                DebugTrace(0, Dbg, "Invalid related file object\n", 0);

                try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND );
            }

            //
            //  A relative open must be via a relative path.
            //

            if (FileName.Length != 0 &&
                FileName.Buffer[0] == L'\\') {

                try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
            }

            //
            //  Set up the file object's Vpb pointer in case anything happens.
            //

            NT_ASSERT( Vcb == RelatedVcb );

            FileObject->Vpb = RelatedFileObject->Vpb;

            //
            //  Now verify the related Fcb so we don't get in trouble later
            //  by assuming its in good shape.
            //

            FatVerifyFcb( IrpContext, RelatedDcb );

            ParentDcb = RelatedDcb;

        } else {

            //
            //  This is not a relative open, so check if we're
            //  opening the root dcb
            //

            if ((FileName.Length == sizeof(WCHAR)) &&
                (FileName.Buffer[0] == L'\\')) {

                //
                //  Check if we were not supposed to open a directory
                //

                if (NonDirectoryFile) {

                    DebugTrace(0, Dbg, "Cannot open root directory as a file\n", 0);

                    try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
                }

                //
                //  Can't open the TargetDirectory of the root directory.
                //

                if (OpenTargetDirectory) {

                    try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
                }

                //
                //  Not allowed to delete root directory.
                //

                if (DeleteOnClose) {

                    try_return( Iosb.Status = STATUS_CANNOT_DELETE );
                }

                DebugTrace(0, Dbg, "Opening root dcb\n", 0);

                CollectCreateHitStatistics(Vcb);

                Iosb = FatOpenRootDcb( IrpContext,
                                       FileObject,
                                       Vcb,
                                       DesiredAccess,
                                       ShareAccess,
                                       CreateDisposition );

                Irp->IoStatus.Information = Iosb.Information;
                try_return( Iosb.Status );
            }

            //
            //  Nope, we will be opening relative to the root directory.
            //

            ParentDcb = Vcb->RootDcb;

            //
            //  Now verify the root Dcb so we don't get in trouble later
            //  by assuming its in good shape.
            //

            FatVerifyFcb( IrpContext, ParentDcb );
        }

        //
        //  FatCommonCreate(): trailing backslash check
        //


        if ((FileName.Length != 0) &&
            (FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) {

            FileName.Length -= sizeof(WCHAR);
            TrailingBackslash = TRUE;

        } else {

            TrailingBackslash = FALSE;
        }

        //
        //  Check for max path.  We might want to tighten this down to DOS MAX_PATH
        //  for maximal interchange with non-NT platforms, but for now defer to the
        //  possibility of something depending on it.
        //

        if (ParentDcb->FullFileName.Buffer == NULL) {

            FatSetFullFileNameInFcb( IrpContext, ParentDcb );
        }

        if ((USHORT) (ParentDcb->FullFileName.Length + sizeof(WCHAR) + FileName.Length) <= FileName.Length) {

            try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
        }

        //
        //  We loop here until we land on an Fcb that is in a good
        //  condition.  This way we can reopen files that have stale handles
        //  to files of the same name but are now different.
        //

        while ( TRUE ) {

            Fcb = ParentDcb;
            RemainingPart = FileName;

            //
            //  Now walk down the Dcb tree looking for the longest prefix.
            //  This one exit condition in the while() is to handle a
            //  special case condition (relative NULL name open), the main
            //  exit conditions are at the bottom of the loop.
            //

            while (RemainingPart.Length != 0) {

                PFCB NextFcb;

                FsRtlDissectName( RemainingPart,
                                  &FinalName,
                                  &NextRemainingPart );

                //
                //  If RemainingPart starts with a backslash the name is
                //  invalid.
                //  Check for no more than 255 characters in FinalName
                //

                if (((NextRemainingPart.Length != 0) && (NextRemainingPart.Buffer[0] == L'\\')) ||
                    (FinalName.Length > 255*sizeof(WCHAR))) {

                    try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
                }

                //
                //  Now, try to convert this one component into Oem and search
                //  the splay tree.  If it works then that's great, otherwise
                //  we have to try with the UNICODE name instead.
                //

                FatEnsureStringBufferEnough( &OemFinalName,
                                             FinalName.Length);

                Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE );

                if (NT_SUCCESS(Status)) {

                    NextFcb = FatFindFcb( IrpContext,
                                          &Fcb->Specific.Dcb.RootOemNode,
                                          (PSTRING)&OemFinalName,
                                          &FileNameOpenedDos );

                } else {

                    NextFcb = NULL;
                    OemFinalName.Length = 0;

                    if (Status != STATUS_UNMAPPABLE_CHARACTER) {

                        try_return( Iosb.Status = Status );
                    }
                }

                //
                //  If we didn't find anything searching the Oem space, we
                //  have to try the Unicode space.  To save cycles in the
                //  common case that this tree is empty, we do a quick check
                //  here.
                //

                if ((NextFcb == NULL) && Fcb->Specific.Dcb.RootUnicodeNode) {

                    //
                    // First downcase, then upcase the string, because this
                    // is what happens when putting names into the tree (see
                    // strucsup.c, FatConstructNamesInFcb()).
                    //

                    FatEnsureStringBufferEnough( &UpcasedFinalName,
                                                 FinalName.Length);

                    Status = RtlDowncaseUnicodeString(&UpcasedFinalName, &FinalName, FALSE );
                    NT_ASSERT( NT_SUCCESS( Status ));

                    Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &UpcasedFinalName, FALSE );
                    NT_ASSERT( NT_SUCCESS( Status ));

                    NextFcb = FatFindFcb( IrpContext,
                                          &Fcb->Specific.Dcb.RootUnicodeNode,
                                          (PSTRING)&UpcasedFinalName,
                                          &FileNameOpenedDos );
                }

                //
                //  If we got back an Fcb then we consumed the FinalName
                //  legitimately, so the remaining name is now RemainingPart.
                //

                if (NextFcb != NULL) {
                    Fcb = NextFcb;
                    RemainingPart = NextRemainingPart;
                }

                if ((NextFcb == NULL) ||
                    (NodeType(NextFcb) == FAT_NTC_FCB) ||
                    (NextRemainingPart.Length == 0)) {

                    break;
                }
            }

            //
            //  Remaining name cannot start with a backslash
            //

            if (RemainingPart.Length && (RemainingPart.Buffer[0] == L'\\')) {

                RemainingPart.Length -= sizeof(WCHAR);
                RemainingPart.Buffer += 1;
            }

            //
            //  Now verify that everybody up to the longest found prefix is valid.
            //

            try {

                FatVerifyFcb( IrpContext, Fcb );

            } except( (GetExceptionCode() == STATUS_FILE_INVALID) ?
                      EXCEPTION_EXECUTE_HANDLER :
                      EXCEPTION_CONTINUE_SEARCH ) {

                  FatResetExceptionState( IrpContext );
            }

            if ( Fcb->FcbCondition == FcbGood ) {

                //
                //  If we are trying to open a paging file and have happened
                //  upon the DelayedCloseFcb, make it go away, and try again.
                //

                if (IsPagingFile && FirstLoop &&
                    (NodeType(Fcb) == FAT_NTC_FCB) &&
                    (!IsListEmpty( &FatData.AsyncCloseList ) ||
                     !IsListEmpty( &FatData.DelayedCloseList ))) {

                    FatFspClose(Vcb);

                    FirstLoop = FALSE;

                    continue;

                } else {

                    break;
                }

            } else {

                FatRemoveNames( IrpContext, Fcb );
            }
        }

        NT_ASSERT( Fcb->FcbCondition == FcbGood );

        //
        //  If there is already an Fcb for a paging file open and
        //  it was not already opened as a paging file, we cannot
        //  continue as it is too difficult to move a live Fcb to
        //  non-paged pool.
        //

        if (IsPagingFile) {

            if (NodeType(Fcb) == FAT_NTC_FCB &&
                !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {

                try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
            }

        //
        //  Check for a system file.
        //

        } else if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {

            try_return( Iosb.Status = STATUS_ACCESS_DENIED );
        }

        //
        //  If the longest prefix is pending delete (either the file or
        //  some higher level directory), we cannot continue.
        //

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

            try_return( Iosb.Status = STATUS_DELETE_PENDING );
        }

        //
        //  Now that we've found the longest matching prefix we'll
        //  check if there isn't any remaining part because that means
        //  we've located an existing fcb/dcb to open and we can do the open
        //  without going to the disk
        //

        if (RemainingPart.Length == 0) {

            //
            //  First check if the user wanted to open the target directory
            //  and if so then call the subroutine to finish the open.
            //

            if (OpenTargetDirectory) {

                CollectCreateHitStatistics(Vcb);

                Iosb = FatOpenTargetDirectory( IrpContext,
                                               FileObject,
                                               Fcb->ParentDcb,
                                               DesiredAccess,
                                               ShareAccess,
                                               TRUE,
                                               FileNameOpenedDos );
                Irp->IoStatus.Information = Iosb.Information;
                try_return( Iosb.Status );
            }

            //
            //  We can open an existing fcb/dcb, now we only need to case
            //  on which type to open.
            //

            if (NodeType(Fcb) == FAT_NTC_DCB || NodeType(Fcb) == FAT_NTC_ROOT_DCB) {

                //
                //  This is a directory we're opening up so check if
                //  we were not to open a directory
                //

                if (NonDirectoryFile) {

                    DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);

                    try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
                }

                DebugTrace(0, Dbg, "Open existing dcb, Dcb = %08lx\n", Fcb);

                CollectCreateHitStatistics(Vcb);

                Iosb = FatOpenExistingDcb( IrpContext,
                                           FileObject,
                                           Vcb,
                                           (PDCB)Fcb,
                                           DesiredAccess,
                                           ShareAccess,
                                           CreateDisposition,
                                           NoEaKnowledge,
                                           DeleteOnClose,
                                           OpenRequiringOplock,
                                           FileNameOpenedDos,
                                           &OplockPostIrp );

                if (Iosb.Status != STATUS_PENDING) {

                    Irp->IoStatus.Information = Iosb.Information;

                } else {

                    DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0);

                    PostIrp = TRUE;
                }

                try_return( Iosb.Status );
            }

            //
            //  Check if we're trying to open an existing Fcb and that
            //  the user didn't want to open a directory.  Note that this
            //  call might actually come back with status_pending because
            //  the user wanted to supersede or overwrite the file and we
            //  cannot block.  If it is pending then we do not complete the
            //  request, and we fall through the bottom to the code that
            //  dispatches the request to the fsp.
            //

            if (NodeType(Fcb) == FAT_NTC_FCB) {

                //
                //  Check if we were only to open a directory
                //

                if (OpenDirectory) {

                    DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);

                    try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
                }

                DebugTrace(0, Dbg, "Open existing fcb, Fcb = %08lx\n", Fcb);

                if ( TrailingBackslash ) {
                    try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
                }

                CollectCreateHitStatistics(Vcb);

                Iosb = FatOpenExistingFcb( IrpContext,
                                           FileObject,
                                           Vcb,
                                           Fcb,
                                           DesiredAccess,
                                           ShareAccess,
                                           AllocationSize,
                                           EaBuffer,
                                           EaLength,
                                           FileAttributes,
                                           CreateDisposition,
                                           NoEaKnowledge,
                                           DeleteOnClose,
                                           OpenRequiringOplock,
                                           FileNameOpenedDos,
                                           &OplockPostIrp );

                if (Iosb.Status != STATUS_PENDING) {

                    //
                    //  Check if we need to set the cache support flag in
                    //  the file object
                    //

                    if (NT_SUCCESS( Iosb.Status) && !NoIntermediateBuffering) {

                        FileObject->Flags |= FO_CACHE_SUPPORTED;
                    }

                    Irp->IoStatus.Information = Iosb.Information;

                } else {

                    DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0);

                    PostIrp = TRUE;
                }

                try_return( Iosb.Status );
            }

            //
            //  Not and Fcb or a Dcb so we bug check
            //
            
#pragma prefast( suppress:28159, "this is a serious corruption if it happens" )
            FatBugCheck( NodeType(Fcb), (ULONG_PTR) Fcb, 0 );
        }

        //
        //  There is more in the name to parse than we have in existing
        //  fcbs/dcbs.  So now make sure that fcb we got for the largest
        //  matching prefix is really a dcb otherwise we can't go any
        //  further
        //

        if ((NodeType(Fcb) != FAT_NTC_DCB) && (NodeType(Fcb) != FAT_NTC_ROOT_DCB)) {

            DebugTrace(0, Dbg, "Cannot open file as subdirectory, Fcb = %08lx\n", Fcb);

            try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND );
        }

        //
        //  Otherwise we continue on processing the Irp and allowing ourselves
        //  to block for I/O as necessary.  Find/create additional dcb's for
        //  the one we're trying to open.  We loop until either remaining part
        //  is empty or we get a bad filename.  When we exit FinalName is
        //  the last name in the string we're after, and ParentDcb is the
        //  parent directory that will contain the opened/created
        //  file/directory.
        //
        //  Make sure the rest of the name is valid in at least the LFN
        //  character set (which just happens to be that of HPFS).
        //
        //  If we are not in ChicagoMode, use FAT semantics.
        //

        ParentDcb = Fcb;
        FirstLoop = TRUE;

        while (TRUE) {

            //
            //  We do one little optimization here on the first iteration of
            //  the loop since we know that we have already tried to convert
            //  FinalOemName from the original UNICODE.
            //

            if (FirstLoop) {

                FirstLoop = FALSE;
                RemainingPart = NextRemainingPart;
                Status = OemFinalName.Length ? STATUS_SUCCESS : STATUS_UNMAPPABLE_CHARACTER;

            } else {

                //
                //  Dissect the remaining part.
                //

                DebugTrace(0, Dbg, "Dissecting the name %Z\n", &RemainingPart);

                FsRtlDissectName( RemainingPart,
                                  &FinalName,
                                  &RemainingPart );

                //
                //  If RemainingPart starts with a backslash the name is
                //  invalid.
                //  Check for no more than 255 characters in FinalName
                //

                if (((RemainingPart.Length != 0) && (RemainingPart.Buffer[0] == L'\\')) ||
                    (FinalName.Length > 255*sizeof(WCHAR))) {

                    try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
                }

                //
                //  Now, try to convert this one component into Oem.  If it works
                //  then that's great, otherwise we have to try with the UNICODE
                //  name instead.
                //

                FatEnsureStringBufferEnough( &OemFinalName,
                                             FinalName.Length);

                Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE );
            }

            if (NT_SUCCESS(Status)) {

                //
                //  We'll start by trying to locate the dirent for the name.  Note
                //  that we already know that there isn't an Fcb/Dcb for the file
                //  otherwise we would have found it when we did our prefix lookup.
                //

                if (FatIsNameShortOemValid( IrpContext, OemFinalName, FALSE, FALSE, FALSE )) {

                    FatStringTo8dot3( IrpContext,
                                      OemFinalName,
                                      &LocalCcb.OemQueryTemplate.Constant );

                    LocalCcb.Flags = 0;

                } else {

                    LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE;
                }

            } else {

                LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE;

                if (Status != STATUS_UNMAPPABLE_CHARACTER) {

                    try_return( Iosb.Status = Status );
                }
            }

            //
            //  Now we know a lot about the final name, so do legal name
            //  checking here.
            //

            if (FatData.ChicagoMode) {

                if (!FatIsNameLongUnicodeValid( IrpContext, &FinalName, FALSE, FALSE, FALSE )) {

                    try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
                }

            } else {

                if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {

                    try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
                }
            }

            DebugTrace(0, Dbg, "FinalName is %Z\n", &FinalName);
            DebugTrace(0, Dbg, "RemainingPart is %Z\n", &RemainingPart);

            FatEnsureStringBufferEnough( &UpcasedFinalName,
                                         FinalName.Length);

            if (!NT_SUCCESS(Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &FinalName, FALSE))) {

                try_return( Iosb.Status = Status );
            }

            LocalCcb.UnicodeQueryTemplate =  UpcasedFinalName;
            LocalCcb.ContainsWildCards = FALSE;

            Lfn.Length = 0;

            FatLocateDirent( IrpContext,
                             ParentDcb,
                             &LocalCcb,
                             0,
                             &Dirent,
                             &DirentBcb,
                             (PVBO)&DirentByteOffset,
                             &FileNameOpenedDos,
                             &Lfn);
            //
            //  Remember we read this Dcb for error recovery.
            //

            FinalDcb = ParentDcb;

            //
            //  If the remaining part is now empty then this is the last name
            //  in the string and the one we want to open
            //

            if (RemainingPart.Length == 0) { break; }

            //
            //  We didn't find a dirent, bail.
            //

            if (Dirent == NULL) {

                Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
                try_return( Iosb.Status );
            }

            //
            //  We now have a dirent, make sure it is a directory
            //

            if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {

                Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
                try_return( Iosb.Status );
            }

            //
            //  Compute the LfnByteOffset.
            //

            LfnByteOffset = DirentByteOffset -
                            FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);

            //
            //  Create a dcb for the new directory
            //

            ParentDcb = FatCreateDcb( IrpContext,
                                      Vcb,
                                      ParentDcb,
                                      LfnByteOffset,
                                      DirentByteOffset,
                                      Dirent,
                                      &Lfn );

            //
            //  Remember we created this Dcb for error recovery.
            //

            FinalDcb = ParentDcb;

            FatSetFullNameInFcb( IrpContext, ParentDcb, &FinalName );
        }

        //
        //  First check if the user wanted to open the target directory
        //  and if so then call the subroutine to finish the open.
        //

        if (OpenTargetDirectory) {

            Iosb = FatOpenTargetDirectory( IrpContext,
                                           FileObject,
                                           ParentDcb,
                                           DesiredAccess,
                                           ShareAccess,
                                           Dirent ? TRUE : FALSE,
                                           FileNameOpenedDos);

            Irp->IoStatus.Information = Iosb.Information;
            try_return( Iosb.Status );
        }

        if (Dirent != NULL) {

            //
            //  Compute the LfnByteOffset.
            //

            LfnByteOffset = DirentByteOffset -
                            FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);

            //
            //  We were able to locate an existing dirent entry, so now
            //  see if it is a directory that we're trying to open.
            //

            if (FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {

                //
                //  Make sure its okay to open a directory
                //

                if (NonDirectoryFile) {

                    DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);

                    try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY );
                }

                DebugTrace(0, Dbg, "Open existing directory\n", 0);

                Iosb = FatOpenExistingDirectory( IrpContext,
                                                 FileObject,
                                                 Vcb,
                                                 &Fcb,
                                                 ParentDcb,
                                                 Dirent,
                                                 LfnByteOffset,
                                                 DirentByteOffset,
                                                 &Lfn,
                                                 DesiredAccess,
                                                 ShareAccess,
                                                 CreateDisposition,
                                                 NoEaKnowledge,
                                                 DeleteOnClose,
                                                 FileNameOpenedDos,
                                                 OpenRequiringOplock );
                
                Irp->IoStatus.Information = Iosb.Information;
                try_return( Iosb.Status );
            }

            //
            //  Otherwise we're trying to open and existing file, and we
            //  need to check if the user only wanted to open a directory.
            //

            if (OpenDirectory) {

                DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);

                try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY );
            }

            DebugTrace(0, Dbg, "Open existing file\n", 0);

            if ( TrailingBackslash ) {
               try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
            }

            Iosb = FatOpenExistingFile( IrpContext,
                                        FileObject,
                                        Vcb,
                                        &Fcb,
                                        ParentDcb,
                                        Dirent,
                                        LfnByteOffset,
                                        DirentByteOffset,
                                        &Lfn,
                                        DesiredAccess,
                                        ShareAccess,
                                        AllocationSize,
                                        EaBuffer,
                                        EaLength,
                                        FileAttributes,
                                        CreateDisposition,
                                        IsPagingFile,
                                        NoEaKnowledge,
                                        DeleteOnClose,
                                        OpenRequiringOplock,
                                        FileNameOpenedDos );

            //
            //  Check if we need to set the cache support flag in
            //  the file object
            //

            if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {

                FileObject->Flags |= FO_CACHE_SUPPORTED;
            }

            Irp->IoStatus.Information = Iosb.Information;
            try_return( Iosb.Status );
        }

        //
        //  We can't locate a dirent so this is a new file.
        //

        //
        //  Now check to see if we wanted to only open an existing file.
        //  And then case on whether we wanted to create a file or a directory.
        //

        if ((CreateDisposition == FILE_OPEN) ||
            (CreateDisposition == FILE_OVERWRITE)) {

            DebugTrace( 0, Dbg, "Cannot open nonexisting file\n", 0);

            try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND );
        }

        //
        //  Skip a few cycles later if we know now that the Oem name is not
        //  valid 8.3.
        //

        if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {

            OemFinalName.Length = 0;
        }

        //
        //  Determine the granted access for this operation now.
        //

        if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {

            try_return( Iosb );
        }

        if (CreateDirectory) {

            DebugTrace(0, Dbg, "Create new directory\n", 0);

            //
            //  If this media is write protected, don't even try the create.
            //

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

                //
                //  Set the real device for the pop-up info, and set the verify
                //  bit in the device object, so that we will force a verify
                //  in case the user put the correct media back in.
                //


                IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
                                              Vcb->Vpb->RealDevice );

                SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);

                FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
            }

            //
            //  Don't allow people to create directories with the
            //  temporary bit set.
            //

            if (TemporaryFile) {

                try_return( Iosb.Status = STATUS_INVALID_PARAMETER );
            }

            Iosb = FatCreateNewDirectory( IrpContext,
                                          FileObject,
                                          Vcb,
                                          ParentDcb,
                                          &OemFinalName,
                                          &FinalName,
                                          DesiredAccess,
                                          ShareAccess,
                                          EaBuffer,
                                          EaLength,
                                          FileAttributes,
                                          NoEaKnowledge,
                                          DeleteOnClose,
                                          OpenRequiringOplock );

            Irp->IoStatus.Information = Iosb.Information;
            try_return( Iosb.Status );
        }

        DebugTrace(0, Dbg, "Create new file\n", 0);

        if ( TrailingBackslash ) {

            try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
        }

        //
        //  If this media is write protected, don't even try the create.
        //

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

            //
            //  Set the real device for the pop-up info, and set the verify
            //  bit in the device object, so that we will force a verify
            //  in case the user put the correct media back in.
            //


            IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
                                          Vcb->Vpb->RealDevice );

            SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);

            FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
        }

        Iosb = FatCreateNewFile( IrpContext,
                                 FileObject,
                                 Vcb,
                                 ParentDcb,
                                 &OemFinalName,
                                 &FinalName,
                                 DesiredAccess,
                                 ShareAccess,
                                 AllocationSize,
                                 EaBuffer,
                                 EaLength,
                                 FileAttributes,
                                 &Lfn,
                                 IsPagingFile,
                                 NoEaKnowledge,
                                 DeleteOnClose,
                                 OpenRequiringOplock,
                                 TemporaryFile );

        //
        //  Check if we need to set the cache support flag in
        //  the file object
        //

        if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {

            FileObject->Flags |= FO_CACHE_SUPPORTED;
        }

        Irp->IoStatus.Information = Iosb.Information;

    try_exit: NOTHING;

        //
        //  This is a Beta Fix.  Do this at a better place later.
        //

        if (NT_SUCCESS(Iosb.Status) && !OpenTargetDirectory) {

            PFCB LocalFcb;

            //
            //  If there is an Fcb/Dcb, set the long file name.
            //

            LocalFcb = FileObject->FsContext;

            if (LocalFcb &&
                ((NodeType(LocalFcb) == FAT_NTC_FCB) ||
                 (NodeType(LocalFcb) == FAT_NTC_DCB)) &&
                (LocalFcb->FullFileName.Buffer == NULL)) {

                FatSetFullNameInFcb( IrpContext, LocalFcb, &FinalName );
            }
        }

    } finally {

        DebugUnwind( FatCommonCreate );

#if (NTDDI_VERSION >= NTDDI_WIN7)

        //
        //  If we're not getting out with success, and if the caller wanted
        //  atomic create-with-oplock semantics make sure we back out any
        //  oplock that may have been granted.
        //

        if ((AbnormalTermination() ||
             !NT_SUCCESS( Iosb.Status )) &&
            OpenRequiringOplock &&
            (Iosb.Status != STATUS_CANNOT_BREAK_OPLOCK) &&
            (IrpContext->ExceptionStatus != STATUS_CANNOT_BREAK_OPLOCK) &&
            (Fcb != NULL) &&
            FatIsFileOplockable( Fcb )) {

            FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
                                IrpContext->OriginatingIrp,
                                OPLOCK_FLAG_BACK_OUT_ATOMIC_OPLOCK,
                                NULL,
                                NULL,
                                NULL );
        }
#endif

        //
        //  There used to be a test here - the ASSERT replaces it.  We will
        //  never have begun enumerating directories if we post the IRP for
        //  oplock reasons.
        //

        NT_ASSERT( !OplockPostIrp || DirentBcb == NULL );

        FatUnpinBcb( IrpContext, DirentBcb );

        //
        //  If we are in an error path, check for any created subdir Dcbs that
        //  have to be unwound.  Don't whack the root directory.
        //
        //  Note this will leave a branch of Dcbs dangling if the directory file
        //  had not been built on the leaf (case: opening path which has an
        //  element containing an invalid character name).
        //

        if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) {

            ULONG SavedFlags;

            //
            //  Before doing the uninitialize, we have to unpin anything
            //  that has been repinned, but disable writethrough first.  We
            //  disable raise from unpin-repin since we're already failing.
            //

            SavedFlags = IrpContext->Flags;
            
            SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE |
                                        IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH );
            
            FatUnpinRepinnedBcbs( IrpContext );
           
            if ((FinalDcb != NULL) &&
                (NodeType(FinalDcb) == FAT_NTC_DCB) &&
                IsListEmpty(&FinalDcb->Specific.Dcb.ParentDcbQueue) &&
                (FinalDcb->OpenCount == 0) &&
                (FinalDcb->Specific.Dcb.DirectoryFile != NULL)) {

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

                FinalDcb->Specific.Dcb.DirectoryFile = NULL;

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                ObDereferenceObject( DirectoryFileObject );
            }
            
            IrpContext->Flags = SavedFlags;
        }

        if (AbnormalTermination()) {

            FatReleaseVcb( IrpContext, Vcb );
        }

        //
        //  Free up any string buffers we allocated
        //

        FatFreeStringBuffer( &OemFinalName);

        FatFreeStringBuffer( &UpcasedFinalName);

        FatFreeStringBuffer( &Lfn);
    }

    //
    //  The following code is only executed if we are exiting the
    //  procedure through a normal termination.  We complete the request
    //  and if for any reason that bombs out then we need to unreference
    //  and possibly delete the fcb and ccb.
    //

    try {

        if (PostIrp) {

            //
            //  If the Irp hasn't already been posted, do it now.
            //

            if (!OplockPostIrp) {

                Iosb.Status = FatFsdPostRequest( IrpContext, Irp );
            }

        } else {
        
            FatUnpinRepinnedBcbs( IrpContext );
        }

    } finally {

        DebugUnwind( FatCommonCreate-in-FatCompleteRequest );

        if (AbnormalTermination() ) {

            PVCB LocalVcb;
            PFCB LocalFcb;
            PCCB LocalCcb2;
            PFILE_OBJECT DirectoryFileObject;

            //
            //  Unwind all of our counts.  Note that if a write failed, then
            //  the volume has been marked for verify, and all volume
            //  structures will be cleaned up automatically.
            //

            (VOID) FatDecodeFileObject( FileObject, &LocalVcb, &LocalFcb, &LocalCcb2 );

            LocalFcb->UncleanCount -= 1;
            LocalFcb->OpenCount -= 1;
            LocalVcb->OpenFileCount -= 1;

            if (IsFileObjectReadOnly(FileObject)) { LocalVcb->ReadOnlyCount -= 1; }

            
            //
            //  WinSE #307418 "Occasional data corruption when standby/resume
            //  while copying files to removable FAT formatted media".
            //  If new file creation request was interrupted by system suspend
            //  operation we should revert the changes we made to the parent
            //  directory and to the allocation table.
            //
            
            if (IrpContext->ExceptionStatus == STATUS_VERIFY_REQUIRED &&
                NodeType( LocalFcb ) == FAT_NTC_FCB) {
                
                FatTruncateFileAllocation( IrpContext, LocalFcb, 0 );

                FatDeleteDirent( IrpContext, LocalFcb, NULL, TRUE );
            }
            
            //
            //  If we leafed out on a new Fcb we should get rid of it at this point.
            //
            //  Since the object isn't being opened, we have to do all of the teardown
            //  here.  Our close path will not occur for this fileobject. Note this
            //  will leave a branch of Dcbs dangling since we do it by hand and don't
            //  chase to the root.
            //

            if (LocalFcb->OpenCount == 0 &&
                (NodeType( LocalFcb ) == FAT_NTC_FCB ||
                 IsListEmpty(&LocalFcb->Specific.Dcb.ParentDcbQueue))) {

                NT_ASSERT( NodeType( LocalFcb ) != FAT_NTC_ROOT_DCB );

                if ( (NodeType( LocalFcb ) == FAT_NTC_DCB) &&
                     (LocalFcb->Specific.Dcb.DirectoryFile != NULL) ) {

                    FatSyncUninitializeCacheMap( IrpContext,
                                                 LocalFcb->Specific.Dcb.DirectoryFile );

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

                } else {
                    if (ARGUMENT_PRESENT( FileObject )) {
                        FileObject->SectionObjectPointer = NULL;
                    }
                    FatDeleteFcb( IrpContext, &LocalFcb );
                }
            }

            FatDeleteCcb( IrpContext, &LocalCcb2 );

            FatReleaseVcb( IrpContext, LocalVcb );

        } else {

            FatReleaseVcb( IrpContext, Vcb );

            if ( !PostIrp ) {

                //
                //  If this request is successful and the file was opened
                //  for FILE_EXECUTE access, then set the FileObject bit.
                //

                NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
                if (FlagOn( *DesiredAccess, FILE_EXECUTE )) {

                    SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ );
                }

                //
                //  Lock volume in drive if we opened a paging file, allocating a
                //  reserve MDL to guarantee paging file operations can always
                //  go forward.
                //

                if (IsPagingFile && NT_SUCCESS(Iosb.Status)) {

#pragma prefast( suppress:28112, "this should be safe" )
                    if (!FatReserveMdl) {

                        PMDL ReserveMdl = IoAllocateMdl( NULL,
                                                         FAT_RESERVE_MDL_SIZE * PAGE_SIZE,
                                                         TRUE,
                                                         FALSE,
                                                         NULL );

                        //
                        //  Stash the MDL, and if it turned out there was already one there
                        //  just free what we got.
                        //

                        InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL );

#pragma prefast( suppress:28112, "this should be safe" )
                        if (FatReserveMdl != ReserveMdl) {

                            IoFreeMdl( ReserveMdl );
                        }
                    }

                    SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);

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

                        FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
                    }
                }

                //
                //  Complete the request.
                //

                FatCompleteRequest( IrpContext, Irp, Iosb.Status );
            }
        }

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

    CollectCreateStatistics(Vcb, Iosb.Status);

    return Iosb.Status;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenVolume (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition
    )

/*++

Routine Description:

    This routine opens the specified volume for DASD access

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume being opened

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    CreateDisposition - Supplies the create disposition for this operation

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;

    IO_STATUS_BLOCK Iosb = {0,0};

    BOOLEAN CleanedVolume = FALSE;

    //
    //  The following variables are for abnormal termination
    //

    BOOLEAN UnwindShareAccess = FALSE;
    PCCB UnwindCcb = NULL;
    BOOLEAN UnwindCounts = FALSE;
    BOOLEAN UnwindVolumeLock = FALSE;

    PAGED_CODE();

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

    try {

        //
        //  Check for proper desired access and rights
        //

        if ((CreateDisposition != FILE_OPEN) &&
            (CreateDisposition != FILE_OPEN_IF)) {

            try_return( Iosb.Status = STATUS_ACCESS_DENIED );
        }

        //
        //  If the user does not want to share write or delete then we will try
        //  and take out a lock on the volume.
        //

        if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) &&
            !FlagOn(ShareAccess, FILE_SHARE_DELETE)) {

#if (NTDDI_VERSION >= NTDDI_VISTA)
            //
            //  See if the user has requested write access.  If so, they cannot share
            //  read.  There is one exception to this.  We allow autochk to get an
            //  implicit lock on the volume while still allowing readers.  Once the
            //  the system is booted, though, we do not allow this type of access.
            //

            if (FlagOn( *DesiredAccess, (FILE_WRITE_DATA | FILE_APPEND_DATA) ) &&
                FsRtlAreVolumeStartupApplicationsComplete()) {

                ClearFlag( ShareAccess, FILE_SHARE_READ );
            }
#endif

            //
            //  Do a quick check here for handles on exclusive open.
            //

            if (!FlagOn(ShareAccess, FILE_SHARE_READ) &&
                !FatIsHandleCountZero( IrpContext, Vcb )) {

                try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
            }

            //
            //  Force Mm to get rid of its referenced file objects.
            //

            FatFlushFat( IrpContext, Vcb );

            FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );

            //
            //  If the user also does not want to share read then we check
            //  if anyone is already using the volume, and if so then we
            //  deny the access.  If the user wants to share read then
            //  we allow the current opens to stay provided they are only
            //  readonly opens and deny further opens.
            //

            if (!FlagOn(ShareAccess, FILE_SHARE_READ)) {

                if (Vcb->OpenFileCount != 0) {

                    try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
                }

            } else {

                if (Vcb->ReadOnlyCount != Vcb->OpenFileCount) {

                    try_return( Iosb.Status = STATUS_SHARING_VIOLATION );
                }
            }

            //
            //  Lock the volume
            //

            Vcb->VcbState |= VCB_STATE_FLAG_LOCKED;
            Vcb->FileObjectWithVcbLocked = FileObject;
            UnwindVolumeLock = TRUE;

            //
            //  Clean the volume
            //

            CleanedVolume = TRUE;

        }  else if (FlagOn( *DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA )) {

            //
            //  Flush the volume and let ourselves push the clean bit out if everything
            //  worked.
            //

            if (NT_SUCCESS( FatFlushVolume( IrpContext, Vcb, Flush ))) {

                CleanedVolume = TRUE;
            }
        }

        //
        //  Clean the volume if we believe it safe and reasonable.
        //

        if (CleanedVolume &&
            FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
            !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
            !CcIsThereDirtyData(Vcb->Vpb)) {

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

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

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

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

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

                FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
            }
        }

        //
        //  If the volume is already opened by someone then we need to check
        //  the share access
        //

        if (Vcb->DirectAccessOpenCount > 0) {

            if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
                                                              ShareAccess,
                                                              FileObject,
                                                              &Vcb->ShareAccess,
                                                              TRUE ))) {

                try_return( Iosb.Status );
            }

        } else {

            IoSetShareAccess( *DesiredAccess,
                              ShareAccess,
                              FileObject,
                              &Vcb->ShareAccess );
        }

        UnwindShareAccess = TRUE;

        //
        //  Set up the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserVolumeOpen,
                          Vcb,
                          UnwindCcb = FatCreateCcb( IrpContext ));

        FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers;

        Vcb->DirectAccessOpenCount += 1;
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
        UnwindCounts = TRUE;
        FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING;

        //
        //  At this point the open will succeed, so check if the user is getting explicit access
        //  to the device.  If not, we will note this so we can deny modifying FSCTL to it.
        //

        IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
        Status = FatExplicitDeviceAccessGranted( IrpContext,
                                                 Vcb->Vpb->RealDevice,
                                                 IrpSp->Parameters.Create.SecurityContext->AccessState,
                                                 (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
                                                                    UserMode :
                                                                    IrpContext->OriginatingIrp->RequestorMode ));

        if (NT_SUCCESS( Status )) {

            SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
        }

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_OPENED;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatOpenVolume );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) {

            if (UnwindCounts) {
                Vcb->DirectAccessOpenCount -= 1;
                Vcb->OpenFileCount -= 1;
                if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
            }
            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
            if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Vcb->ShareAccess ); }
            if (UnwindVolumeLock) { Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; }
        }

        DebugTrace(-1, Dbg, "FatOpenVolume -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenRootDcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition
    )

/*++

Routine Description:

    This routine opens the root dcb for the volume

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume whose dcb is being opened.

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    CreateDisposition - Supplies the create disposition for this operation

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

Arguments:

--*/

{
    PDCB RootDcb;
    IO_STATUS_BLOCK Iosb = {0};

    //
    //  The following variables are for abnormal termination
    //

    BOOLEAN UnwindShareAccess = FALSE;
    PCCB UnwindCcb = NULL;
    BOOLEAN UnwindCounts = FALSE;
    BOOLEAN RootDcbAcquired = FALSE;

    PAGED_CODE();

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

    //
    //  Locate the root dcb
    //

    RootDcb = Vcb->RootDcb;

    //
    //  Get the Dcb exlcusive.  This is important as cleanup does not
    //  acquire the Vcb.
    //

    (VOID)FatAcquireExclusiveFcb( IrpContext, RootDcb );
    RootDcbAcquired = TRUE;

    try {

        //
        //  Check the create disposition and desired access
        //

        if ((CreateDisposition != FILE_OPEN) &&
            (CreateDisposition != FILE_OPEN_IF)) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        if (!FatCheckFileAccess( IrpContext,
                                 RootDcb->DirentFatFlags,
                                 DesiredAccess)) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        //
        //  If the Root dcb is already opened by someone then we need
        //  to check the share access
        //

        if (RootDcb->OpenCount > 0) {

            if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
                                                              ShareAccess,
                                                              FileObject,
                                                              &RootDcb->ShareAccess,
                                                              TRUE ))) {

                try_return( Iosb );
            }

        } else {

            IoSetShareAccess( *DesiredAccess,
                              ShareAccess,
                              FileObject,
                              &RootDcb->ShareAccess );
        }

        UnwindShareAccess = TRUE;

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserDirectoryOpen,
                          RootDcb,
                          UnwindCcb = FatCreateCcb( IrpContext ));

        RootDcb->UncleanCount += 1;
        RootDcb->OpenCount += 1;
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
        UnwindCounts = TRUE;

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_OPENED;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatOpenRootDcb );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindCounts) {
                RootDcb->UncleanCount -= 1;
                RootDcb->OpenCount -= 1;
                Vcb->OpenFileCount -= 1;
                if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; }
            }
            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
            if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &RootDcb->ShareAccess ); }
        }

        if (RootDcbAcquired) {

            FatReleaseFcb( IrpContext, RootDcb );
        }

        DebugTrace(-1, Dbg, "FatOpenRootDcb -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingDcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB Dcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos,
    _Out_ PBOOLEAN OplockPostIrp
    )

/*++

Routine Description:

    This routine opens the specified existing dcb

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume containing the dcb

    Dcb - Supplies the already existing dcb

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    CreateDisposition - Supplies the create disposition for this operation

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};
    PBCB DirentBcb = NULL;
    PDIRENT Dirent;

    //
    //  The following variables are for abnormal termination
    //

    BOOLEAN UnwindShareAccess = FALSE;
    PCCB UnwindCcb = NULL;
    BOOLEAN DcbAcquired = FALSE;

#if (NTDDI_VERSION <= NTDDI_WIN7)
    UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif

    PAGED_CODE();

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

    //
    //  Get the Dcb exlcusive.  This is important as cleanup does not
    //  acquire the Vcb.
    //

    (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb );
    DcbAcquired = TRUE;

    try {

        *OplockPostIrp = FALSE;

        //
        //  Before spending any noticeable effort, see if we have the odd case
        //  of someone trying to delete-on-close the root dcb.  This will only
        //  happen if we're hit with a null-filename relative open via the root.
        //

        if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && DeleteOnClose) {

            Iosb.Status = STATUS_CANNOT_DELETE;
            try_return( Iosb );
        }

#if (NTDDI_VERSION >= NTDDI_WIN8)

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        if (Iosb.Status != STATUS_SUCCESS) {

            try_return( NOTHING );
        }

#endif
        //
        //  If the caller has no Ea knowledge, we immediately check for
        //  Need Ea's on the file.  We don't need to check for ea's on the
        //  root directory, because it never has any.  Fat32 doesn't have
        //  any, either.
        //

        if (NoEaKnowledge && NodeType(Dcb) != FAT_NTC_ROOT_DCB &&
            !FatIsFat32(Vcb)) {

            ULONG NeedEaCount;

            //
            //  Get the dirent for the file and then check that the need
            //  ea count is 0.
            //

            FatGetDirentFromFcbOrDcb( IrpContext,
                                      Dcb,
                                      FALSE,
                                      &Dirent,
                                      &DirentBcb );

            FatGetNeedEaCount( IrpContext,
                               Vcb,
                               Dirent,
                               &NeedEaCount );

            FatUnpinBcb( IrpContext, DirentBcb );

            if (NeedEaCount != 0) {

                Iosb.Status = STATUS_ACCESS_DENIED;
                try_return( Iosb );
            }
        }

        //
        //  Check the create disposition and desired access
        //

        if ((CreateDisposition != FILE_OPEN) &&
            (CreateDisposition != FILE_OPEN_IF)) {

            Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
            try_return( Iosb );
        }

        if (!FatCheckFileAccess( IrpContext,
                                 Dcb->DirentFatFlags,
                                 DesiredAccess)) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        //
        //  If the dcb is already opened by someone then we need
        //  to check the share access
        //

        if (Dcb->OpenCount > 0) {

            if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext,
                                                               FileObject,
                                                               Dcb,
                                                               DesiredAccess,
                                                               ShareAccess ))) {
#if (NTDDI_VERSION >= NTDDI_WIN8)

                PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
                NTSTATUS OplockBreakStatus = STATUS_SUCCESS;
                
                //
                //  If we got a sharing violation try to break outstanding handle
                //  oplocks and retry the sharing check.  If the caller specified
                //  FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock;
                //  we just return the sharing violation.
                //
                
                if ((Iosb.Status == STATUS_SHARING_VIOLATION) &&
                    !FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {
                
                    OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Dcb),
                                                           IrpContext->OriginatingIrp,
                                                           0,
                                                           IrpContext,
                                                           FatOplockComplete,
                                                           FatPrePostIrp );
                
                    //
                    //  If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP
                    //  has been posted and we need to stop working.
                    //
                
                    if (OplockBreakStatus == STATUS_PENDING) {
                
                        Iosb.Status = STATUS_PENDING;
                        *OplockPostIrp = TRUE;
                        try_return( NOTHING );
                
                    //
                    //  If FsRtlOplockBreakH returned an error we want to return that now.
                    //
                
                    } else if (!NT_SUCCESS( OplockBreakStatus )) {
                
                        Iosb.Status = OplockBreakStatus;
                        try_return( Iosb );
                
                    //
                    //  Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating
                    //  that there is no oplock to be broken.  The sharing violation is
                    //  returned in that case.
                    //
                    
                    } else {
                
                        NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS );
                
                        try_return( Iosb );
                    }
                
                //
                //  The initial sharing check failed with something other than sharing
                //  violation (which should never happen, but let's be future-proof),
                //  or we *did* get a sharing violation and the caller specified
                //  FILE_COMPLETE_IF_OPLOCKED.  Either way this create is over.
                //
                
                } else {
                
                    try_return( Iosb );
                }
#else

                try_return( Iosb );
#endif
            }
        }

#if (NTDDI_VERSION >= NTDDI_WIN8)

        //
        //  Now check that we can continue based on the oplock state of the
        //  directory.  If there are no open handles yet we don't need to do
        //  this check; oplocks can only exist when there are handles.
        //

        if (Dcb->UncleanCount != 0) {

            Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Dcb),
                                            IrpContext->OriginatingIrp,
                                            IrpContext,
                                            FatOplockComplete,
                                            FatPrePostIrp );
        }

        //
        //  if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
        //  to service an oplock break and we need to leave now.
        //

        if (Iosb.Status == STATUS_PENDING) {

            *OplockPostIrp = TRUE;
            try_return( NOTHING );
        }

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Fcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb),
                                             IrpContext->OriginatingIrp,
                                             (Dcb->UncleanCount + 1) );
        }

        //
        //  If we've encountered a failure we need to leave.  FsRtlCheckOplock
        //  will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated
        //  and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED
        //  on the create call.  That's an NT_SUCCESS code, so we need to keep
        //  going.
        //

        if ((Iosb.Status != STATUS_SUCCESS) &&
            (Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) {

            try_return( NOTHING );
        }

#endif

        //
        //  Now that we're done with the oplock work update the share counts.
        //  If the Dcb isn't yet opened we just set the share access rather than
        //  update it.
        //

        if (Dcb->OpenCount > 0) {

            IoUpdateShareAccess( FileObject, &Dcb->ShareAccess );

        } else {

            IoSetShareAccess( *DesiredAccess,
                              ShareAccess,
                              FileObject,
                              &Dcb->ShareAccess );
        }

        UnwindShareAccess = TRUE;

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserDirectoryOpen,
                          Dcb,
                          UnwindCcb = FatCreateCcb( IrpContext ));

        Dcb->UncleanCount += 1;
        Dcb->OpenCount += 1;
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }

        //
        //  Mark the delete on close bit if the caller asked for that.
        //

        {
            PCCB Ccb = (PCCB)FileObject->FsContext2;


            if (DeleteOnClose) {

                SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
            }
            if (FileNameOpenedDos) {
                
                SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
            }

        }

        //
        //  In case this was set, clear it now.
        //

        ClearFlag(Dcb->FcbState, FCB_STATE_DELAY_CLOSE);

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_OPENED;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatOpenExistingDcb );

        //
        //  Unpin the Dirent Bcb if pinned.
        //

        FatUnpinBcb( IrpContext, DirentBcb );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
            if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); }
        }

        if (DcbAcquired) {

            FatReleaseFcb( IrpContext, Dcb );
        }

        DebugTrace(-1, Dbg, "FatOpenExistingDcb -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PFCB Fcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos,
    _Out_ PBOOLEAN OplockPostIrp
    )

/*++

Routine Description:

    This routine opens the specified existing fcb

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume containing the Fcb

    Fcb - Supplies the already existing fcb

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    AllocationSize - Supplies the initial allocation if the file is being
        superseded or overwritten

    EaBuffer - Supplies the Ea set if the file is being superseded or
        overwritten

    EaLength - Supplies the size, in byte, of the EaBuffer

    FileAttributes - Supplies file attributes to use if the file is being
        superseded or overwritten

    CreateDisposition - Supplies the create disposition for this operation

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

    OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.

    FileNameOpenedDos - The caller hit the short side of the name pair finding
        this file

    OplockPostIrp - Address to store boolean indicating if the Irp needs to
        be posted to the Fsp.

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    PBCB DirentBcb = NULL;
    PDIRENT Dirent;

    ACCESS_MASK AddedAccess = 0;

    //
    //  The following variables are for abnormal termination
    //

    BOOLEAN UnwindShareAccess = FALSE;
    PCCB UnwindCcb = NULL;
    BOOLEAN DecrementFcbOpenCount = FALSE;
    BOOLEAN FcbAcquired = FALSE;

    PAGED_CODE();

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

    //
    //  Get the Fcb exlcusive.  This is important as cleanup does not
    //  acquire the Vcb.

    //

    (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
    FcbAcquired = TRUE;

    try {

        *OplockPostIrp = FALSE;

#if (NTDDI_VERSION >= NTDDI_WIN7)

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        if (Iosb.Status != STATUS_SUCCESS) {

            try_return( NOTHING );
        }
#endif

        //
        //  Take special action if there is a current batch oplock or
        //  batch oplock break in process on the Fcb.
        //

        if (FsRtlCurrentBatchOplock( FatGetFcbOplock(Fcb) )) {

            //
            //  We remember if a batch oplock break is underway for the
            //  case where the sharing check fails.
            //

            Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;

            Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
                                            IrpContext->OriginatingIrp,
                                            IrpContext,
                                            FatOplockComplete,
                                            FatPrePostIrp );

            //
            //  if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
            //  to service an oplock break and we need to leave now.
            //

            if (Iosb.Status == STATUS_PENDING) {

                *OplockPostIrp = TRUE;
                try_return( NOTHING );
            }
        }

        //
        //  Check if the user wanted to create the file, also special case
        //  the supersede and overwrite options.  Those add additional,
        //  possibly only implied, desired accesses to the caller, which
        //  we must be careful to pull back off if the caller did not actually
        //  request them.
        //
        //  In other words, check against the implied access, but do not modify
        //  share access as a result.
        //

        if (CreateDisposition == FILE_CREATE) {

            Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
            try_return( Iosb );

        } else if (CreateDisposition == FILE_SUPERSEDE) {

            SetFlag( AddedAccess,
                     DELETE & ~(*DesiredAccess) );

            *DesiredAccess |= DELETE;

        } else if ((CreateDisposition == FILE_OVERWRITE) ||
                   (CreateDisposition == FILE_OVERWRITE_IF)) {

            SetFlag( AddedAccess,
                     (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) );

            *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
        }

        //
        //  Check the desired access
        //

        if (!FatCheckFileAccess( IrpContext,
                                 Fcb->DirentFatFlags,
                                 DesiredAccess )) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        //
        //  Check for trying to delete a read only file.
        //

        if (DeleteOnClose &&
            FlagOn( Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY )) {

            Iosb.Status = STATUS_CANNOT_DELETE;
            try_return( Iosb );
        }

        //
        //  If we are asked to do an overwrite or supersede operation then
        //  deny access for files where the file attributes for system and
        //  hidden do not match
        //

        if ((CreateDisposition == FILE_SUPERSEDE) ||
            (CreateDisposition == FILE_OVERWRITE) ||
            (CreateDisposition == FILE_OVERWRITE_IF)) {

            BOOLEAN Hidden;
            BOOLEAN System;

            Hidden = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_HIDDEN );
            System = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_SYSTEM );

            if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
                (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) {

                DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0);


                Iosb.Status = STATUS_ACCESS_DENIED;
                try_return( Iosb );
            }

            //
            //  If this media is write protected, don't even try the create.
            //

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

                //
                //  Set the real device for the pop-up info, and set the verify
                //  bit in the device object, so that we will force a verify
                //  in case the user put the correct media back in.
                //

                IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
                                              Vcb->Vpb->RealDevice );

                SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);

                FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
            }
        }

        //
        //  Check if the Fcb has the proper share access.  This routine will also
        //  check for writable user secions if the user did not allow write sharing.
        //

        if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext,
                                                           FileObject,
                                                           Fcb,
                                                           DesiredAccess,
                                                           ShareAccess ))) {

#if (NTDDI_VERSION >= NTDDI_WIN7)

            PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
            NTSTATUS OplockBreakStatus = STATUS_SUCCESS;

            //
            //  If we got a sharing violation try to break outstanding handle
            //  oplocks and retry the sharing check.  If the caller specified
            //  FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock;
            //  we just return the sharing violation.
            //
            
            if ((Iosb.Status == STATUS_SHARING_VIOLATION) &&
                !FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) {

                OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Fcb),
                                                       IrpContext->OriginatingIrp,
                                                       0,
                                                       IrpContext,
                                                       FatOplockComplete,
                                                       FatPrePostIrp );

                //
                //  If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP
                //  has been posted and we need to stop working.
                //

                if (OplockBreakStatus == STATUS_PENDING) {

                    Iosb.Status = STATUS_PENDING;
                    *OplockPostIrp = TRUE;
                    try_return( NOTHING );

                //
                //  If FsRtlOplockBreakH returned an error we want to return that now.
                //

                } else if (!NT_SUCCESS( OplockBreakStatus )) {

                    Iosb.Status = OplockBreakStatus;
                    try_return( Iosb );

                //
                //  Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating
                //  that there is no oplock to be broken.  The sharing violation is
                //  returned in that case.
                //
                
                } else {

                    NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS );

                    try_return( Iosb );
                }

            //
            //  The initial sharing check failed with something other than sharing
            //  violation (which should never happen, but let's be future-proof),
            //  or we *did* get a sharing violation and the caller specified
            //  FILE_COMPLETE_IF_OPLOCKED.  Either way this create is over.
            //

            } else {

                try_return( Iosb );
            }

#else

            try_return( Iosb );

#endif
        }

        //
        //  Now check that we can continue based on the oplock state of the
        //  file.  If there are no open handles yet we don't need to do this
        //  check; oplocks can only exist when there are handles.
        //
        //  It is important that we modified the DesiredAccess in place so
        //  that the Oplock check proceeds against any added access we had
        //  to give the caller.
        //

        if (Fcb->UncleanCount != 0) {

            Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
                                            IrpContext->OriginatingIrp,
                                            IrpContext,
                                            FatOplockComplete,
                                            FatPrePostIrp );
        }

        //
        //  if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted
        //  to service an oplock break and we need to leave now.
        //

        if (Iosb.Status == STATUS_PENDING) {

            *OplockPostIrp = TRUE;
            try_return( NOTHING );
        }

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Fcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb),
                                             IrpContext->OriginatingIrp,
                                             (Fcb->UncleanCount + 1) );
        }

        //
        //  If we've encountered a failure we need to leave.  FsRtlCheckOplock
        //  will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated
        //  and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED
        //  on the create call.  That's an NT_SUCCESS code, so we need to keep
        //  going.
        //

        if ((Iosb.Status != STATUS_SUCCESS) &&
            (Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) {

            try_return( NOTHING );
        }

        //
        //  Set the flag indicating if Fast I/O is possible
        //

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

        //
        //  If the user wants write access access to the file make sure there
        //  is not a process mapping this file as an image.  Any attempt to
        //  delete the file will be stopped in fileinfo.c
        //
        //  If the user wants to delete on close, we must check at this
        //  point though.
        //

        if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) {

            Fcb->OpenCount += 1;
            DecrementFcbOpenCount = TRUE;

            if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
                                      MmFlushForWrite )) {

                Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE :
                                              STATUS_SHARING_VIOLATION;
                try_return( Iosb );
            }
        }

        //
        //  If this is a non-cached open on a non-paging file, and there
        //  are no open cached handles, but there is a still a data
        //  section, attempt a flush and purge operation to avoid cache
        //  coherency overhead later.  We ignore any I/O errors from
        //  the flush.
        //
        //  We set the CREATE_IN_PROGRESS flag to prevent the Fcb from
        //  going away out from underneath us.
        //

        if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
            (Fcb->UncleanCount == Fcb->NonCachedUncleanCount) &&
            (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) &&
            !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {

            SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);

            CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL );

            //
            //  Grab and release PagingIo to serialize ourselves with the lazy writer.
            //  This will work to ensure that all IO has completed on the cached
            //  data and we will succesfully tear away the cache section.
            //

            ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
            ExReleaseResourceLite( Fcb->Header.PagingIoResource );

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

            ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);
        }

        //
        //  Check if the user only wanted to open the file
        //

        if ((CreateDisposition == FILE_OPEN) ||
            (CreateDisposition == FILE_OPEN_IF)) {

            DebugTrace(0, Dbg, "Doing open operation\n", 0);

            //
            //  If the caller has no Ea knowledge, we immediately check for
            //  Need Ea's on the file.
            //

            if (NoEaKnowledge && !FatIsFat32(Vcb)) {

                ULONG NeedEaCount;

                //
                //  Get the dirent for the file and then check that the need
                //  ea count is 0.
                //

                FatGetDirentFromFcbOrDcb( IrpContext,
                                          Fcb,
                                          FALSE,
                                          &Dirent,
                                          &DirentBcb );

                FatGetNeedEaCount( IrpContext,
                                   Vcb,
                                   Dirent,
                                   &NeedEaCount );

                FatUnpinBcb( IrpContext, DirentBcb );

                if (NeedEaCount != 0) {

                    Iosb.Status = STATUS_ACCESS_DENIED;
                    try_return( Iosb );
                }
            }

            //
            //  Everything checks out okay, so setup the context and
            //  section object pointers.
            //

            FatSetFileObject( FileObject,
                              UserFileOpen,
                              Fcb,
                              UnwindCcb = FatCreateCcb( IrpContext ));

            FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;

            //
            //  Fill in the information field, the status field is already
            //  set.
            //

            Iosb.Information = FILE_OPENED;

            try_return( Iosb );
        }

        //
        //  Check if we are to supersede/overwrite the file, we can wait for
        //  any I/O at this point
        //

        if ((CreateDisposition == FILE_SUPERSEDE) ||
            (CreateDisposition == FILE_OVERWRITE) ||
            (CreateDisposition == FILE_OVERWRITE_IF)) {

            NTSTATUS OldStatus;

            DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0);

            //
            //  We remember the previous status code because it may contain
            //  information about the oplock status.
            //

            OldStatus = Iosb.Status;

            //
            //  Determine the granted access for this operation now.
            //

            if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {

                try_return( Iosb );
            }

            //
            //  And overwrite the file.
            //

            Iosb = FatSupersedeOrOverwriteFile( IrpContext,
                                                FileObject,
                                                Fcb,
                                                AllocationSize,
                                                EaBuffer,
                                                EaLength,
                                                FileAttributes,
                                                CreateDisposition,
                                                NoEaKnowledge );

            if (Iosb.Status == STATUS_SUCCESS) {

                Iosb.Status = OldStatus;
            }

            try_return( Iosb );
        }

        //
        //  If we ever get here then the I/O system gave us some bad input
        //

#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
        FatBugCheck( CreateDisposition, 0, 0 );

    try_exit: NOTHING;

        //
        //  Update the share access and counts if successful
        //

        if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS(Iosb.Status)) {

            //
            //  Now, we may have added some access bits above to indicate the access
            //  this caller would conflict with (as opposed to what they get) in order
            //  to perform the overwrite/supersede.  We need to make a call to that will
            //  recalculate the bits in the fileobject to reflect the real access they
            //  will get.
            //

            if (AddedAccess) {

                NTSTATUS Status;

                ClearFlag( *DesiredAccess, AddedAccess );

#pragma prefast( suppress:28931, "it needs to be there for debug assert" );                
                Status = IoCheckShareAccess( *DesiredAccess,
                                             ShareAccess,
                                             FileObject,
                                             &Fcb->ShareAccess,
                                             TRUE );

                //
                //  It must be the case that we are really asking for less access, so
                //  any conflict must have been detected before this point.
                //

                NT_ASSERT( Status == STATUS_SUCCESS );

            } else {

                IoUpdateShareAccess( FileObject, &Fcb->ShareAccess );
            }

            UnwindShareAccess = TRUE;

            //
            //  In case this was set, clear it now.
            //

            ClearFlag(Fcb->FcbState, FCB_STATE_DELAY_CLOSE);

            Fcb->UncleanCount += 1;
            Fcb->OpenCount += 1;
            if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
                Fcb->NonCachedUncleanCount += 1;
            }
            Vcb->OpenFileCount += 1;
            if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }

            {
                PCCB Ccb;
                PIO_STACK_LOCATION IrpSp;

                Ccb = (PCCB)FileObject->FsContext2;

                //
                //  Mark the DeleteOnClose bit if the operation was successful.
                //

                if ( DeleteOnClose ) {

                    SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
                }

                //
                //  Mark the OpenedByShortName bit if the operation was successful.
                //

                if ( FileNameOpenedDos ) {

                    SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
                }

                //
                //  Mark the ManageVolumeAccess bit if the privilege is held.
                //

                IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );

                if (FatCheckManageVolumeAccess( IrpContext,
                                                IrpSp->Parameters.Create.SecurityContext->AccessState,
                                                (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
                                                                   UserMode :
                                                                   IrpContext->OriginatingIrp->RequestorMode ))) {

                    SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
                }
            }
        }

    } finally {

        DebugUnwind( FatOpenExistingFcb );

        //
        //  Unpin the Dirent Bcb if pinned.
        //

        FatUnpinBcb( IrpContext, DirentBcb );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
            if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); }
        }

        if (DecrementFcbOpenCount) {

            Fcb->OpenCount -= 1;

            if (Fcb->OpenCount == 0) {
                if (ARGUMENT_PRESENT( FileObject )) {
                    FileObject->SectionObjectPointer = NULL;
                }
                FatDeleteFcb( IrpContext, &Fcb );
                FcbAcquired = FALSE;
            }
        }

        if (FcbAcquired) {

            FatReleaseFcb( IrpContext, Fcb );
        }

        DebugTrace(-1, Dbg, "FatOpenExistingFcb -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}

//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenTargetDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PDCB Dcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ BOOLEAN DoesNameExist,
    _In_ BOOLEAN FileNameOpenedDos
    )

/*++

Routine Description:

    This routine opens the target directory and replaces the name in the
    file object with the remaining name.

Arguments:

    FileObject - Supplies the File object

    Dcb - Supplies an already existing dcb that we are going to open

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    DoesNameExist - Indicates if the file name already exists in the
        target directory.


Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    //
    //  The following variables are for abnormal termination
    //

    BOOLEAN UnwindShareAccess = FALSE;
    PCCB UnwindCcb = NULL;
    BOOLEAN DcbAcquired = FALSE;

    PAGED_CODE();

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

    //
    //  Get the Dcb exlcusive.  This is important as cleanup does not
    //  acquire the Vcb.
    //

    (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb );
    DcbAcquired = TRUE;

    try {

        ULONG i;

        //
        //  If the Dcb is already opened by someone then we need
        //  to check the share access
        //

        if (Dcb->OpenCount > 0) {

            if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess,
                                                              ShareAccess,
                                                              FileObject,
                                                              &Dcb->ShareAccess,
                                                              TRUE ))) {

                try_return( Iosb );
            }

        } else {

            IoSetShareAccess( *DesiredAccess,
                              ShareAccess,
                              FileObject,
                              &Dcb->ShareAccess );
        }

        UnwindShareAccess = TRUE;

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserDirectoryOpen,
                          Dcb,
                          UnwindCcb = FatCreateCcb( IrpContext ));

        Dcb->UncleanCount += 1;
        Dcb->OpenCount += 1;
        Dcb->Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Dcb->Vcb->ReadOnlyCount += 1; }

        //
        //  Update the name in the file object, by definition the remaining
        //  part must be shorter than the original file name so we'll just
        //  overwrite the file name.
        //

        i = FileObject->FileName.Length/sizeof(WCHAR) - 1;

        //
        //  Get rid of a trailing backslash
        //

        if (FileObject->FileName.Buffer[i] == L'\\') {

            NT_ASSERT(i != 0);

            FileObject->FileName.Length -= sizeof(WCHAR);
            i -= 1;
        }

        //
        //  Find the first non-backslash character.  i will be its index.
        //

        while (TRUE) {

            if (FileObject->FileName.Buffer[i] == L'\\') {

                i += 1;
                break;
            }

            if (i == 0) {
                break;
            }

            i--;
        }

        if (i) {

            FileObject->FileName.Length -= (USHORT)(i * sizeof(WCHAR));

            RtlMoveMemory( &FileObject->FileName.Buffer[0],
                           &FileObject->FileName.Buffer[i],
                           FileObject->FileName.Length );
        }

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = (DoesNameExist ? FILE_EXISTS : FILE_DOES_NOT_EXIST);

        if ( ( NT_SUCCESS(Iosb.Status) ) && ( DoesNameExist ) ) {
            PCCB Ccb;

            Ccb = (PCCB)FileObject->FsContext2;

            //
            //  Mark the OpenedByShortName bit if the operation was successful.
            //

            if ( FileNameOpenedDos ) {

                SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
            }
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatOpenTargetDirectory );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
            if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); }
        }

        if (DcbAcquired) {

            FatReleaseFcb( IrpContext, Dcb );
        }

        DebugTrace(-1, Dbg, "FatOpenTargetDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}



//
//  Internal support routine
//
_Success_(return.Status == STATUS_SUCCESS)
_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
#pragma warning(suppress:6101) // bug in PREFast means the _Success_ annotation is not correctly applied
FatOpenExistingDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Outptr_result_maybenull_ PDCB *Dcb,
    _In_ PDCB ParentDcb,
    _In_ PDIRENT Dirent,
    _In_ ULONG LfnByteOffset,
    _In_ ULONG DirentByteOffset,
    _In_ PUNICODE_STRING Lfn,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN FileNameOpenedDos,
    _In_ BOOLEAN OpenRequiringOplock
    )

/*++

Routine Description:

    This routine opens the specified directory.  The directory has not
    previously been opened.

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume containing the dcb

    Dcb - Returns the newly-created DCB for the file.

    ParentDcb - Supplies the parent directory containing the subdirectory
        to be opened

    DirectoryName - Supplies the file name of the directory being opened.

    Dirent - Supplies the dirent for the directory being opened

    LfnByteOffset - Tells where the Lfn begins.  If there is no Lfn
        this field is the same as DirentByteOffset.

    DirentByteOffset - Supplies the Vbo of the dirent within its parent
        directory

    Lfn - May supply a long name for the file.

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    CreateDisposition - Supplies the create disposition for this operation

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

    OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    //
    //  The following variables are for abnormal termination
    //

    PDCB UnwindDcb = NULL;
    PCCB UnwindCcb = NULL;

    UNREFERENCED_PARAMETER( DeleteOnClose );
#if (NTDDI_VERSION <= NTDDI_WIN7)
    UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif

    PAGED_CODE();

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

    try {

        //
        //  If the caller has no Ea knowledge, we immediately check for
        //  Need Ea's on the file.
        //

        if (NoEaKnowledge && !FatIsFat32(Vcb)) {

            ULONG NeedEaCount;

            FatGetNeedEaCount( IrpContext,
                               Vcb,
                               Dirent,
                               &NeedEaCount );

            if (NeedEaCount != 0) {

                Iosb.Status = STATUS_ACCESS_DENIED;
                try_return( Iosb );
            }
        }

        //
        //  Check the create disposition and desired access
        //

        if ((CreateDisposition != FILE_OPEN) &&
            (CreateDisposition != FILE_OPEN_IF)) {

            Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
            try_return( Iosb );
        }

        if (!FatCheckFileAccess( IrpContext,
                                 Dirent->Attributes,
                                 DesiredAccess)) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        //
        //  Create a new dcb for the directory
        //

        *Dcb = UnwindDcb = FatCreateDcb( IrpContext,
                                        Vcb,
                                        ParentDcb,
                                        LfnByteOffset,
                                        DirentByteOffset,
                                        Dirent,
                                        Lfn );
#if (NTDDI_VERSION >= NTDDI_WIN8)

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Dcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Fcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Dcb),
                                             IrpContext->OriginatingIrp,
                                             ((*Dcb)->UncleanCount + 1) );
        }

        //
        //  Get out if either of the above calls failed.  Raise to trigger
        //  cleanup of the new Dcb.
        //

        if (Iosb.Status != STATUS_SUCCESS) {

            NT_ASSERT( Iosb.Status != STATUS_PENDING );
            
            FatRaiseStatus( IrpContext, Iosb.Status );
        }
#endif

        //
        //  Setup our share access
        //

        IoSetShareAccess( *DesiredAccess,
                          ShareAccess,
                          FileObject,
                          &(*Dcb)->ShareAccess );

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserDirectoryOpen,
                          (*Dcb),
                          UnwindCcb = FatCreateCcb( IrpContext ));

        (*Dcb)->UncleanCount += 1;
        (*Dcb)->OpenCount += 1;
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_OPENED;

        if ( NT_SUCCESS(Iosb.Status) ) {
            PCCB Ccb;

            Ccb = (PCCB)FileObject->FsContext2;

            //
            //  Mark the OpenedByShortName bit if the operation was successful.
            //

            if ( FileNameOpenedDos ) {

                SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
            }
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatOpenExistingDirectory );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindDcb != NULL) { 
                if (ARGUMENT_PRESENT( FileObject )) {
                    FileObject->SectionObjectPointer = NULL;
                }
                FatDeleteFcb( IrpContext, &UnwindDcb ); 
                *Dcb = NULL;
            }
            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
        }

        DebugTrace(-1, Dbg, "FatOpenExistingDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatOpenExistingFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Outptr_result_maybenull_ PFCB *Fcb,
    _In_ PDCB ParentDcb,
    _In_ PDIRENT Dirent,
    _In_ ULONG LfnByteOffset,
    _In_ ULONG DirentByteOffset,
    _In_ PUNICODE_STRING Lfn,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN IsPagingFile,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN FileNameOpenedDos
    )

/*++

Routine Description:

    This routine opens the specified file.  The file has not previously
    been opened.

Arguments:

    FileObject - Supplies the File object

    Vcb - Supplies the Vcb denoting the volume containing the file

    Fcb - Returns the newly-created FCB for the file.

    ParentDcb - Supplies the parent directory containing the file to be
        opened

    Dirent - Supplies the dirent for the file being opened

    LfnByteOffset - Tells where the Lfn begins.  If there is no Lfn
        this field is the same as DirentByteOffset.

    DirentByteOffset - Supplies the Vbo of the dirent within its parent
        directory

    Lfn - May supply a long name for the file.

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the share access of the caller

    AllocationSize - Supplies the initial allocation if the file is being
        superseded, overwritten, or created.

    EaBuffer - Supplies the Ea set if the file is being superseded,
        overwritten, or created.

    EaLength - Supplies the size, in byte, of the EaBuffer

    FileAttributes - Supplies file attributes to use if the file is being
        superseded, overwritten, or created

    CreateDisposition - Supplies the create disposition for this operation

    IsPagingFile - Indicates if this is the paging file being opened.

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

    OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.

    FileNameOpenedDos - The caller opened this file by hitting the 8.3 side
        of the Lfn/8.3 pair

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    ACCESS_MASK AddedAccess = 0;

    //
    //  The following variables are for abnormal termination
    //

    PFCB UnwindFcb = NULL;
    PCCB UnwindCcb = NULL;

#if (NTDDI_VERSION < NTDDI_WIN7)
    UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif

    PAGED_CODE();

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

    try {

        //
        //  Check if the user wanted to create the file or if access is
        //  denied
        //

        if (CreateDisposition == FILE_CREATE) {
            Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
            try_return( Iosb );

        } else if ((CreateDisposition == FILE_SUPERSEDE) && !IsPagingFile) {

            SetFlag( AddedAccess,
                     DELETE & ~(*DesiredAccess) );

            *DesiredAccess |= DELETE;

        } else if (((CreateDisposition == FILE_OVERWRITE) ||
                    (CreateDisposition == FILE_OVERWRITE_IF)) && !IsPagingFile) {

            SetFlag( AddedAccess,
                     (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) );

            *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
        }

        if (!FatCheckFileAccess( IrpContext,
                                 Dirent->Attributes,
                                 DesiredAccess)) {

            Iosb.Status = STATUS_ACCESS_DENIED;
            try_return( Iosb );
        }

        //
        //  Check for trying to delete a read only file.
        //

        if (DeleteOnClose &&
            FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY )) {

            Iosb.Status = STATUS_CANNOT_DELETE;
            try_return( Iosb );
        }

        //
        //  IF we are asked to do an overwrite or supersede operation then
        //  deny access for files where the file attributes for system and
        //  hidden do not match
        //

        if ((CreateDisposition == FILE_SUPERSEDE) ||
            (CreateDisposition == FILE_OVERWRITE) ||
            (CreateDisposition == FILE_OVERWRITE_IF)) {

            BOOLEAN Hidden;
            BOOLEAN System;

            Hidden = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_HIDDEN );
            System = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_SYSTEM );

            if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
                (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) {

                DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0);

                if ( !IsPagingFile ) {

                    Iosb.Status = STATUS_ACCESS_DENIED;
                    try_return( Iosb );
                }
            }

            //
            //  If this media is write protected, don't even try the create.
            //

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

                //
                //  Set the real device for the pop-up info, and set the verify
                //  bit in the device object, so that we will force a verify
                //  in case the user put the correct media back in.
                //


                IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
                                              Vcb->Vpb->RealDevice );

                SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);

                FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED );
            }
        }

        //
        //  Create a new Fcb for the file, and set the file size in
        //  the fcb.
        //

        *Fcb = UnwindFcb = FatCreateFcb( IrpContext,
                                         Vcb,
                                         ParentDcb,
                                         LfnByteOffset,
                                         DirentByteOffset,
                                         Dirent,
                                         Lfn,
                                         IsPagingFile,
                                         FALSE );

        //
        //  If this is a paging file, lookup the allocation size so that
        //  the Mcb is always valid
        //

        if (IsPagingFile) {

            FatLookupFileAllocationSize( IrpContext, *Fcb );
        }

#if (NTDDI_VERSION >= NTDDI_WIN7)

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Fcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Fcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Fcb),
                                             IrpContext->OriginatingIrp,
                                             ((*Fcb)->UncleanCount + 1) );
        }

        //
        //  Get out if either of the above calls failed.  Raise to trigger
        //  cleanup of the new Fcb.
        //

        if (Iosb.Status != STATUS_SUCCESS) {

            NT_ASSERT( Iosb.Status != STATUS_PENDING );
            
            FatRaiseStatus( IrpContext, Iosb.Status );
        }
#endif

        //
        //  Now case on whether we are to simply open, supersede, or
        //  overwrite the file.
        //

        switch (CreateDisposition) {

        case FILE_OPEN:
        case FILE_OPEN_IF:

            DebugTrace(0, Dbg, "Doing only an open operation\n", 0);

            //
            //  If the caller has no Ea knowledge, we immediately check for
            //  Need Ea's on the file.
            //

            if (NoEaKnowledge && !FatIsFat32(Vcb)) {

                ULONG NeedEaCount;

                FatGetNeedEaCount( IrpContext,
                                   Vcb,
                                   Dirent,
                                   &NeedEaCount );

                if (NeedEaCount != 0) {

                    FatRaiseStatus( IrpContext, STATUS_ACCESS_DENIED );
                }
            }

            //
            //  Setup the context and section object pointers.
            //

            FatSetFileObject( FileObject,
                              UserFileOpen,
                              *Fcb,
                              UnwindCcb = FatCreateCcb( IrpContext ));

            FileObject->SectionObjectPointer = &(*Fcb)->NonPaged->SectionObjectPointers;

            Iosb.Status = STATUS_SUCCESS;
            Iosb.Information = FILE_OPENED;
            break;

        case FILE_SUPERSEDE:
        case FILE_OVERWRITE:
        case FILE_OVERWRITE_IF:

            DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0);

            //
            //  Determine the granted access for this operation now.
            //

            if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {

                try_return( Iosb );
            }

            Iosb = FatSupersedeOrOverwriteFile( IrpContext,
                                                FileObject,
                                                *Fcb,
                                                AllocationSize,
                                                EaBuffer,
                                                EaLength,
                                                FileAttributes,
                                                CreateDisposition,
                                                NoEaKnowledge );
            break;

        default:

            DebugTrace(0, Dbg, "Illegal Create Disposition\n", 0);

#pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
            FatBugCheck( CreateDisposition, 0, 0 );
            break;
        }

    try_exit: NOTHING;

        //
        //  Setup our share access and counts if things were successful.
        //

        if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS( Iosb.Status )) {

            //
            //  Remove any virtual access the caller needed to check against, but will
            //  not really receive.  Overwrite/supersede is a bit of a special case.
            //

            ClearFlag( *DesiredAccess, AddedAccess );

            IoSetShareAccess( *DesiredAccess,
                              ShareAccess,
                              FileObject,
                              &(*Fcb)->ShareAccess );

            (*Fcb)->UncleanCount += 1;
            (*Fcb)->OpenCount += 1;
            if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
                (*Fcb)->NonCachedUncleanCount += 1;
            }
            Vcb->OpenFileCount += 1;
            if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }
        }

        {
            PCCB Ccb;
            PIO_STACK_LOCATION IrpSp;

            Ccb = (PCCB)FileObject->FsContext2;

            if ( NT_SUCCESS(Iosb.Status) ) {

                //
                //  Mark the DeleteOnClose bit if the operation was successful.
                //

                if ( DeleteOnClose ) {

                    SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
                }

                //
                //  Mark the OpenedByShortName bit if the operation was successful.
                //

                if ( FileNameOpenedDos ) {

                    SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
                }

                //
                //  Mark the ManageVolumeAccess bit if the privilege is held.
                //

                IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );

                if (FatCheckManageVolumeAccess( IrpContext,
                                                IrpSp->Parameters.Create.SecurityContext->AccessState,
                                                (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
                                                                   UserMode :
                                                                   IrpContext->OriginatingIrp->RequestorMode ))) {
                
                    SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
                }

            }
        }


    } finally {

        DebugUnwind( FatOpenExistingFile );

        //
        //  If this is an abnormal termination then undo our work
        //

        if (AbnormalTermination()) {

            if (UnwindFcb != NULL) { 
                if (ARGUMENT_PRESENT( FileObject )) {
                    FileObject->SectionObjectPointer = NULL;
                }
                FatDeleteFcb( IrpContext, &UnwindFcb );
                *Fcb = NULL;
            }
            
            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
        }

        DebugTrace(-1, Dbg, "FatOpenExistingFile -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewDirectory (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB ParentDcb,
    _In_ POEM_STRING OemName,
    _In_ PUNICODE_STRING UnicodeName,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock
    )

/*++

Routine Description:

    This routine creates a new directory.  The directory has already been
    verified not to exist yet.

Arguments:

    FileObject - Supplies the file object for the newly created directory

    Vcb - Supplies the Vcb denote the volume to contain the new directory

    ParentDcb - Supplies the parent directory containg the newly created
        directory

    OemName - Supplies the Oem name for the newly created directory.  It may
        or maynot be 8.3 complient, but will be upcased.

    UnicodeName - Supplies the Unicode name for the newly created directory.
        It may or maynot be 8.3 complient.  This name contains the original
        case information.

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the shared access of the caller

    EaBuffer - Supplies the Ea set for the newly created directory

    EaLength - Supplies the length, in bytes, of EaBuffer

    FileAttributes - Supplies the file attributes for the newly created
        directory.

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

    OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb;

    PDCB Dcb = NULL;
    PCCB Ccb = NULL;

    PDIRENT Dirent = NULL;
    PBCB DirentBcb = NULL;
    ULONG DirentsNeeded;
    ULONG DirentByteOffset;

    PDIRENT ShortDirent;
    ULONG ShortDirentByteOffset;

    USHORT EaHandle;

    BOOLEAN AllLowerComponent;
    BOOLEAN AllLowerExtension;
    BOOLEAN CreateLfn;

    ULONG BytesInFirstPage = 0;
    ULONG DirentsInFirstPage = 0;
    PDIRENT FirstPageDirent = 0;

    PBCB SecondPageBcb = NULL;
    ULONG SecondPageOffset;
    PDIRENT SecondPageDirent = NULL;

    BOOLEAN DirentFromPool = FALSE;

    OEM_STRING ShortName;
    UCHAR ShortNameBuffer[12];

#if (NTDDI_VERSION <= NTDDI_WIN7)
    UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif

    PAGED_CODE();

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

    ShortName.Length = 0;
    ShortName.MaximumLength = 12;
    ShortName.Buffer = (PCHAR)&ShortNameBuffer[0];

    EaHandle = 0;

    //
    //  We fail this operation if the caller doesn't understand Ea's.
    //

    if (NoEaKnowledge
        && EaLength > 0) {

        Iosb.Status = STATUS_ACCESS_DENIED;

        DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status);
        return Iosb;
    }

    //
    //  DeleteOnClose and ReadOnly are not compatible.
    //

    if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {

        Iosb.Status = STATUS_CANNOT_DELETE;
        return Iosb;
    }

    //  Now get the names that we will be using.
    //

    FatSelectNames( IrpContext,
                    ParentDcb,
                    OemName,
                    UnicodeName,
                    &ShortName,
                    NULL,
                    &AllLowerComponent,
                    &AllLowerExtension,
                    &CreateLfn );

    //
    //  If we are not in Chicago mode, ignore the magic bits.
    //

    if (!FatData.ChicagoMode) {

        AllLowerComponent = FALSE;
        AllLowerExtension = FALSE;
        CreateLfn = FALSE;
    }

    //
    //  Create/allocate a new dirent
    //

    DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(UnicodeName) + 1 : 1;

    DirentByteOffset = FatCreateNewDirent( IrpContext,
                                           ParentDcb,
                                           DirentsNeeded );
    try {

        FatPrepareWriteDirectoryFile( IrpContext,
                                      ParentDcb,
                                      DirentByteOffset,
                                      sizeof(DIRENT),
                                      &DirentBcb,
                                      &Dirent,
                                      FALSE,
                                      TRUE,
                                      &Iosb.Status );

        NT_ASSERT( NT_SUCCESS( Iosb.Status ) && DirentBcb && Dirent );

        //
        //  Deal with the special case of an LFN + Dirent structure crossing
        //  a page boundry.
        //

        if ((DirentByteOffset / PAGE_SIZE) !=
            ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {

            SecondPageBcb;
            SecondPageOffset;
            SecondPageDirent;

            SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;

            BytesInFirstPage = SecondPageOffset - DirentByteOffset;

            DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);

            FatPrepareWriteDirectoryFile( IrpContext,
                                          ParentDcb,
                                          SecondPageOffset,
                                          sizeof(DIRENT),
                                          &SecondPageBcb,
                                          &SecondPageDirent,
                                          FALSE,
                                          TRUE,
                                          &Iosb.Status );

            NT_ASSERT( NT_SUCCESS( Iosb.Status ) && SecondPageBcb && SecondPageDirent );

            FirstPageDirent = Dirent;

            Dirent = FsRtlAllocatePoolWithTag( PagedPool,
                                               DirentsNeeded * sizeof(DIRENT),
                                               TAG_DIRENT );

            DirentFromPool = TRUE;
        }

        //
        //  Bump up Dirent and DirentByteOffset
        //

        ShortDirent = Dirent + DirentsNeeded - 1;
        ShortDirentByteOffset = DirentByteOffset +
                                (DirentsNeeded - 1) * sizeof(DIRENT);

        NT_ASSERT( NT_SUCCESS( Iosb.Status ));


        //
        //  Fill in the fields of the dirent.
        //

        FatConstructDirent( IrpContext,
                            ShortDirent,
                            &ShortName,
                            AllLowerComponent,
                            AllLowerExtension,
                            CreateLfn ? UnicodeName : NULL,
                            (UCHAR)(FileAttributes | FAT_DIRENT_ATTR_DIRECTORY),
                            TRUE,
                            NULL );

        //
        //  If the dirent crossed pages, we have to do some real gross stuff.
        //

        if (DirentFromPool) {

            RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage );

            RtlCopyMemory( SecondPageDirent,
                           Dirent + DirentsInFirstPage,
                           DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage );

            ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1;
        }

        //
        //  Create a new dcb for the directory.
        //

        Dcb = FatCreateDcb( IrpContext,
                            Vcb,
                            ParentDcb,
                            DirentByteOffset,
                            ShortDirentByteOffset,
                            ShortDirent,
                            CreateLfn ? UnicodeName : NULL );

#if (NTDDI_VERSION >= NTDDI_WIN8)
        //
        //  The next three FsRtl calls are for oplock work.  We deliberately
        //  do these here so that if either call fails we will be able to 
        //  clean up without adding a bunch of code to unwind counts, fix
        //  the file object, etc.
        //

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Dcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb),
                                             IrpContext->OriginatingIrp,
                                             (Dcb->UncleanCount + 1) );
        }

        //
        //  Break parent directory oplock.  Directory oplock breaks are always
        //  advisory, so we will never block/get STATUS_PENDING here.  On the
        //  off chance this fails with INSUFFICIENT_RESOURCES we do it here
        //  where we can still tolerate a failure.
        //
        
        if (Iosb.Status == STATUS_SUCCESS) {

            Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb),
                                              IrpContext->OriginatingIrp,
                                              OPLOCK_FLAG_PARENT_OBJECT,
                                              NULL,
                                              NULL,
                                              NULL );
        }
        
        //
        //  Get out if any of the oplock calls failed.
        //

        if (Iosb.Status != STATUS_SUCCESS) {

            FatRaiseStatus( IrpContext, Iosb.Status );
        }
#endif

        //
        //  Tentatively add the new Ea's,
        //

        if (EaLength > 0) {

            //
            //  This returns false if we are trying to create a file
            //  with Need Ea's and don't understand EA's.
            //

            FatCreateEa( IrpContext,
                         Dcb->Vcb,
                         (PUCHAR) EaBuffer,
                         EaLength,
                         &Dcb->ShortName.Name.Oem,
                         &EaHandle );
        }

        if (!FatIsFat32(Dcb->Vcb)) {

            ShortDirent->ExtendedAttributes = EaHandle;
        }

        //
        //  After this point we cannot just simply mark the dirent deleted,
        //  we have to deal with the directory file object.
        //

        //
        //  Make the dirent into a directory.  Note that even if this call
        //  raises because of disk space, the diectory file object has been
        //  created.
        //

        FatInitializeDirectoryDirent( IrpContext, Dcb, ShortDirent );

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts.  Note that this call cannot fail.
        //

        FatSetFileObject( FileObject,
                          UserDirectoryOpen,
                          Dcb,
                          Ccb = FatCreateCcb( IrpContext ) );

        //
        //  Initialize the LongFileName if it has not already been set, so that
        //  FatNotify below won't have to.  If there are filesystem filters
        //  attached to FAT, the LongFileName could have gotten set if the
        //  filter queried for name information on this file object while
        //  watching the IO needed in FatInitializeDirectoryDirent.
        //

        if (Dcb->FullFileName.Buffer == NULL) {

            FatSetFullNameInFcb( IrpContext, Dcb, UnicodeName );
        }

        //
        //  We call the notify package to report that the
        //  we added a file.
        //

        FatNotifyReportChange( IrpContext,
                               Vcb,
                               Dcb,
                               FILE_NOTIFY_CHANGE_DIR_NAME,
                               FILE_ACTION_ADDED );

        //
        //  Setup our share access
        //

        IoSetShareAccess( *DesiredAccess,
                          ShareAccess,
                          FileObject,
                          &Dcb->ShareAccess );

        //
        //  From this point on, nothing can raise.
        //

        Dcb->UncleanCount += 1;
        Dcb->OpenCount += 1;
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }

        if (DeleteOnClose) {

            SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
        }

        //
        //  And set our return status
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_CREATED;

    } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

        //
        //  We'll catch all exceptions and handle them below.
        //

        Iosb.Status = IrpContext->ExceptionStatus;
    }

    //
    //  If we failed then undo our work.
    //

    if (!NT_SUCCESS( Iosb.Status )) {

        //
        //  We always have to delete the Ccb if we created one.
        //

        if ( Ccb != NULL ) {

            FatDeleteCcb( IrpContext, &Ccb );
        }

#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
        if ( Dcb == NULL) {

            NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
                    RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
                                   DirentByteOffset / sizeof(DIRENT),
                                   DirentsNeeded ) );

            RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
                          DirentByteOffset / sizeof(DIRENT),
                          DirentsNeeded );

            //
            //  Mark the dirents deleted.  The codes is complex because of
            //  dealing with an LFN than crosses a page boundry.
            //

            if (Dirent != NULL) {

                ULONG i;

                //
                //  We failed before creating a directory file object.
                //  We can just mark the dirent deleted and exit.
                //

                for (i = 0; i < DirentsNeeded; i++) {

                    if (DirentFromPool == FALSE) {

                        //
                        //  Simple case.
                        //

                        Dirent[i].FileName[0] = FAT_DIRENT_DELETED;

                    } else {

                        //
                        //  If the second CcPreparePinWrite failed, we have
                        //  to stop early.
                        //

                        if ((SecondPageBcb == NULL) &&
                            (i == DirentsInFirstPage)) {

                            break;
                        }

                        //
                        //  Now conditionally update either page.
                        //

                        if (i < DirentsInFirstPage) {

                            FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED;

                        } else {

                            SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED;
                        }
                    }
                }
            }
        }
    }

    //
    //  Just drop the Bcbs we have in the parent right now so if we
    //  failed to create the directory and we take the path to rip apart
    //  the partially created child, when we sync-uninit we won't cause
    //  a lazy writer processing the parent to block on us. This would
    //  consume one of the lazy writers, one of which must be running free
    //  in order for us to come back from the sync-uninit.
    //
    //  Neat, huh?
    //
    //  Granted, the delete dirent below will be marginally less efficient
    //  since the Bcb may be reclaimed by the time it executes. Life is
    //  tough.
    //

    FatUnpinBcb( IrpContext, DirentBcb );
    FatUnpinBcb( IrpContext, SecondPageBcb );

    if (DirentFromPool) {

        ExFreePool( Dirent );
    }

    if (!NT_SUCCESS( Iosb.Status )) {

#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" )
        if (Dcb != NULL) {

            //
            //  We have created the Dcb.  If an error occurred while
            //  creating the Ea's, there will be no directory file
            //  object.
            //

            PFILE_OBJECT DirectoryFileObject;

            DirectoryFileObject = Dcb->Specific.Dcb.DirectoryFile;

            //
            //  Knock down all of the repinned data so we can begin to destroy
            //  this failed child.  We don't care about any raising here - we're
            //  already got a fire going.
            //
            //  Note that if we failed to do this, the repinned initial pieces
            //  of the child would cause the sync-uninit to block forever.
            //
            //  A previous spin on this fix had us not make the ./.. creation
            //  "reversible" (bad term) and thus avoid having the Bcb still
            //  outstanding.  This wound up causing very bad things to happen
            //  on DMF floppies when we tried to do a similar yank-down in the
            //  create path - we want the purge it does to make sure we never
            //  try to write the bytes out ... it is just a lot cleaner to
            //  unpinrepin.  I'll leave the reversible logic in place if it ever
            //  proves useful.
            //

            //
            //  There is a possibility that this may be a generally good idea
            //  for "live" finally clauses - set in ExceptionFilter, clear in
            //  ProcessException. Think about this.
            //

            SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE );
            FatUnpinRepinnedBcbs( IrpContext );
            ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE );

            if (DirectoryFileObject != NULL) {

                FatSyncUninitializeCacheMap( IrpContext,
                                             DirectoryFileObject );
            }

            if (Dcb->FirstClusterOfFile != 0) {

                try {

                    //
                    //  Now zap the allocation backing it.  Do it after removing the cachemap so that
                    //  pending writes don't get perplexed looking at free FAT entries.
                    //

                    FatTruncateFileAllocation( IrpContext, Dcb, 0 );

                } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

                    //
                    //  We catch all exceptions that Fat catches, but don't do
                    //  anything with them.
                    //
                }
            }

            try {

                //
                //  Remove the directory entry we made in the parent Dcb.
                //

                FatDeleteDirent( IrpContext, Dcb, NULL, TRUE );

                //
                //  FatDeleteDirent can pin and dirty BCBs, so lets unrepin again.
                //

                FatUnpinRepinnedBcbs( IrpContext );

            } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {

                //
                //  We catch all exceptions that Fat catches, but don't do
                //  anything with them.
                //
            }

            //
            //  Finaly, dereference the directory file object. This will
            //  cause a close Irp to be processed, blowing away the Fcb.
            //

            if (DirectoryFileObject != NULL) {

                //
                //  Dereference the file object for this DCB.  The DCB will
                //  go away when this file object is closed.
                //

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

            } else {

                //
                //  This was also a PDK fix.  If the stream file exists, this would
                //  be done during the dereference file object operation.  Otherwise
                //  we have to remove the Dcb and check if we should remove the parent.
                //  For now we will just leave the parent lying around.
                //
                
#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but FileObject can be NULL depending on where we raise" )                
                if (ARGUMENT_PRESENT( FileObject )) {
                    FileObject->SectionObjectPointer = NULL;
                }
                FatDeleteFcb( IrpContext, &Dcb );
            }
        }

        DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status);

        FatRaiseStatus( IrpContext, Iosb.Status );
    }

    UNREFERENCED_PARAMETER( EaBuffer );
    UNREFERENCED_PARAMETER( EaLength );

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatCreateNewFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PVCB Vcb,
    _Inout_ PDCB ParentDcb,
    _In_ POEM_STRING OemName,
    _In_ PUNICODE_STRING UnicodeName,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ USHORT ShareAccess,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ PUNICODE_STRING LfnBuffer,
    _In_ BOOLEAN IsPagingFile,
    _In_ BOOLEAN NoEaKnowledge,
    _In_ BOOLEAN DeleteOnClose,
    _In_ BOOLEAN OpenRequiringOplock,
    _In_ BOOLEAN TemporaryFile
    )

/*++

Routine Description:

    This routine creates a new file.  The file has already been verified
    not to exist yet.

Arguments:

    FileObject - Supplies the file object for the newly created file

    Vcb - Supplies the Vcb denote the volume to contain the new file

    ParentDcb - Supplies the parent directory containg the newly created
        File

    OemName - Supplies the Oem name for the newly created file.  It may
        or maynot be 8.3 complient, but will be upcased.

    UnicodeName - Supplies the Unicode name for the newly created file.
        It may or maynot be 8.3 complient.  This name contains the original
        case information.

    DesiredAccess - Supplies the desired access of the caller

    ShareAccess - Supplies the shared access of the caller

    AllocationSize - Supplies the initial allocation size for the file

    EaBuffer - Supplies the Ea set for the newly created file

    EaLength - Supplies the length, in bytes, of EaBuffer

    FileAttributes - Supplies the file attributes for the newly created
        file

    LfnBuffer - A MAX_LFN sized buffer for directory searching

    IsPagingFile - Indicates if this is the paging file being created

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

    DeleteOnClose - The caller wants the file gone when the handle is closed

    OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option.

    TemporaryFile - Signals the lazywriter to not write dirty data unless
        absolutely has to.


Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    PFCB Fcb = NULL;

    PDIRENT Dirent = NULL;
    PBCB DirentBcb = NULL;
    ULONG DirentsNeeded;
    ULONG DirentByteOffset;

    PDIRENT ShortDirent;
    ULONG ShortDirentByteOffset;

    USHORT EaHandle;

    BOOLEAN AllLowerComponent;
    BOOLEAN AllLowerExtension;
    BOOLEAN CreateLfn;

    ULONG BytesInFirstPage = 0;
    ULONG DirentsInFirstPage = 0;
    PDIRENT FirstPageDirent = NULL;

    PBCB SecondPageBcb = NULL;
    ULONG SecondPageOffset;
    PDIRENT SecondPageDirent = NULL;

    BOOLEAN DirentFromPool = FALSE;

    OEM_STRING ShortName;
    UCHAR ShortNameBuffer[12];

    UNICODE_STRING UniTunneledShortName;
    WCHAR UniTunneledShortNameBuffer[12];
    UNICODE_STRING UniTunneledLongName;
    WCHAR UniTunneledLongNameBuffer[26];
    LARGE_INTEGER TunneledCreationTime;
    ULONG TunneledDataSize;
    BOOLEAN HaveTunneledInformation;
    BOOLEAN UsingTunneledLfn = FALSE;

    PUNICODE_STRING RealUnicodeName;

    //
    //  The following variables are for abnormal termination
    //

    PDIRENT UnwindDirent = NULL;
    PFCB UnwindFcb = NULL;
    BOOLEAN UnwindAllocation = FALSE;
    PCCB UnwindCcb = NULL;

    ULONG LocalAbnormalTermination = FALSE;

#if (NTDDI_VERSION < NTDDI_WIN7)
    UNREFERENCED_PARAMETER( OpenRequiringOplock );
#endif

    PAGED_CODE();

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

    ShortName.Length = 0;
    ShortName.MaximumLength = sizeof(ShortNameBuffer);
    ShortName.Buffer = (PCHAR)&ShortNameBuffer[0];

    UniTunneledShortName.Length = 0;
    UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer);
    UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];

    UniTunneledLongName.Length = 0;
    UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer);
    UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];

    EaHandle = 0;

    //
    //  We fail this operation if the caller doesn't understand Ea's.
    //

    if (NoEaKnowledge
        && EaLength > 0) {

        Iosb.Status = STATUS_ACCESS_DENIED;

        DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status);
        return Iosb;
    }

    //
    //  DeleteOnClose and ReadOnly are not compatible.
    //

    if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) {

        Iosb.Status = STATUS_CANNOT_DELETE;
        return Iosb;
    }

    //
    //  Look in the tunnel cache for names and timestamps to restore
    //

    TunneledDataSize = sizeof(LARGE_INTEGER);
    HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
                                                      FatDirectoryKey(ParentDcb),
                                                      UnicodeName,
                                                      &UniTunneledShortName,
                                                      &UniTunneledLongName,
                                                      &TunneledDataSize,
                                                      &TunneledCreationTime );
    NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));

    //
    //  Now get the names that we will be using.
    //

    FatSelectNames( IrpContext,
                    ParentDcb,
                    OemName,
                    UnicodeName,
                    &ShortName,
                    (HaveTunneledInformation? &UniTunneledShortName : NULL),
                    &AllLowerComponent,
                    &AllLowerExtension,
                    &CreateLfn );

    //
    //  If we are not in Chicago mode, ignore the magic bits.
    //

    RealUnicodeName = UnicodeName;

    if (!FatData.ChicagoMode) {

        AllLowerComponent = FALSE;
        AllLowerExtension = FALSE;
        CreateLfn = FALSE;

    } else {

        //
        //  If the Unicode name was legal for a short name and we got
        //  a tunneling hit which had a long name associated which is
        //  avaliable for use, use it.
        //

        if (!CreateLfn &&
            UniTunneledLongName.Length &&
            !FatLfnDirentExists(IrpContext, ParentDcb, &UniTunneledLongName, LfnBuffer)) {

            UsingTunneledLfn = TRUE;
            CreateLfn = TRUE;

            RealUnicodeName = &UniTunneledLongName;

            //
            //  Short names are always upcase if an LFN exists
            //

            AllLowerComponent = FALSE;
            AllLowerExtension = FALSE;
        }
    }

    //
    //  Create/allocate a new dirent
    //

    DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(RealUnicodeName) + 1 : 1;

    DirentByteOffset = FatCreateNewDirent( IrpContext,
                                           ParentDcb,
                                           DirentsNeeded );

    try {

        FatPrepareWriteDirectoryFile( IrpContext,
                                      ParentDcb,
                                      DirentByteOffset,
                                      sizeof(DIRENT),
                                      &DirentBcb,
                                      &Dirent,
                                      FALSE,
                                      TRUE,
                                      &Iosb.Status );

        NT_ASSERT( NT_SUCCESS( Iosb.Status ) );

        UnwindDirent = Dirent;

        //
        //  Deal with the special case of an LFN + Dirent structure crossing
        //  a page boundry.
        //

        if ((DirentByteOffset / PAGE_SIZE) !=
            ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {

            SecondPageBcb;
            SecondPageOffset;
            SecondPageDirent;

            SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;

            BytesInFirstPage = SecondPageOffset - DirentByteOffset;

            DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);

            FatPrepareWriteDirectoryFile( IrpContext,
                                          ParentDcb,
                                          SecondPageOffset,
                                          sizeof(DIRENT),
                                          &SecondPageBcb,
                                          &SecondPageDirent,
                                          FALSE,
                                          TRUE,
                                          &Iosb.Status );

            NT_ASSERT( NT_SUCCESS( Iosb.Status ) );

            FirstPageDirent = Dirent;

            Dirent = FsRtlAllocatePoolWithTag( PagedPool,
                                               DirentsNeeded * sizeof(DIRENT),
                                               TAG_DIRENT );

            DirentFromPool = TRUE;
        }

        //
        //  Bump up Dirent and DirentByteOffset
        //

        ShortDirent = Dirent + DirentsNeeded - 1;
        ShortDirentByteOffset = DirentByteOffset +
                                (DirentsNeeded - 1) * sizeof(DIRENT);

        NT_ASSERT( NT_SUCCESS( Iosb.Status ));


        //
        //  Fill in the fields of the dirent.
        //

        FatConstructDirent( IrpContext,
                            ShortDirent,
                            &ShortName,
                            AllLowerComponent,
                            AllLowerExtension,
                            CreateLfn ? RealUnicodeName : NULL,
                            (UCHAR)(FileAttributes | FILE_ATTRIBUTE_ARCHIVE),
                            TRUE,
                            (HaveTunneledInformation ? &TunneledCreationTime : NULL) );

        //
        //  If the dirent crossed pages, we have to do some real gross stuff.
        //

        if (DirentFromPool) {

            RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage );

            RtlCopyMemory( SecondPageDirent,
                           Dirent + DirentsInFirstPage,
                           DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage );

            ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1;
        }

        //
        //  Create a new Fcb for the file.  Once the Fcb is created we
        //  will not need to unwind dirent because delete dirent will
        //  now do the work.
        //

        Fcb = UnwindFcb = FatCreateFcb( IrpContext,
                                        Vcb,
                                        ParentDcb,
                                        DirentByteOffset,
                                        ShortDirentByteOffset,
                                        ShortDirent,
                                        CreateLfn ? RealUnicodeName : NULL,
                                        IsPagingFile,
                                        FALSE );
        UnwindDirent = NULL;

#if (NTDDI_VERSION >= NTDDI_WIN7)
        //
        //  The next three FsRtl calls are for oplock work.  We deliberately
        //  do these here so that if either call fails we will be able to 
        //  clean up without adding a bunch of code to unwind counts, fix
        //  the file object, etc.
        //

        //
        //  Let's make sure that if the caller provided an oplock key that it
        //  gets stored in the file object.
        //

        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY,
                                          NULL,
                                          NULL,
                                          NULL );

        //
        //  If the caller wants atomic create-with-oplock semantics, tell
        //  the oplock package.  We haven't incremented the Fcb's UncleanCount
        //  for this create yet, so add that in on the call.
        //

        if (OpenRequiringOplock &&
            (Iosb.Status == STATUS_SUCCESS)) {

            Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb),
                                             IrpContext->OriginatingIrp,
                                             (Fcb->UncleanCount + 1) );
        }
#endif

#if (NTDDI_VERSION >= NTDDI_WIN8)
        //
        //  Break parent directory oplock.  Directory oplock breaks are always
        //  advisory, so we will never block/get STATUS_PENDING here.  On the
        //  off chance this fails with INSUFFICIENT_RESOURCES we do it here
        //  where we can still tolerate a failure.
        //
        
        if (Iosb.Status == STATUS_SUCCESS) {

            Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb),
                                              IrpContext->OriginatingIrp,
                                              OPLOCK_FLAG_PARENT_OBJECT,
                                              NULL,
                                              NULL,
                                              NULL );
        }

        //
        //  Get out if any of the oplock calls failed.  We raise to provoke
        //  abnormal termination and ensure that the newly-created Fcb gets
        //  deleted.
        //

        if (Iosb.Status != STATUS_SUCCESS) {

            FatRaiseStatus( IrpContext, Iosb.Status );
        }
#endif

        //
        //  If this is a temporary file, note it in the FcbState
        //

        if (TemporaryFile) {

            SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
        }

        //
        //  Add some initial file allocation
        //

        FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize );
        UnwindAllocation = TRUE;

        Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;

        //
        //  Tentatively add the new Ea's
        //

        if ( EaLength > 0 ) {

            FatCreateEa( IrpContext,
                         Fcb->Vcb,
                         (PUCHAR) EaBuffer,
                         EaLength,
                         &Fcb->ShortName.Name.Oem,
                         &EaHandle );
        }

        if (!FatIsFat32(Fcb->Vcb)) {

            ShortDirent->ExtendedAttributes = EaHandle;
        }

        //
        //  Initialize the LongFileName right now so that FatNotify
        //  below won't have to.
        //

        FatSetFullNameInFcb( IrpContext, Fcb, RealUnicodeName );

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserFileOpen,
                          Fcb,
                          UnwindCcb = FatCreateCcb( IrpContext ));

        FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;

        //
        //  We call the notify package to report that the
        //  we added a file.
        //

        FatNotifyReportChange( IrpContext,
                               Vcb,
                               Fcb,
                               FILE_NOTIFY_CHANGE_FILE_NAME,
                               FILE_ACTION_ADDED );

        //
        //  Setup our share access
        //

        IoSetShareAccess( *DesiredAccess,
                          ShareAccess,
                          FileObject,
                          &Fcb->ShareAccess );

        Fcb->UncleanCount += 1;
        Fcb->OpenCount += 1;
        if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) {
            Fcb->NonCachedUncleanCount += 1;
        }
        Vcb->OpenFileCount += 1;
        if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; }

        //
        //  And set our return status
        //

        Iosb.Status = STATUS_SUCCESS;
        Iosb.Information = FILE_CREATED;

        if ( NT_SUCCESS(Iosb.Status) ) {

            PIO_STACK_LOCATION IrpSp;

            //
            //  Mark the DeleteOnClose bit if the operation was successful.
            //

            if ( DeleteOnClose ) {

                SetFlag( UnwindCcb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
            }

            //
            //  Mark the OpenedByShortName bit if the operation was successful.
            //  If we created an Lfn, we have some sort of generated short name
            //  and thus don't consider ourselves to have opened it - though we
            //  may have had a case mix Lfn "Foo.bar" and generated "FOO.BAR"
            //
            //  Unless, of course, we wanted to create a short name and hit an
            //  associated Lfn in the tunnel cache
            //

            if ( !CreateLfn && !UsingTunneledLfn ) {

                SetFlag( UnwindCcb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME );
            }

            //
            //  Mark the ManageVolumeAccess bit if the privilege is held.
            //

            IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );

            if (FatCheckManageVolumeAccess( IrpContext,
                                            IrpSp->Parameters.Create.SecurityContext->AccessState,
                                            (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
                                                               UserMode :
                                                               IrpContext->OriginatingIrp->RequestorMode ))) {
            
                SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS );
            }

        }

    } finally {

        DebugUnwind( FatCreateNewFile );

        if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {

            //
            //  Tunneling package grew the buffer from pool
            //

            ExFreePool( UniTunneledLongName.Buffer );
        }

        //
        //  If this is an abnormal termination then undo our work.
        //
        //  The extra exception handling here is complex.  We've got
        //  two places here where an exception can be thrown again.
        //

        LocalAbnormalTermination = AbnormalTermination();

        if (LocalAbnormalTermination) {

            if (UnwindFcb == NULL) {

                NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) ||
                        RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
                                       DirentByteOffset / sizeof(DIRENT),
                                       DirentsNeeded ) );

                RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap,
                              DirentByteOffset / sizeof(DIRENT),
                              DirentsNeeded );
            }

            //
            //  Mark the dirents deleted.  The code is complex because of
            //  dealing with an LFN that crosses a page boundary.
            //

            if (UnwindDirent != NULL) {

                ULONG i;

                for (i = 0; i < DirentsNeeded; i++) {

                    if (DirentFromPool == FALSE) {

                        //
                        //  Simple case.
                        //

                        Dirent[i].FileName[0] = FAT_DIRENT_DELETED;

                    } else {

                        //
                        //  If the second CcPreparePinWrite failed, we have
                        //  to stop early.
                        //

                        if ((SecondPageBcb == NULL) &&
                            (i == DirentsInFirstPage)) {

                            break;
                        }

                        //
                        //  Now conditionally update either page.
                        //

                        if (i < DirentsInFirstPage) {

                            FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED;

                        } else {

                            SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED;
                        }
                    }
                }
            }
        }

        //
        //  We must handle exceptions in the following fragments and plow on with the
        //  unwind of this create operation.  This is basically inverted from the
        //  previous state of the code.  Since AbnormalTermination() changes when we
        //  enter a new enclosure, we cached the original state ...
        //

        try {

            if (LocalAbnormalTermination) {
                if (UnwindAllocation) {
                    FatTruncateFileAllocation( IrpContext, Fcb, 0 );
                }
            }

        } finally {

            try {

                if (LocalAbnormalTermination) {
                    if (UnwindFcb != NULL) {
                        FatDeleteDirent( IrpContext, UnwindFcb, NULL, TRUE );
                    }
                }

            } finally {

                if (LocalAbnormalTermination) {
                    if (UnwindFcb != NULL) { 
                        if (ARGUMENT_PRESENT( FileObject )) {
                            FileObject->SectionObjectPointer = NULL;
                        }
                        FatDeleteFcb( IrpContext, &UnwindFcb ); 
                    }
                    if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
                }

                //
                //  This is the normal cleanup code.
                //

                FatUnpinBcb( IrpContext, DirentBcb );
                FatUnpinBcb( IrpContext, SecondPageBcb );

                if (DirentFromPool) {

                    ExFreePool( Dirent );
                }

            }
        }

        DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


//
//  Internal support routine
//

_Requires_lock_held_(_Global_critical_region_)
IO_STATUS_BLOCK
FatSupersedeOrOverwriteFile (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFILE_OBJECT FileObject,
    _Inout_ PFCB Fcb,
    _In_ ULONG AllocationSize,
    _In_ PFILE_FULL_EA_INFORMATION EaBuffer,
    _In_ ULONG EaLength,
    _In_ UCHAR FileAttributes,
    _In_ ULONG CreateDisposition,
    _In_ BOOLEAN NoEaKnowledge
    )

/*++

Routine Description:

    This routine performs a file supersede or overwrite operation.

Arguments:

    FileObject - Supplies a pointer to the file object

    Fcb - Supplies a pointer to the Fcb

    AllocationSize - Supplies an initial allocation size

    EaBuffer - Supplies the Ea set for the superseded/overwritten file

    EaLength - Supplies the length, in bytes, of EaBuffer

    FileAttributes - Supplies the supersede/overwrite file attributes

    CreateDisposition - Supplies the create disposition for the file
        It must be either supersede, overwrite, or overwrite if.

    NoEaKnowledge - This opener doesn't understand Ea's and we fail this
        open if the file has NeedEa's.

Return Value:

    IO_STATUS_BLOCK - Returns the completion status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb = {0};

    PDIRENT Dirent;
    PBCB DirentBcb;

    USHORT EaHandle = 0;
    BOOLEAN EaChange = FALSE;
    BOOLEAN ReleasePaging = FALSE;

    PCCB Ccb;

    ULONG NotifyFilter;

    //
    //  The following variables are for abnormal termination
    //

    PCCB UnwindCcb = NULL;
    USHORT UnwindEa = 0;

    PAGED_CODE();

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

    DirentBcb = NULL;

    //
    //  We fail this operation if the caller doesn't understand Ea's.
    //

    if (NoEaKnowledge
        && EaLength > 0) {

        Iosb.Status = STATUS_ACCESS_DENIED;

        DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status);
        return Iosb;
    }

    try {

        //
        //  Before we actually truncate, check to see if the purge
        //  is going to fail.
        //

        if (!MmCanFileBeTruncated( &Fcb->NonPaged->SectionObjectPointers,
                                   &FatLargeZero )) {

            try_return( Iosb.Status = STATUS_USER_MAPPED_FILE );
        }

        //
        //  Setup the context and section object pointers, and update
        //  our reference counts
        //

        FatSetFileObject( FileObject,
                          UserFileOpen,
                          Fcb,
                          Ccb = UnwindCcb = FatCreateCcb( IrpContext ));

        FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers;

        //
        //  Since this is an supersede/overwrite, purge the section so
        //  that mappers will see zeros.  We set the CREATE_IN_PROGRESS flag
        //  to prevent the Fcb from going away out from underneath us.
        //

        SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);

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

        //
        //  Tentatively add the new Ea's
        //

        if (EaLength > 0) {

            FatCreateEa( IrpContext,
                         Fcb->Vcb,
                         (PUCHAR) EaBuffer,
                         EaLength,
                         &Fcb->ShortName.Name.Oem,
                         &EaHandle );

            UnwindEa = EaHandle;
            EaChange = TRUE;
        }

#if (NTDDI_VERSION >= NTDDI_WIN8)
        //
        //  Break parent directory oplock.  Directory oplock breaks are always
        //  advisory, so we will never block/get STATUS_PENDING here.  On the
        //  off chance this fails with INSUFFICIENT_RESOURCES we do it here
        //  where we can still tolerate a failure.
        //
        
        Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
                                          IrpContext->OriginatingIrp,
                                          OPLOCK_FLAG_PARENT_OBJECT,
                                          NULL,
                                          NULL,
                                          NULL );

        if (Iosb.Status != STATUS_SUCCESS) {

            FatRaiseStatus( IrpContext, Iosb.Status );
        }
#endif

        //
        //  Now set the new allocation size, we do that by first
        //  zeroing out the current file size.  Then we truncate and
        //  allocate up to the new allocation size
        //

        (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
        ReleasePaging = TRUE;

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

        //
        //  Tell the cache manager the size went to zero
        //  This call is unconditional, because MM always wants to know.
        //

        CcSetFileSizes( FileObject,
                        (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );

        FatTruncateFileAllocation( IrpContext, Fcb, AllocationSize );

        ExReleaseResourceLite( Fcb->Header.PagingIoResource );
        ReleasePaging = FALSE;

        FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize );

        Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;

        //
        //  Modify the attributes and time of the file, by first reading
        //  in the dirent for the file and then updating its attributes
        //  and time fields.  Note that for supersede we replace the file
        //  attributes as opposed to adding to them.
        //

        FatGetDirentFromFcbOrDcb( IrpContext,
                                  Fcb,
                                  FALSE,
                                  &Dirent,
                                  &DirentBcb );
        //
        //  We should get the dirent since this Fcb is in good condition, verified as
        //  we crawled down the prefix tree.
        //
        //  Update the appropriate dirent fields, and the fcb fields
        //

        Dirent->FileSize = 0;

        FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;

        if (CreateDisposition == FILE_SUPERSEDE) {

            Dirent->Attributes = FileAttributes;

        } else {

            Dirent->Attributes |= FileAttributes;
        }

        Fcb->DirentFatFlags = Dirent->Attributes;

        KeQuerySystemTime( &Fcb->LastWriteTime );

        (VOID)FatNtTimeToFatTime( IrpContext,
                                  &Fcb->LastWriteTime,
                                  TRUE,
                                  &Dirent->LastWriteTime,
                                  NULL );

        if (FatData.ChicagoMode) {

            Dirent->LastAccessDate = Dirent->LastWriteTime.Date;
        }

        NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE
                       | FILE_NOTIFY_CHANGE_ATTRIBUTES
                       | FILE_NOTIFY_CHANGE_SIZE;

        //
        //  And now delete the previous Ea set if there was one.
        //

        if (!FatIsFat32(Fcb->Vcb) && Dirent->ExtendedAttributes != 0) {

            //
            //  ****    SDK fix, we won't fail this if there is
            //          an error in the Ea's, we'll just leave
            //          the orphaned Ea's in the file.
            //

            EaChange = TRUE;

            try {

                FatDeleteEa( IrpContext,
                             Fcb->Vcb,
                             Dirent->ExtendedAttributes,
                             &Fcb->ShortName.Name.Oem );

            } except( EXCEPTION_EXECUTE_HANDLER ) {

                  FatResetExceptionState( IrpContext );
            }
        }

        //
        //  Update the extended attributes handle in the dirent.
        //

        if (EaChange) {

            NT_ASSERT(!FatIsFat32(Fcb->Vcb));

            Dirent->ExtendedAttributes = EaHandle;

            NotifyFilter |= FILE_NOTIFY_CHANGE_EA;
        }

        //
        //  Now update the dirent to the new ea handle and set the bcb dirty
        //  Once we do this we can no longer back out the Ea
        //

        FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
        UnwindEa = 0;

        //
        //  Indicate that the Eas for this file have changed.
        //

        Ccb->EaModificationCount += Fcb->EaModificationCount;

        //
        //  Check to see if we need to notify outstanding Irps for full
        //  changes only (i.e., we haven't added, deleted, or renamed the file).
        //

        FatNotifyReportChange( IrpContext,
                               Fcb->Vcb,
                               Fcb,
                               NotifyFilter,
                               FILE_ACTION_MODIFIED );

        //
        //  And set our status to success
        //

        Iosb.Status = STATUS_SUCCESS;

        if (CreateDisposition == FILE_SUPERSEDE) {

            Iosb.Information = FILE_SUPERSEDED;

        } else {

            Iosb.Information = FILE_OVERWRITTEN;
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatSupersedeOfOverwriteFile );

        if (ReleasePaging)  {  ExReleaseResourceLite( Fcb->Header.PagingIoResource );  }

        //
        //  If this is an abnormal termination then undo our work.
        //

        if (AbnormalTermination()) {

            if (UnwindEa != 0) { FatDeleteEa( IrpContext, Fcb->Vcb, UnwindEa, &Fcb->ShortName.Name.Oem ); }
            if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); }
        }

        FatUnpinBcb( IrpContext, DirentBcb );

        ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS);

        DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status);
    }

    return Iosb;
}


VOID
FatSetFullNameInFcb (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFCB Fcb,
    _In_ PUNICODE_STRING FinalName
    )

/*++

Routine Description:

    This routine attempts a quick form of the full FatSetFullFileNameInFcb
    operation.

    NOTE: this routine is probably not worth the code duplication involved,
    and is not equipped to handle the cases where the parent doesn't have
    the full name set up.

Arguments:

    Fcb - Supplies a pointer to the Fcb

    FinalName - Supplies the last component of the path to this Fcb's dirent

Return Value:

    None.  May silently fail.

--*/

{
    PAGED_CODE();

    UNREFERENCED_PARAMETER( IrpContext );

    NT_ASSERT( Fcb->FullFileName.Buffer == NULL );

    //
    //  Prefer the ExactCaseLongName of the file for this operation, if set.  In
    //  this way we avoid building the fullname with a short filename.  Several
    //  operations assume this - the FinalNameLength in particular is the Lfn
    //  (if existant) length, and we use this to crack the fullname in paths
    //  such as the FsRtlNotify caller.
    //
    //  If the caller specified a particular name and it is short, it is the
    //  case that the long name was set up.
    //

    if (Fcb->ExactCaseLongName.Buffer) {

        NT_ASSERT( Fcb->ExactCaseLongName.Length != 0 );
        FinalName = &Fcb->ExactCaseLongName;
    }

    //
    //  Special case the root.
    //

    if (NodeType(Fcb->ParentDcb) == FAT_NTC_ROOT_DCB) {

        Fcb->FullFileName.Length =
        Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + FinalName->Length;

        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
                                                             Fcb->FullFileName.Length,
                                                             TAG_FILENAME_BUFFER );

        Fcb->FullFileName.Buffer[0] = L'\\';

        RtlCopyMemory( &Fcb->FullFileName.Buffer[1],
                       &FinalName->Buffer[0],
                       FinalName->Length );

    } else {

        PUNICODE_STRING Prefix;

        Prefix = &Fcb->ParentDcb->FullFileName;

        //
        //  It is possible our parent's full filename is not set.  Simply fail
        //  this attempt.
        //

        if (Prefix->Buffer == NULL) {

            return;
        }

        Fcb->FullFileName.Length =
        Fcb->FullFileName.MaximumLength = Prefix->Length + sizeof(WCHAR) + FinalName->Length;

        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
                                                             Fcb->FullFileName.Length,
                                                             TAG_FILENAME_BUFFER );

        RtlCopyMemory( &Fcb->FullFileName.Buffer[0],
                       &Prefix->Buffer[0],
                       Prefix->Length );

        Fcb->FullFileName.Buffer[Prefix->Length / sizeof(WCHAR)] = L'\\';

        RtlCopyMemory( &Fcb->FullFileName.Buffer[(Prefix->Length / sizeof(WCHAR)) + 1],
                       &FinalName->Buffer[0],
                       FinalName->Length );

    }
}


NTSTATUS
FatCheckSystemSecurityAccess (
    _In_ PIRP_CONTEXT IrpContext
    )
{
    PACCESS_STATE AccessState;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );

    PAGED_CODE();

    //
    //  We check if the caller wants ACCESS_SYSTEM_SECURITY access on this
    //  object and fail the request if he does.
    //

    NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
    AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;

    //
    //  Check if the remaining privilege includes ACCESS_SYSTEM_SECURITY.
    //

    if (FlagOn( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY )) {

        if (!SeSinglePrivilegeCheck( FatSecurityPrivilege,
                                     UserMode )) {

            return STATUS_ACCESS_DENIED;
        }

        //
        //  Move this privilege from the Remaining access to Granted access.
        //

        ClearFlag( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY );
        SetFlag( AccessState->PreviouslyGrantedAccess, ACCESS_SYSTEM_SECURITY );
    }

    return STATUS_SUCCESS;
}


NTSTATUS
FatCheckShareAccess (
    _In_ PIRP_CONTEXT IrpContext,
    _In_ PFILE_OBJECT FileObject,
    _In_ PFCB FcbOrDcb,
    _In_ PACCESS_MASK DesiredAccess,
    _In_ ULONG ShareAccess
    )

/*++

Routine Description:

    This routine checks conditions that may result in a sharing violation.

Arguments:

    FileObject - Pointer to the file object of the current open request.

    FcbOrDcb - Supplies a pointer to the Fcb/Dcb.

    DesiredAccess - Desired access of current open request.

    ShareAccess - Shared access requested by current open request.

Return Value:

    If the accessor has access to the file, STATUS_SUCCESS is returned.
    Otherwise, STATUS_SHARING_VIOLATION is returned.
    
--*/

{
    PAGED_CODE();

#if (NTDDI_VERSION >= NTDDI_VISTA)
    //
    //  Do an extra test for writeable user sections if the user did not allow
    //  write sharing - this is neccessary since a section may exist with no handles
    //  open to the file its based against.
    //

    if ((NodeType( FcbOrDcb ) == FAT_NTC_FCB) &&
        !FlagOn( ShareAccess, FILE_SHARE_WRITE ) &&
        FlagOn( *DesiredAccess, FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE | MAXIMUM_ALLOWED ) &&
        MmDoesFileHaveUserWritableReferences( &FcbOrDcb->NonPaged->SectionObjectPointers )) {

        return STATUS_SHARING_VIOLATION;
    }
#endif

    //
    //  Check if the Fcb has the proper share access.
    //

    return IoCheckShareAccess( *DesiredAccess,
                               ShareAccess,
                               FileObject,
                               &FcbOrDcb->ShareAccess,
                               FALSE );

    UNREFERENCED_PARAMETER( IrpContext );
}


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS