/*++

Copyright (c) 1989-2000 Microsoft Corporation

Module Name:

    FileInfo.c

Abstract:

    This module implements the File Information routines for Fat called by
    the dispatch driver.

// @@BEGIN_DDKSPLIT

Author:

    Gary Kimura     [GaryKi]    22-Oct-1990

Revision History:

// @@END_DDKSPLIT

--*/

#include "FatProcs.h"

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

#define BugCheckFileId                   (FAT_BUG_CHECK_FILEINFO)

//
//  The local debug trace level
//

#define Dbg                              (DEBUG_TRACE_FILEINFO)

VOID
FatQueryBasicInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_BASIC_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryStandardInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_STANDARD_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryInternalInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_INTERNAL_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryEaInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_EA_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryPositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_POSITION_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryNameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PCCB Ccb,
    IN OUT PFILE_NAME_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryShortNameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_NAME_INFORMATION Buffer,
    IN OUT PLONG Length
    );

VOID
FatQueryNetworkInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
    IN OUT PLONG Length
    );

NTSTATUS
FatSetBasicInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFCB Fcb,
    IN PCCB Ccb
    );

NTSTATUS
FatSetDispositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject,
    IN PFCB Fcb
    );

NTSTATUS
FatSetRenameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PVCB Vcb,
    IN PFCB Fcb,
    IN PCCB Ccb
    );

NTSTATUS
FatSetPositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject
    );

NTSTATUS
FatSetAllocationInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject
    );

NTSTATUS
FatSetEndOfFileInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject,
    IN PVCB Vcb,
    IN PFCB Fcb
    );

VOID
FatDeleteFile (
    IN PIRP_CONTEXT IrpContext,
    IN PDCB TargetDcb,
    IN ULONG LfnOffset,
    IN ULONG DirentOffset,
    IN PDIRENT Dirent,
    IN PUNICODE_STRING Lfn
    );

VOID
FatRenameEAs (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN USHORT ExtendedAttributes,
    IN POEM_STRING OldOemName
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatCommonQueryInformation)
#pragma alloc_text(PAGE, FatCommonSetInformation)
#pragma alloc_text(PAGE, FatFsdQueryInformation)
#pragma alloc_text(PAGE, FatFsdSetInformation)
#pragma alloc_text(PAGE, FatQueryBasicInfo)
#pragma alloc_text(PAGE, FatQueryEaInfo)
#pragma alloc_text(PAGE, FatQueryInternalInfo)
#pragma alloc_text(PAGE, FatQueryNameInfo)
#pragma alloc_text(PAGE, FatQueryNetworkInfo)
#pragma alloc_text(PAGE, FatQueryShortNameInfo)
#pragma alloc_text(PAGE, FatQueryPositionInfo)
#pragma alloc_text(PAGE, FatQueryStandardInfo)
#pragma alloc_text(PAGE, FatSetAllocationInfo)
#pragma alloc_text(PAGE, FatSetBasicInfo)
#pragma alloc_text(PAGE, FatSetDispositionInfo)
#pragma alloc_text(PAGE, FatSetEndOfFileInfo)
#pragma alloc_text(PAGE, FatSetPositionInfo)
#pragma alloc_text(PAGE, FatSetRenameInfo)
#pragma alloc_text(PAGE, FatDeleteFile)
#pragma alloc_text(PAGE, FatRenameEAs)
#endif


NTSTATUS
FatFsdQueryInformation (
    IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the Fsd part of the NtQueryInformationFile API
    call.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the file
        being queried exists.

    Irp - Supplies the Irp being processed.

Return Value:

    NTSTATUS - The FSD status for the Irp.

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

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

    //
    //  Call the common query routine, with blocking allowed if synchronous
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );

        Status = FatCommonQueryInformation( 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, "FatFsdQueryInformation -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


NTSTATUS
FatFsdSetInformation (
    IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtSetInformationFile API
    call.

Arguments:

    VolumeDeviceObject - Supplies the volume device object where the file
        being set exists.

    Irp - Supplies the Irp being processed.

Return Value:

    NTSTATUS - The FSD status for the Irp.

--*/

{
    NTSTATUS Status;
    PIRP_CONTEXT IrpContext = NULL;

    BOOLEAN TopLevel;

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

    //
    //  Call the common set routine, with blocking allowed if synchronous
    //

    FsRtlEnterFileSystem();

    TopLevel = FatIsIrpTopLevel( Irp );

    try {

        IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );

        Status = FatCommonSetInformation( 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, "FatFsdSetInformation -> %08lx\n", Status);

    UNREFERENCED_PARAMETER( VolumeDeviceObject );

    return Status;
}


NTSTATUS
FatCommonQueryInformation (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for querying file information called by both
    the fsd and fsp threads.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;

    PIO_STACK_LOCATION IrpSp;

    PFILE_OBJECT FileObject;

    LONG Length;
    FILE_INFORMATION_CLASS FileInformationClass;
    PVOID Buffer;

    TYPE_OF_OPEN TypeOfOpen;
    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    BOOLEAN FcbAcquired;

    PFILE_ALL_INFORMATION AllInfo;

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    FileObject = IrpSp->FileObject;

    DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0);
    DebugTrace( 0, Dbg, "Irp                    = %08lx\n", Irp);
    DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.QueryFile.Length);
    DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass);
    DebugTrace( 0, Dbg, "->Buffer               = %08lx\n", Irp->AssociatedIrp.SystemBuffer);

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

    Length = (LONG)IrpSp->Parameters.QueryFile.Length;
    FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass;
    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  Decode the file object
    //

    TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );

    FcbAcquired = FALSE;
    Status = STATUS_SUCCESS;

    try {

        //
        //  Case on the type of open we're dealing with
        //

        switch (TypeOfOpen) {

        case UserVolumeOpen:

            //
            //  We cannot query the user volume open.
            //

            Status = STATUS_INVALID_PARAMETER;
            break;

        case UserFileOpen:

        case UserDirectoryOpen:

        case DirectoryFile:

            //
            //  Acquire shared access to the fcb, except for a paging file
            //  in order to avoid deadlocks with Mm.
            //

            if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {

                if (!FatAcquireSharedFcb( IrpContext, Fcb )) {

                    DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
                    
                    Status = FatFsdPostRequest( IrpContext, Irp );
                    IrpContext = NULL;
                    Irp = NULL;

                    try_return( Status );
                }

                FcbAcquired = TRUE;
            }

            //
            //  Make sure the Fcb is in a usable condition.  This
            //  will raise an error condition if the fcb is unusable
            //

            FatVerifyFcb( IrpContext, Fcb );

            //
            //  Based on the information class we'll do different
            //  actions.  Each of hte procedures that we're calling fills
            //  up the output buffer, if possible.  They will raise the
            //  status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
            //  This is considered a somewhat unusual case and is handled
            //  more cleanly with the exception mechanism rather than
            //  testing a return status value for each call.
            //

            switch (FileInformationClass) {

            case FileAllInformation:

                //
                //  For the all information class we'll typecast a local
                //  pointer to the output buffer and then call the
                //  individual routines to fill in the buffer.
                //

                AllInfo = Buffer;
                Length -= (sizeof(FILE_ACCESS_INFORMATION)
                           + sizeof(FILE_MODE_INFORMATION)
                           + sizeof(FILE_ALIGNMENT_INFORMATION));

                FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length );
                FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length );
                FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length );
                FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length );
                FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length );
                FatQueryNameInfo( IrpContext, Fcb, Ccb, &AllInfo->NameInformation, &Length );

                break;

            case FileBasicInformation:

                FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
                break;

            case FileStandardInformation:

                FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length );
                break;

            case FileInternalInformation:

                FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length );
                break;

            case FileEaInformation:

                FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length );
                break;

            case FilePositionInformation:

                FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length );
                break;

            case FileNameInformation:

                FatQueryNameInfo( IrpContext, Fcb, Ccb, Buffer, &Length );
                break;

            case FileAlternateNameInformation:

                FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length );
                break;

            case FileNetworkOpenInformation:

                FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
                break;

            default:

                Status = STATUS_INVALID_PARAMETER;
                break;
            }

            break;
 
        default:

            KdPrintEx((DPFLTR_FASTFAT_ID,
                       DPFLTR_INFO_LEVEL,
                       "FATQueryFile, Illegal TypeOfOpen = %08lx\n",
                       TypeOfOpen));

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        //
        //  If we overflowed the buffer, set the length to 0 and change the
        //  status to STATUS_BUFFER_OVERFLOW.
        //

        if ( Length < 0 ) {

            Status = STATUS_BUFFER_OVERFLOW;

            Length = 0;
        }

        //
        //  Set the information field to the number of bytes actually filled in
        //  and then complete the request
        //

        Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCommonQueryInformation );

        if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }

        if (!AbnormalTermination()) {

            FatCompleteRequest( IrpContext, Irp, Status );
        }

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

    return Status;
}


NTSTATUS
FatCommonSetInformation (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for setting file information called by both
    the fsd and fsp threads.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The return status for the operation

--*/

{
    NTSTATUS Status;

    PIO_STACK_LOCATION IrpSp;

    PFILE_OBJECT FileObject;
    FILE_INFORMATION_CLASS FileInformationClass;

    TYPE_OF_OPEN TypeOfOpen;
    PVCB Vcb;
    PFCB Fcb;
    PCCB Ccb;

    BOOLEAN VcbAcquired = FALSE;
    BOOLEAN FcbAcquired = FALSE;

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0);
    DebugTrace( 0, Dbg, "Irp                    = %08lx\n", Irp);
    DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.SetFile.Length);
    DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass);
    DebugTrace( 0, Dbg, "->FileObject           = %08lx\n", IrpSp->Parameters.SetFile.FileObject);
    DebugTrace( 0, Dbg, "->ReplaceIfExists      = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
    DebugTrace( 0, Dbg, "->Buffer               = %08lx\n", Irp->AssociatedIrp.SystemBuffer);

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

    FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
    FileObject = IrpSp->FileObject;

    //
    //  Decode the file object
    //

    TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );

    try {

        //
        //  Case on the type of open we're dealing with
        //

        switch (TypeOfOpen) {

        case UserVolumeOpen:

            //
            //  We cannot query the user volume open.
            //

            try_return( Status = STATUS_INVALID_PARAMETER );
            break;

        case UserFileOpen:

            if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
                ((FileInformationClass == FileEndOfFileInformation) ||
                 (FileInformationClass == FileAllocationInformation))) {

                //
                //  We check whether we can proceed
                //  based on the state of the file oplocks.
                //

                Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock,
                                           Irp,
                                           IrpContext,
                                           NULL,
                                           NULL );

                if (Status != STATUS_SUCCESS) {

                    try_return( Status );
                }

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

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

        case UserDirectoryOpen:

            break;

        default:

            try_return( Status = STATUS_INVALID_PARAMETER );
        }

        //
        //  We can only do a set on a nonroot dcb, so we do the test
        //  and then fall through to the user file open code.
        //

        if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {

            if (FileInformationClass == FileDispositionInformation) {

                try_return( Status = STATUS_CANNOT_DELETE );
            }

            try_return( Status = STATUS_INVALID_PARAMETER );
        }

        //
        //  In the following two cases, we cannot have creates occuring
        //  while we are here, so acquire the volume exclusive.
        //

        if ((FileInformationClass == FileDispositionInformation) ||
            (FileInformationClass == FileRenameInformation)) {

            if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {

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

                Status = FatFsdPostRequest( IrpContext, Irp );
                Irp = NULL;
                IrpContext = NULL;

                try_return( Status );
            }

            VcbAcquired = TRUE;
        }

        //
        //  We need to look here to check whether the oplock state
        //  will allow us to continue.  We may have to loop to prevent
        //  an oplock being granted between the time we check the oplock
        //  and obtain the Fcb.
        //

        //
        //  Acquire exclusive access to the Fcb,  We use exclusive
        //  because it is probable that one of the subroutines
        //  that we call will need to monkey with file allocation,
        //  create/delete extra fcbs.  So we're willing to pay the
        //  cost of exclusive Fcb access.
        //
        //  Note that we do not acquire the resource for paging file
        //  operations in order to avoid deadlock with Mm.
        //

        if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {

            if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {

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

                Status = FatFsdPostRequest( IrpContext, Irp );
                Irp = NULL;
                IrpContext = NULL;

                try_return( Status );
            }

            FcbAcquired = TRUE;
        }

        Status = STATUS_SUCCESS;

        //
        //  Make sure the Fcb is in a usable condition.  This
        //  will raise an error condition if the fcb is unusable
        //

        FatVerifyFcb( IrpContext, Fcb );

        //
        //  Based on the information class we'll do different
        //  actions.  Each of the procedures that we're calling will either
        //  complete the request of send the request off to the fsp
        //  to do the work.
        //

        switch (FileInformationClass) {

        case FileBasicInformation:

            Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb );
            break;

        case FileDispositionInformation:

            //
            //  If this is on deferred flush media, we have to be able to wait.
            //

            if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
                 !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {

                Status = FatFsdPostRequest( IrpContext, Irp );
                Irp = NULL;
                IrpContext = NULL;

            } else {

                Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb );
            }

            break;

        case FileRenameInformation:

            //
            //  We proceed with this operation only if we can wait
            //

            if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {

                Status = FatFsdPostRequest( IrpContext, Irp );
                Irp = NULL;
                IrpContext = NULL;

            } else {

                Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb );

                //
                //  If STATUS_PENDING is returned it means the oplock
                //  package has the Irp.  Don't complete the request here.
                //

                if (Status == STATUS_PENDING) {
                    Irp = NULL;
                    IrpContext = NULL;
                }
            }

            break;

        case FilePositionInformation:

            Status = FatSetPositionInfo( IrpContext, Irp, FileObject );
            break;

        case FileLinkInformation:

            Status = STATUS_INVALID_DEVICE_REQUEST;
            break;

        case FileAllocationInformation:

            Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject );
            break;

        case FileEndOfFileInformation:

            Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb );
            break;

        default:

            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        if ( IrpContext != NULL ) {

            FatUnpinRepinnedBcbs( IrpContext );
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatCommonSetInformation );

        if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
        if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }

        if (!AbnormalTermination()) {

            FatCompleteRequest( IrpContext, Irp, Status );
        }

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

    return Status;
}


//
//  Internal Support Routine
//

VOID
FatQueryBasicInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_BASIC_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++
 Description:

    This routine performs the query basic information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    FileObject - Supplies the flag bit that indicates the file was modified.

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

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

    //
    //  Zero out the output buffer, and set it to indicate that
    //  the query is a normal file.  Later we might overwrite the
    //  attribute.
    //

    RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );

    //
    //  Extract the data and fill in the non zero fields of the output
    //  buffer
    //

    if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {

        //
        //  We have to munge a lie on the fly.  Every time we have to
        //  use 1/1/80 we need to convert to GMT since the TZ may have
        //  changed on us.
        //

        ExLocalTimeToSystemTime( &FatJanOne1980,
                                 &Buffer->LastWriteTime );
        Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;

    } else {

        Buffer->LastWriteTime = Fcb->LastWriteTime;
        Buffer->CreationTime = Fcb->CreationTime;
        Buffer->LastAccessTime = Fcb->LastAccessTime;
    }

    Buffer->FileAttributes = Fcb->DirentFatFlags;

    //
    //  If the temporary flag is set, then set it in the buffer.
    //

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

        SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
    }

    //
    //  If no attributes were set, set the normal bit.
    //

    if (Buffer->FileAttributes == 0) {

        Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
    }

    //
    //  Update the length and status output variables
    //

    *Length -= sizeof( FILE_BASIC_INFORMATION );

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0);

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryStandardInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_STANDARD_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine performs the query standard information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

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

    //
    //  Zero out the output buffer, and fill in the number of links
    //  and the delete pending flag.
    //

    RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );

    Buffer->NumberOfLinks = 1;
    Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );

    //
    //  Case on whether this is a file or a directory, and extract
    //  the information and fill in the fcb/dcb specific parts
    //  of the output buffer
    //

    if (NodeType(Fcb) == FAT_NTC_FCB) {

        if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {

            FatLookupFileAllocationSize( IrpContext, Fcb );
        }

        Buffer->AllocationSize = Fcb->Header.AllocationSize;
        Buffer->EndOfFile = Fcb->Header.FileSize;

        Buffer->Directory = FALSE;

    } else {

        Buffer->Directory = TRUE;
    }

    //
    //  Update the length and status output variables
    //

    *Length -= sizeof( FILE_STANDARD_INFORMATION );

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0);

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryInternalInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_INTERNAL_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine performs the query internal information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

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

    try {

        Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );

        //
        //  Update the length and status output variables
        //

        *Length -= sizeof( FILE_INTERNAL_INFORMATION );

    } finally {

        DebugUnwind( FatQueryInternalInfo );

        DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

        DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
    }

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryEaInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_EA_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine performs the query Ea information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

{
    PBCB Bcb;

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

    Bcb = NULL;

    try {

        //
        //  Zero out the output buffer
        //

        RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );

        //
        //  The Root dcb does not have any EAs so don't look for any.  Fat32
        //  doesn't have any, either.
        //

        if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB &&
             !FatIsFat32( Fcb->Vcb )) {

            PDIRENT Dirent;

            //
            //  Try to get the dirent for this file.
            //

            FatGetDirentFromFcbOrDcb( IrpContext,
                                      Fcb,
                                      &Dirent,
                                      &Bcb );
            
            if (Dirent != NULL) {
                
                //
                //  Get a the size needed to store the full eas for the file.
                //

                FatGetEaLength( IrpContext,
                                Fcb->Vcb,
                                Dirent,
                                &Buffer->EaSize );
            }
        }

        //
        //  Update the length and status output variables
        //

        *Length -= sizeof( FILE_EA_INFORMATION );

    } finally {

        DebugUnwind( FatQueryEaInfo );

        //
        //  Unpin the dirent if pinned.
        //

        FatUnpinBcb( IrpContext, Bcb );

        DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

        DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0);
    }

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryPositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_POSITION_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine performs the query position information function for fat.

Arguments:

    FileObject - Supplies the File object being queried

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

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

    //
    //  Get the current position found in the file object.
    //

    Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;

    //
    //  Update the length and status output variables
    //

    *Length -= sizeof( FILE_POSITION_INFORMATION );

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0);

    UNREFERENCED_PARAMETER( IrpContext );

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryNameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PCCB Ccb,
    IN OUT PFILE_NAME_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine performs the query name information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    Ccb - Supplies the Ccb for the context of the user open

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

{
    ULONG BytesToCopy;
    LONG TrimLength;
    BOOLEAN Overflow = FALSE;

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

    //
    //  Convert the name to UNICODE
    //

    *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);

    //
    //  Use the full filename to build the path up.  If we wanted to be
    //  slick in the future, we'd just build the path directly into the
    //  return buffer and avoid constructing the full filename, but since
    //  the full filename winds up being required so often lets not
    //  over optimize this case yet.
    //
    
    if (Fcb->FullFileName.Buffer == NULL) {

        FatSetFullFileNameInFcb( IrpContext, Fcb );
    }

    //
    //  Here is where it gets a smidge tricky.  FinalNameLength is the length
    //  of the LFN element if it exists, and since the long name is always used
    //  to build FullFileName, we have two cases:
    //
    //  1) short name: use FinalNameLength to tear off the path from FullFileName
    //      and append the UNICODE converted short name.
    //  2) long name: just use FullFileName
    //
    //  We bias to the name the user thinks they opened by.  This winds
    //  up fixing some oddball tunneling cases where intermediate filters
    //  translate operations like delete into renames - this lets them
    //  do the operation in the context of the name the user was using.
    //
    //  It also matches what NTFS does, and so we have the definition of
    //  correct behavior.
    //

    //
    //
    //  Assume there is no long name and we are just going to use 
    //  FullFileName.
    //

    TrimLength = 0;

    //
    //  If a LongName exists and the original open was by the short name
    //  then set TrimLength to point to the place where the short name goes.
    //
    //
    //  Note: The Ccb can be NULL.  The lazy writer calls to get the name of
    //  a DirectoryOpen FILE_OBJECT that it wants to display in the lost
    //  delayed write popup.  Handle this case by just using the FileFullName.
    //

    if (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL) {

        if ((Ccb != NULL) && FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) {

            TrimLength = Fcb->FinalNameLength;

        }

    }

    if (*Length < Fcb->FullFileName.Length - TrimLength) {

        BytesToCopy = *Length;
        Overflow = TRUE;

    } else {

        BytesToCopy = Fcb->FullFileName.Length - TrimLength;
        *Length -= BytesToCopy;
    }

    RtlCopyMemory( &Buffer->FileName[0],
                   Fcb->FullFileName.Buffer,
                   BytesToCopy );

    //
    //  Note that this is just the amount of name we've copied so far.  It'll
    //  either be all of it (long) or the path element including the \ (short).
    //
    
    Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength;
    
    //
    //  If we trimmed off the name element, this is the short name case.  Pick
    //  up the UNICODE conversion and append it.
    //
    
    if (TrimLength != 0) {

        UNICODE_STRING ShortName;
        WCHAR ShortNameBuffer[12];
        NTSTATUS Status;

        //
        //  Convert the short name to UNICODE and figure out how much
        //  of it can fit.  Again, we always bump the returned length
        //  to indicate how much is available even if we can't return it.
        //

        ShortName.Length = 0;
        ShortName.MaximumLength = sizeof(ShortNameBuffer);
        ShortName.Buffer = ShortNameBuffer;

        Status = RtlOemStringToCountedUnicodeString( &ShortName,
                                                     &Fcb->ShortName.Name.Oem,
                                                     FALSE );

        ASSERT( Status == STATUS_SUCCESS );
        
        if (!Overflow) {
            
            if (*Length < ShortName.Length) {

                BytesToCopy = *Length;
                Overflow = TRUE;
            
            } else {

                BytesToCopy = ShortName.Length;
                *Length -= BytesToCopy;
            }

            RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength,
                           ShortName.Buffer,
                           BytesToCopy );
        }

        Buffer->FileNameLength += ShortName.Length;
    }

    if (Overflow) {
        
        *Length = -1;
    }
    
    //
    //  Return to caller
    //

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);

    UNREFERENCED_PARAMETER( IrpContext );

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryShortNameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN OUT PFILE_NAME_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++

Routine Description:

    This routine queries the short name of the file.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

{
    NTSTATUS Status;

    ULONG BytesToCopy;
    WCHAR ShortNameBuffer[12];
    UNICODE_STRING ShortName;

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

    //
    //  Convert the name to UNICODE
    //

    ShortName.Length = 0;
    ShortName.MaximumLength = sizeof(ShortNameBuffer);
    ShortName.Buffer = ShortNameBuffer;

    *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);

    Status = RtlOemStringToCountedUnicodeString( &ShortName,
                                                 &Fcb->ShortName.Name.Oem,
                                                 FALSE );

    ASSERT( Status == STATUS_SUCCESS );

    //
    //  If we overflow, set *Length to -1 as a flag.
    //

    if (*Length < ShortName.Length) {

        BytesToCopy = *Length;
        *Length = -1;

    } else {

        BytesToCopy = ShortName.Length;
        *Length -= ShortName.Length;
    }

    RtlCopyMemory( &Buffer->FileName[0],
                   &ShortName.Buffer[0],
                   BytesToCopy );

    Buffer->FileNameLength = ShortName.Length;

    //
    //  Return to caller
    //

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);

    UNREFERENCED_PARAMETER( IrpContext );

    return;
}


//
//  Internal Support Routine
//

VOID
FatQueryNetworkInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject,
    IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
    IN OUT PLONG Length
    )

/*++
 Description:

    This routine performs the query network open information function for fat.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified

    FileObject - Supplies the flag bit that indicates the file was modified.

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    None

--*/

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

    //
    //  Zero out the output buffer, and set it to indicate that
    //  the query is a normal file.  Later we might overwrite the
    //  attribute.
    //

    RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) );

    //
    //  Extract the data and fill in the non zero fields of the output
    //  buffer
    //

    if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {

        //
        //  We have to munge a lie on the fly.  Every time we have to
        //  use 1/1/80 we need to convert to GMT since the TZ may have
        //  changed on us.
        //

        ExLocalTimeToSystemTime( &FatJanOne1980,
                                 &Buffer->LastWriteTime );
        Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;

    } else {

        Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart;
        Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart;
        Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart;
    }

    Buffer->FileAttributes = Fcb->DirentFatFlags;

    //
    //  If the temporary flag is set, then set it in the buffer.
    //

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

        SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
    }

    //
    //  If no attributes were set, set the normal bit.
    //

    if (Buffer->FileAttributes == 0) {

        Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
    }
    //
    //  Case on whether this is a file or a directory, and extract
    //  the information and fill in the fcb/dcb specific parts
    //  of the output buffer
    //

    if (NodeType(Fcb) == FAT_NTC_FCB) {

        if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {

            FatLookupFileAllocationSize( IrpContext, Fcb );
        }

        Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart;
        Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart;
    }

    //
    //  Update the length and status output variables
    //

    *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );

    DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);

    DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0);

    return;
}


//
//  Internal Support routine
//

NTSTATUS
FatSetBasicInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFCB Fcb,
    IN PCCB Ccb
    )

/*++

Routine Description:

    This routine performs the set basic information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    Fcb - Supplies the Fcb or Dcb being processed, already known not to
        be the root dcb

    Ccb - Supplies the flag bit that control updating the last modify
        time on cleanup.

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    NTSTATUS Status;

    PFILE_BASIC_INFORMATION Buffer;

    PDIRENT Dirent;
    PBCB DirentBcb;

    FAT_TIME_STAMP CreationTime;
    UCHAR CreationMSec;
    FAT_TIME_STAMP LastWriteTime;
    FAT_TIME_STAMP LastAccessTime;
    FAT_DATE LastAccessDate;
    UCHAR Attributes;

    BOOLEAN ModifyCreation = FALSE;
    BOOLEAN ModifyLastWrite = FALSE;
    BOOLEAN ModifyLastAccess = FALSE;

    LARGE_INTEGER LargeCreationTime;
    LARGE_INTEGER LargeLastWriteTime;
    LARGE_INTEGER LargeLastAccessTime;


    ULONG NotifyFilter = 0;

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

    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  If the user is specifying -1 for a field, that means
    //  we should leave that field unchanged, even if we might
    //  have otherwise set it ourselves.  We'll set the Ccb flag
    //  saying that the user set the field so that we
    //  don't do our default updating.
    //
    //  We set the field to 0 then so we know not to actually
    //  set the field to the user-specified (and in this case,
    //  illegal) value.
    //

    if (Buffer->LastWriteTime.QuadPart == -1) {

        SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
        Buffer->LastWriteTime.QuadPart = 0;
    }

    if (Buffer->LastAccessTime.QuadPart == -1) {

        SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
        Buffer->LastAccessTime.QuadPart = 0;
    }

    if (Buffer->CreationTime.QuadPart == -1) {

        SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
        Buffer->CreationTime.QuadPart = 0;
    }

    DirentBcb = NULL;

    Status = STATUS_SUCCESS;

    try {

        LARGE_INTEGER FatLocalDecThirtyOne1979;
        LARGE_INTEGER FatLocalJanOne1980;

        ExLocalTimeToSystemTime( &FatDecThirtyOne1979,
                                 &FatLocalDecThirtyOne1979 );

        ExLocalTimeToSystemTime( &FatJanOne1980,
                                 &FatLocalJanOne1980 );

        //
        //  Get a pointer to the dirent
        //

        ASSERT( Fcb->FcbCondition == FcbGood );
        
        FatGetDirentFromFcbOrDcb( IrpContext,
                                  Fcb,
                                  &Dirent,
                                  &DirentBcb );

        ASSERT( Dirent && DirentBcb );

        //
        //  Check if the user specified a non-zero creation time
        //

        if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) {

            LargeCreationTime = Buffer->CreationTime;

            //
            //  Convert the Nt time to a Fat time
            //

            if ( !FatNtTimeToFatTime( IrpContext,
                                      &LargeCreationTime,
                                      FALSE,
                                      &CreationTime,
                                      &CreationMSec )) {

                //
                //  Special case the value 12/31/79 and treat this as 1/1/80.
                //  This '79 value can happen because of time zone issues.
                //

                if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                    (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) {

                    CreationTime = FatTimeJanOne1980;
                    LargeCreationTime = FatLocalJanOne1980;

                } else {

                    DebugTrace(0, Dbg, "Invalid CreationTime\n", 0);
                    try_return( Status = STATUS_INVALID_PARAMETER );
                }

                //
                //  Don't worry about CreationMSec
                //

                CreationMSec = 0;
            }

            ModifyCreation = TRUE;
        }

        //
        //  Check if the user specified a non-zero last access time
        //

        if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) {

            LargeLastAccessTime = Buffer->LastAccessTime;

            //
            //  Convert the Nt time to a Fat time
            //

            if ( !FatNtTimeToFatTime( IrpContext,
                                      &LargeLastAccessTime,
                                      TRUE,
                                      &LastAccessTime,
                                      NULL )) {

                //
                //  Special case the value 12/31/79 and treat this as 1/1/80.
                //  This '79 value can happen because of time zone issues.
                //

                if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                    (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) {

                    LastAccessTime = FatTimeJanOne1980;
                    LargeLastAccessTime = FatLocalJanOne1980;

                } else {

                    DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0);
                    try_return( Status = STATUS_INVALID_PARAMETER );
                }
            }

            LastAccessDate = LastAccessTime.Date;
            ModifyLastAccess = TRUE;
        }

        //
        //  Check if the user specified a non-zero last write time
        //

        if (Buffer->LastWriteTime.QuadPart != 0) {

            //
            //  First do a quick check here if the this time is the same
            //  time as LastAccessTime.
            //

            if (ModifyLastAccess &&
                (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) {

                ModifyLastWrite = TRUE;
                LastWriteTime = LastAccessTime;
                LargeLastWriteTime = LargeLastAccessTime;

            } else {

                LargeLastWriteTime = Buffer->LastWriteTime;

                //
                //  Convert the Nt time to a Fat time
                //

                if ( !FatNtTimeToFatTime( IrpContext,
                                          &LargeLastWriteTime,
                                          TRUE,
                                          &LastWriteTime,
                                          NULL )) {


                    //
                    //  Special case the value 12/31/79 and treat this as 1/1/80.
                    //  This '79 value can happen because of time zone issues.
                    //

                    if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                        (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) {

                        LastWriteTime = FatTimeJanOne1980;
                        LargeLastWriteTime = FatLocalJanOne1980;

                    } else {

                        DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0);
                        try_return( Status = STATUS_INVALID_PARAMETER );
                    }
                }

                ModifyLastWrite = TRUE;
            }
        }


        //
        //  Check if the user specified a non zero file attributes byte
        //

        if (Buffer->FileAttributes != 0) {

            //
            //  Only permit the attributes that FAT understands.  The rest are silently
            //  dropped on the floor.
            //

            Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY |
                                                           FILE_ATTRIBUTE_HIDDEN |
                                                           FILE_ATTRIBUTE_SYSTEM |
                                                           FILE_ATTRIBUTE_DIRECTORY |
                                                           FILE_ATTRIBUTE_ARCHIVE));

            //
            //  Make sure that for a file the directory bit is not set
            //  and that for a directory the bit is set.
            //

            if (NodeType(Fcb) == FAT_NTC_FCB) {

                if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {

                    DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0);
                    try_return( Status = STATUS_INVALID_PARAMETER );
                }

            } else {

                Attributes |= FAT_DIRENT_ATTR_DIRECTORY;
            }

            //
            //  Mark the FcbState temporary flag correctly.
            //

            if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {

                //
                //  Don't allow the temporary bit to be set on directories.
                //

                if (NodeType(Fcb) == FAT_NTC_DCB) {

                    DebugTrace(0, Dbg, "No temporary directories\n", 0);
                    try_return( Status = STATUS_INVALID_PARAMETER );
                }

                SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );

                SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
                         FO_TEMPORARY_FILE );

            } else {

                ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );

                ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
                           FO_TEMPORARY_FILE );
            }

            //
            //  Set the new attributes byte, and mark the bcb dirty
            //

            Fcb->DirentFatFlags = Attributes;

            Dirent->Attributes = Attributes;

            NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
        }

        if ( ModifyCreation ) {

            //
            //  Set the new last write time in the dirent, and mark
            //  the bcb dirty
            //

            Fcb->CreationTime = LargeCreationTime;
            Dirent->CreationTime = CreationTime;
            Dirent->CreationMSec = CreationMSec;


            NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
            //
            //  Now we have to round the time in the Fcb up to the
            //  nearest tem msec.
            //

            Fcb->CreationTime.QuadPart =

                ((Fcb->CreationTime.QuadPart + AlmostTenMSec) /
                 TenMSec) * TenMSec;

            //
            //  Now because the user just set the creation time we
            //  better not set the creation time on close
            //

            SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
        }

        if ( ModifyLastAccess ) {

            //
            //  Set the new last write time in the dirent, and mark
            //  the bcb dirty
            //

            Fcb->LastAccessTime = LargeLastAccessTime;
            Dirent->LastAccessDate = LastAccessDate;

            NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;

            //
            //  Now we have to truncate the time in the Fcb down to the
            //  current day.  This has to be in LocalTime though, so first
            //  convert to local, trunacate, then set back to GMT.
            //

            ExSystemTimeToLocalTime( &Fcb->LastAccessTime,
                                     &Fcb->LastAccessTime );

            Fcb->LastAccessTime.QuadPart =

                (Fcb->LastAccessTime.QuadPart /
                 FatOneDay.QuadPart) * FatOneDay.QuadPart;

            ExLocalTimeToSystemTime( &Fcb->LastAccessTime,
                                     &Fcb->LastAccessTime );

            //
            //  Now because the user just set the last access time we
            //  better not set the last access time on close
            //

            SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
        }

        if ( ModifyLastWrite ) {

            //
            //  Set the new last write time in the dirent, and mark
            //  the bcb dirty
            //

            Fcb->LastWriteTime = LargeLastWriteTime;
            Dirent->LastWriteTime = LastWriteTime;

            NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;

            //
            //  Now we have to round the time in the Fcb up to the
            //  nearest two seconds.
            //

            Fcb->LastWriteTime.QuadPart =

                ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) /
                 TwoSeconds) * TwoSeconds;

            //
            //  Now because the user just set the last write time we
            //  better not set the last write time on close
            //

            SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
        }

        //
        //  If we modified any of the values, we report this to the notify
        //  package.
        //
        //  We also take this opportunity to set the current file size and
        //  first cluster in the Dirent in order to support a server hack.
        //

        if (NotifyFilter != 0) {

            if (NodeType(Fcb) == FAT_NTC_FCB) {

                Dirent->FileSize = Fcb->Header.FileSize.LowPart;

                Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile;

                if (FatIsFat32(Fcb->Vcb)) {

                    Dirent->FirstClusterOfFileHi =
                            (USHORT)(Fcb->FirstClusterOfFile >> 16);
                }
            }

            FatNotifyReportChange( IrpContext,
                                   Fcb->Vcb,
                                   Fcb,
                                   NotifyFilter,
                                   FILE_ACTION_MODIFIED );

            FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
        }

    try_exit: NOTHING;
    } finally {

        DebugUnwind( FatSetBasicInfo );

        FatUnpinBcb( IrpContext, DirentBcb );

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

    return Status;
}

//
//  Internal Support Routine
//

NTSTATUS
FatSetDispositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject,
    IN PFCB Fcb
    )

/*++

Routine Description:

    This routine performs the set disposition information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    FileObject - Supplies the file object being processed

    Fcb - Supplies the Fcb or Dcb being processed, already known not to
        be the root dcb

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    PFILE_DISPOSITION_INFORMATION Buffer;
    PBCB Bcb;
    PDIRENT Dirent;

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

    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  Check if the user wants to delete the file or not delete
    //  the file
    //

    if (Buffer->DeleteFile) {

        //
        //  Check if the file is marked read only
        //

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

            DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0);

            return STATUS_CANNOT_DELETE;
        }

        //
        //  Make sure there is no process mapping this file as an image.
        //

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

            DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);

            return STATUS_CANNOT_DELETE;
        }

        //
        //  Check if this is a dcb and if so then only allow
        //  the request if the directory is empty.
        //

        if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {

            DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0);

            return STATUS_CANNOT_DELETE;
        }

        if (NodeType(Fcb) == FAT_NTC_DCB) {

            DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0);

            //
            //  Check if the directory is empty
            //

            if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) {

                DebugTrace(-1, Dbg, "Directory is not empty\n", 0);

                return STATUS_DIRECTORY_NOT_EMPTY;
            }
        }

        //
        //  If this is a floppy, touch the volume so to verify that it
        //  is not write protected.
        //

        if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {

            PVCB Vcb;
            PBCB Bcb = NULL;
            UCHAR *Buffer;
            UCHAR TmpChar;
            ULONG BytesToMap;

            IO_STATUS_BLOCK Iosb;

            Vcb = Fcb->Vcb;

            BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ?
                         FatReservedBytes(&Vcb->Bpb) +
                         FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE;

            FatReadVolumeFile( IrpContext,
                               Vcb,
                               0,
                               BytesToMap,
                               &Bcb,
                               (PVOID *)&Buffer );

            try {

                if (!CcPinMappedData( Vcb->VirtualVolumeFile,
                                      &FatLargeZero,
                                      BytesToMap,
                                      BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
                                      &Bcb )) {

                    //
                    // Could not pin the data without waiting (cache miss).
                    //

                    FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
                }

                //
                //  Make Mm, myself, and Cc think the byte is dirty, and then
                //  force a writethrough.
                //

                Buffer += FatReservedBytes(&Vcb->Bpb);

                TmpChar = Buffer[0];
                Buffer[0] = TmpChar;

                FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb,
                                FatReservedBytes( &Vcb->Bpb ),
                                FatReservedBytes( &Vcb->Bpb ),
                                Vcb->Bpb.BytesPerSector );

            } finally {

                if (AbnormalTermination() && (Bcb != NULL)) {

                    FatUnpinBcb( IrpContext, Bcb );
                }
            }

            CcRepinBcb( Bcb );
            CcSetDirtyPinnedData( Bcb, NULL );
            CcUnpinData( Bcb );
            DbgDoit( ASSERT( IrpContext->PinCount ));
            DbgDoit( IrpContext->PinCount -= 1 );
            CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );

            //
            //  If this was not successful, raise the status.
            //

            if ( !NT_SUCCESS(Iosb.Status) ) {

                FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status );
            }

        } else {

            //
            //  Just set a Bcb dirty here.  The above code was only there to
            //  detect a write protected floppy, while the below code works
            //  for any write protected media and only takes a hit when the
            //  volume in clean.
            //

            FatGetDirentFromFcbOrDcb( IrpContext,
                                      Fcb,
                                      &Dirent,
                                      &Bcb );

            //
            //  This has to work for the usual reasons (we verified the Fcb within
            //  volume synch).
            //
            
            ASSERT( Bcb != NULL );

            try {

                FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );

            } finally {

                FatUnpinBcb( IrpContext, Bcb );
            }
        }

        //
        //  At this point either we have a file or an empty directory
        //  so we know the delete can proceed.
        //

        SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
        FileObject->DeletePending = TRUE;

        //
        //  If this is a directory then report this delete pending to
        //  the dir notify package.
        //

        if (NodeType(Fcb) == FAT_NTC_DCB) {

            FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync,
                                            &Fcb->Vcb->DirNotifyList,
                                            FileObject->FsContext,
                                            NULL,
                                            FALSE,
                                            FALSE,
                                            0,
                                            NULL,
                                            NULL,
                                            NULL );
        }
    } else {

        //
        //  The user doesn't want to delete the file so clear
        //  the delete on close bit
        //

        DebugTrace(0, Dbg, "User want to not delete file\n", 0);

        ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
        FileObject->DeletePending = FALSE;
    }

    DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);

    return STATUS_SUCCESS;
}


//
//  Internal Support Routine
//

NTSTATUS
FatSetRenameInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PVCB Vcb,
    IN PFCB Fcb,
    IN PCCB Ccb
    )

/*++

Routine Description:

    This routine performs the set name information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    Vcb - Supplies the Vcb being processed

    Fcb - Supplies the Fcb or Dcb being processed, already known not to
        be the root dcb

    Ccb - Supplies the Ccb corresponding to the handle opening the source
        file

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    BOOLEAN AllLowerComponent;
    BOOLEAN AllLowerExtension;
    BOOLEAN CaseOnlyRename;
    BOOLEAN ContinueWithRename;
    BOOLEAN CreateLfn;
    BOOLEAN DeleteSourceDirent;
    BOOLEAN DeleteTarget;
    BOOLEAN NewDirentFromPool;
    BOOLEAN RenamedAcrossDirectories;
    BOOLEAN ReplaceIfExists;

    CCB LocalCcb;
    PCCB SourceCcb;

    DIRENT SourceDirent;

    NTSTATUS Status;

    OEM_STRING OldOemName;
    OEM_STRING NewOemName;
    UCHAR OemNameBuffer[24*2];

    PBCB DotDotBcb;
    PBCB NewDirentBcb;
    PBCB OldDirentBcb;
    PBCB SecondPageBcb;
    PBCB TargetDirentBcb;

    PDCB TargetDcb;
    PDCB OldParentDcb;

    PDIRENT DotDotDirent;
    PDIRENT FirstPageDirent;
    PDIRENT NewDirent;
    PDIRENT OldDirent;
    PDIRENT SecondPageDirent;
    PDIRENT ShortDirent;
    PDIRENT TargetDirent;

    PFCB TempFcb;

    PFILE_OBJECT TargetFileObject;
    PFILE_OBJECT FileObject;

    PIO_STACK_LOCATION IrpSp;

    PLIST_ENTRY Links;

    ULONG BytesInFirstPage;
    ULONG DirentsInFirstPage;
    ULONG DirentsRequired;
    ULONG NewOffset;
    ULONG NotifyAction;
    ULONG SecondPageOffset;
    ULONG ShortDirentOffset;
    ULONG TargetDirentOffset;
    ULONG TargetLfnOffset;

    UNICODE_STRING NewName;
    UNICODE_STRING NewUpcasedName;
    UNICODE_STRING OldName;
    UNICODE_STRING OldUpcasedName;
    UNICODE_STRING TargetLfn;

    PWCHAR UnicodeBuffer;

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

    BOOLEAN InvalidateFcbOnRaise = FALSE;

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

    //
    //  P H A S E  0: Initialize some variables.
    //

    CaseOnlyRename = FALSE;
    ContinueWithRename = FALSE;
    DeleteSourceDirent = FALSE;
    DeleteTarget = FALSE;
    NewDirentFromPool = FALSE;
    RenamedAcrossDirectories = FALSE;

    DotDotBcb = NULL;
    NewDirentBcb = NULL;
    OldDirentBcb = NULL;
    SecondPageBcb = NULL;
    TargetDirentBcb = NULL;

    NewOemName.Length = 0;
    NewOemName.MaximumLength = 24;
    NewOemName.Buffer = &OemNameBuffer[0];

    OldOemName.Length = 0;
    OldOemName.MaximumLength = 24;
    OldOemName.Buffer = &OemNameBuffer[24];

    UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool,
                                              4 * MAX_LFN_CHARACTERS * sizeof(WCHAR),
                                              TAG_FILENAME_BUFFER );

    NewUpcasedName.Length = 0;
    NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
    NewUpcasedName.Buffer = &UnicodeBuffer[0];

    OldName.Length = 0;
    OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
    OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS];

    OldUpcasedName.Length = 0;
    OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
    OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2];

    TargetLfn.Length = 0;
    TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
    TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3];

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

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

    //
    //  Remember the name in case we have to modify the name
    //  value in the ea.
    //

    RtlCopyMemory( OldOemName.Buffer,
                   Fcb->ShortName.Name.Oem.Buffer,
                   OldOemName.Length );

    //
    //  Get the current stack location
    //

    IrpSp = IoGetCurrentIrpStackLocation( Irp );

    //
    //  Extract information from the Irp to make our life easier
    //

    FileObject = IrpSp->FileObject;
    SourceCcb = FileObject->FsContext2;
    TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
    ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;

    RtlZeroMemory( &LocalCcb, sizeof(CCB) );

    //
    //  P H A S E  1:
    //
    //  Test if rename is legal.  Only small side-effects are not undone.
    //

    try {

        //
        //  Can't rename the root directory
        //

        if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {

            try_return( Status = STATUS_INVALID_PARAMETER );
        }

        //
        //  Check that we were not given a dcb with open handles beneath
        //  it.  If there are only UncleanCount == 0 Fcbs beneath us, then
        //  remove them from the prefix table, and they will just close
        //  and go away naturally.
        //

        if (NodeType(Fcb) == FAT_NTC_DCB) {

            PFCB BatchOplockFcb;
            ULONG BatchOplockCount;

            //
            //  Loop until there are no batch oplocks in the subtree below
            //  this directory.
            //

            while (TRUE) {

                BatchOplockFcb = NULL;
                BatchOplockCount = 0;

                //
                //  First look for any UncleanCount != 0 Fcbs, and fail if we
                //  find any.
                //

                for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
                      TempFcb != Fcb;
                      TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {

                     if ( TempFcb->UncleanCount != 0 ) {

                         //
                         // If there is a batch oplock on this file then
                         // increment our count and remember the Fcb if
                         // this is the first.
                         //

                         if ( (NodeType(TempFcb) == FAT_NTC_FCB) &&
                              FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock ) ) {

                             BatchOplockCount += 1;
                             if ( BatchOplockFcb == NULL ) {

                                 BatchOplockFcb = TempFcb;
                             }

                         } else {

                            try_return( Status = STATUS_ACCESS_DENIED );
                         }
                     }
                }

                //
                //  If this is not the first pass for rename and the number
                //  of batch oplocks has not decreased then give up.
                //

                if ( BatchOplockFcb != NULL ) {

                    if ( (Irp->IoStatus.Information != 0) &&
                         (BatchOplockCount >= Irp->IoStatus.Information) ) {

                        try_return( Status = STATUS_ACCESS_DENIED );
                    }

                    //
                    //  Try to break this batch oplock.
                    //

                    Irp->IoStatus.Information = BatchOplockCount;
                    Status = FsRtlCheckOplock( &BatchOplockFcb->Specific.Fcb.Oplock,
                                               Irp,
                                               IrpContext,
                                               FatOplockComplete,
                                               NULL );

                    //
                    //  If the oplock was already broken then look for more
                    //  batch oplocks.
                    //

                    if (Status == STATUS_SUCCESS) {

                        continue;
                    }

                    //
                    //  Otherwise the oplock package will post or complete the
                    //  request.
                    //

                    try_return( Status = STATUS_PENDING );
                }

                break;
            }

            //
            //  Now try to get as many of these file object, and thus Fcbs
            //  to go away as possible, flushing first, of course.
            //

            FatPurgeReferencedFileObjects( IrpContext, Fcb, TRUE );

            //
            //  OK, so there are no UncleanCount != 0, Fcbs.  Infact, there
            //  shouldn't really be any Fcbs left at all, except obstinate
            //  ones from user mapped sections ....oh well, he shouldn't have
            //  closed his handle if he wanted the file to stick around.  So
            //  remove any Fcbs beneath us from the splay table and mark them
            //  DELETE_ON_CLOSE so that any future operations will fail.
            //

            for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
                  TempFcb != Fcb;
                  TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {

                FatRemoveNames( IrpContext, TempFcb );

                SetFlag( TempFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
            }
        }

        //
        //  Check if this is a simple rename or a fully-qualified rename
        //  In both cases we need to figure out what the TargetDcb, and
        //  NewName are.
        //

        if (TargetFileObject == NULL) {

            //
            //  In the case of a simple rename the target dcb is the
            //  same as the source file's parent dcb, and the new file name
            //  is taken from the system buffer
            //

            PFILE_RENAME_INFORMATION Buffer;

            Buffer = Irp->AssociatedIrp.SystemBuffer;

            TargetDcb = Fcb->ParentDcb;

            NewName.Length = (USHORT) Buffer->FileNameLength;
            NewName.Buffer = (PWSTR) &Buffer->FileName;

            //
            //  Make sure the name is of legal length.
            //

            if (NewName.Length >= 255*sizeof(WCHAR)) {

                try_return( Status = STATUS_OBJECT_NAME_INVALID );
            }

        } else {

            //
            //  For a fully-qualified rename the target dcb is taken from
            //  the target file object, which must be on the same vcb as
            //  the source.
            //

            PVCB TargetVcb;
            PCCB TargetCcb;

            if ((FatDecodeFileObject( TargetFileObject,
                                      &TargetVcb,
                                      &TargetDcb,
                                      &TargetCcb ) != UserDirectoryOpen) ||
                (TargetVcb != Vcb)) {

                try_return( Status = STATUS_INVALID_PARAMETER );
            }

            //
            //  This name is by definition legal.
            //

            NewName = *((PUNICODE_STRING)&TargetFileObject->FileName);
        }

        //
        //  We will need an upcased version of the unicode name and the
        //  old name as well.
        //

        Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE );

        if (!NT_SUCCESS(Status)) {

            try_return( Status );
        }

        FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName );

        Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE );

        if (!NT_SUCCESS(Status)) {
            try_return(Status);
        }

        //
        //  Check if the current name and new name are equal, and the
        //  DCBs are equal.  If they are then our work is already done.
        //

        if (TargetDcb == Fcb->ParentDcb) {

            //
            //  OK, now if we found something then check if it was an exact
            //  match or just a case match.  If it was an exact match, then
            //  we can bail here.
            //

            if (FsRtlAreNamesEqual( &NewName,
                                    &OldName,
                                    FALSE,
                                    NULL )) {

                 try_return( Status = STATUS_SUCCESS );
            }

            //
            //  Check now for a case only rename.
            //


            if (FsRtlAreNamesEqual( &NewUpcasedName,
                                    &OldUpcasedName,
                                    FALSE,
                                    NULL )) {

                 CaseOnlyRename = TRUE;
            }

        } else {

            RenamedAcrossDirectories = TRUE;
        }

        //
        //  Upcase the name and convert it to the Oem code page.
        //
        //  If the new UNICODE name is already more than 12 characters,
        //  then we know the Oem name will not be valid
        //

        if (NewName.Length <= 12*sizeof(WCHAR)) {

            FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName );

            //
            //  If the name is not valid 8.3, zero the length.
            //

            if (FatSpaceInName( IrpContext, &NewName ) ||
                !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) {

                NewOemName.Length = 0;
            }

        } else {

            NewOemName.Length = 0;
        }

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

        TunneledDataSize = sizeof(LARGE_INTEGER);
        HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
                                                          FatDirectoryKey(TargetDcb),
                                                          &NewName,
                                                          &UniTunneledShortName,
                                                          &UniTunneledLongName,
                                                          &TunneledDataSize,
                                                          &TunneledCreationTime );
        ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));

        //
        //  Now we need to determine how many dirents this new name will
        //  require.
        //

        if ((NewOemName.Length == 0) ||
            (FatEvaluateNameCase( IrpContext,
                                  &NewName,
                                  &AllLowerComponent,
                                  &AllLowerExtension,
                                  &CreateLfn ),
             CreateLfn)) {

            DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1;

        } else {

            //
            //  The user-given name is a short name, but we might still have
            //  a tunneled long name we want to use. See if we can.
            //

            if (UniTunneledLongName.Length && 
                !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) {

                UsingTunneledLfn = CreateLfn = TRUE;
                DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1;

            } else {

                //
                //  This really is a simple dirent.  Note that the two AllLower BOOLEANs
                //  are correctly set now.
                //

                DirentsRequired = 1;
            }
        }

        //
        //  Do some extra checks here if we are not in Chicago mode.
        //

        if (!FatData.ChicagoMode) {

            //
            //  If the name was not 8.3 valid, fail the rename.
            //

            if (NewOemName.Length == 0) {

                try_return( Status = STATUS_OBJECT_NAME_INVALID );
            }

            //
            //  Don't use the magic bits.
            //

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

        if (!CaseOnlyRename) {

            //
            //  Check if the new name already exists, wait is known to be
            //  true.
            //

            if (NewOemName.Length != 0) {

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

            } else {

                SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
            }

            LocalCcb.UnicodeQueryTemplate = NewUpcasedName;
            LocalCcb.ContainsWildCards = FALSE;

            FatLocateDirent( IrpContext,
                             TargetDcb,
                             &LocalCcb,
                             0,
                             &TargetDirent,
                             &TargetDirentBcb,
                             &TargetDirentOffset,
                             NULL,
                             &TargetLfn);

            if (TargetDirent != NULL) {

                //
                //  The name already exists, check if the user wants
                //  to overwrite the name, and has access to do the overwrite
                //  We cannot overwrite a directory.
                //

                if ((!ReplaceIfExists) ||
                    (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) ||
                    (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) {

                    try_return( Status = STATUS_OBJECT_NAME_COLLISION );
                }

                //
                //  Check that the file has no open user handles, if it does
                //  then we will deny access.  We do the check by searching
                //  down the list of fcbs opened under our parent Dcb, and making
                //  sure none of the maching Fcbs have a non-zero unclean count or
                //  outstanding image sections.
                //

                for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
                     Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) {

                    TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );

                    //
                    //  Advance now.  The image section flush may cause the final
                    //  close, which will recursively happen underneath of us here.
                    //  It would be unfortunate if we looked through free memory.
                    //

                    Links = Links->Flink;

                    if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) &&
                        ((TempFcb->UncleanCount != 0) ||
                         !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers,
                                               MmFlushForDelete))) {

                        //
                        //  If there are batch oplocks on this file then break the
                        //  oplocks before failing the rename.
                        //

                        Status = STATUS_ACCESS_DENIED;

                        if ((NodeType(TempFcb) == FAT_NTC_FCB) &&
                            FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock )) {

                            //
                            //  Do all of our cleanup now since the IrpContext
                            //  could go away when this request is posted.
                            //

                            FatUnpinBcb( IrpContext, TargetDirentBcb );

                            Status = FsRtlCheckOplock( &TempFcb->Specific.Fcb.Oplock,
                                                       Irp,
                                                       IrpContext,
                                                       FatOplockComplete,
                                                       NULL );

                            if (Status != STATUS_PENDING) {

                                Status = STATUS_ACCESS_DENIED;
                            }
                        }

                        try_return( NOTHING );
                    }
                }

                //
                //  OK, this target is toast.  Remember the Lfn offset.
                //

                TargetLfnOffset = TargetDirentOffset -
                                  FAT_LFN_DIRENTS_NEEDED(&TargetLfn) *
                                  sizeof(DIRENT);

                DeleteTarget = TRUE;
            }
        }

        //
        //  If we will need more dirents than we have, allocate them now.
        //

        if ((TargetDcb != Fcb->ParentDcb) ||
            (DirentsRequired !=
             (Fcb->DirentOffsetWithinDirectory -
              Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) {

            //
            //  Get some new allocation
            //

            NewOffset = FatCreateNewDirent( IrpContext,
                                            TargetDcb,
                                            DirentsRequired );

            DeleteSourceDirent = TRUE;

        } else {

            NewOffset = Fcb->LfnOffsetWithinDirectory;
        }

        ContinueWithRename = TRUE;

    try_exit: NOTHING;

    } finally {

        if (!ContinueWithRename) {

            //
            //  Undo everything from above.
            //

            ExFreePool( UnicodeBuffer );
            FatUnpinBcb( IrpContext, TargetDirentBcb );
        }
    }

    //
    //  Now, if we are already done, return here.
    //

    if (!ContinueWithRename) {

        return Status;
    }

    //
    //  P H A S E  2: Actually perform the rename.
    //

    try {

        //
        //  Report the fact that we are going to remove this entry.
        //  If we renamed within the same directory and the new name for the
        //  file did not previously exist, we report this as a rename old
        //  name.  Otherwise this is a removed file.
        //

        if (!RenamedAcrossDirectories && !DeleteTarget) {

            NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;

        } else {

            NotifyAction = FILE_ACTION_REMOVED;
        }

        FatNotifyReportChange( IrpContext,
                               Vcb,
                               Fcb,
                               ((NodeType( Fcb ) == FAT_NTC_FCB)
                                ? FILE_NOTIFY_CHANGE_FILE_NAME
                                : FILE_NOTIFY_CHANGE_DIR_NAME ),
                               NotifyAction );

        //
        //  Capture a copy of the source dirent.
        //

        FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &OldDirent, &OldDirentBcb );
        SourceDirent = *OldDirent;

        try {

            //
            //  Tunnel the source Fcb - the names are disappearing regardless of
            //  whether the dirent allocation physically changed
            //

            FatTunnelFcbOrDcb( Fcb, SourceCcb );

            //
            //  From here until very nearly the end of the operation, if we raise there
            //  is no reasonable way to suppose we'd be able to undo the damage.  Not
            //  being a transactional filesystem, FAT is at the mercy of a lot of things
            //  (as the astute reader has no doubt realized by now).
            //

            InvalidateFcbOnRaise = TRUE;

            //
            //  Delete our current dirent(s) if we got a new one.
            //

            if (DeleteSourceDirent) {

                FatDeleteDirent( IrpContext, Fcb, NULL, FALSE );
            }

            //
            //  Delete a target conflict if we were meant to.
            //

            if (DeleteTarget) {

                FatDeleteFile( IrpContext,
                               TargetDcb,
                               TargetLfnOffset,
                               TargetDirentOffset,
                               TargetDirent,
                               &TargetLfn );
            }

            //
            //  We need to evaluate any short names required.  If there were any
            //  conflicts in existing short names, they would have been deleted above.
            //
            //  It isn't neccesary to worry about the UsingTunneledLfn case. Since we
            //  actually already know whether CreateLfn will be set either NewName is
            //  an Lfn and !UsingTunneledLfn is implied or NewName is a short name and
            //  we can handle that externally.
            //

            FatSelectNames( IrpContext,
                            TargetDcb,
                            &NewOemName,
                            &NewName,
                            &NewOemName,
                            (HaveTunneledInformation ? &UniTunneledShortName : NULL),
                            &AllLowerComponent,
                            &AllLowerExtension,
                            &CreateLfn );

            if (!CreateLfn && UsingTunneledLfn) {

                CreateLfn = TRUE;
                NewName = UniTunneledLongName;

                //
                //  Short names are always upcase if an LFN exists
                //

                AllLowerComponent = FALSE;
                AllLowerExtension = FALSE;
            }

            //
            //  OK, now setup the new dirent(s) for the new name.
            //

            FatPrepareWriteDirectoryFile( IrpContext,
                                          TargetDcb,
                                          NewOffset,
                                          sizeof(DIRENT),
                                          &NewDirentBcb,
                                          &NewDirent,
                                          FALSE,
                                          TRUE,
                                          &Status );

            ASSERT( NT_SUCCESS( Status ) );

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

            if ((NewOffset / PAGE_SIZE) !=
                ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {

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

                BytesInFirstPage = SecondPageOffset - NewOffset;

                DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);

                FatPrepareWriteDirectoryFile( IrpContext,
                                              TargetDcb,
                                              SecondPageOffset,
                                              sizeof(DIRENT),
                                              &SecondPageBcb,
                                              &SecondPageDirent,
                                              FALSE,
                                              TRUE,
                                              &Status );

                ASSERT( NT_SUCCESS( Status ) );

                FirstPageDirent = NewDirent;

                NewDirent = FsRtlAllocatePoolWithTag( PagedPool,
                                                      DirentsRequired * sizeof(DIRENT),
                                                      TAG_DIRENT );

                NewDirentFromPool = TRUE;
            }

            //
            //  Bump up Dirent and DirentOffset
            //

            ShortDirent = NewDirent + DirentsRequired - 1;
            ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT);

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

            *ShortDirent = SourceDirent;

            FatConstructDirent( IrpContext,
                                ShortDirent,
                                &NewOemName,
                                AllLowerComponent,
                                AllLowerExtension,
                                CreateLfn ? &NewName : NULL,
                                SourceDirent.Attributes,
                                FALSE,
                                (HaveTunneledInformation ? &TunneledCreationTime : NULL) );

            if (HaveTunneledInformation) {

                //
                //  Need to go in and fix the timestamps in the FCB. Note that we can't use
                //  the TunneledCreationTime since the conversions may have failed.
                //

                Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec);
                Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0);
                Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate);
            }

            //
            //  If the dirent crossed pages, split the contents of the
            //  temporary pool between the two pages.
            //

            if (NewDirentFromPool) {

                RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage );

                RtlCopyMemory( SecondPageDirent,
                               NewDirent + DirentsInFirstPage,
                               DirentsRequired*sizeof(DIRENT) - BytesInFirstPage );

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

        } finally {

            //
            //  Remove the entry from the splay table, and then remove the
            //  full file name and exact case lfn. It is important that we
            //  always remove the name from the prefix table regardless of
            //  other errors.
            //

            FatRemoveNames( IrpContext, Fcb );

            if (Fcb->FullFileName.Buffer != NULL) {

                ExFreePool( Fcb->FullFileName.Buffer );
                Fcb->FullFileName.Buffer = NULL;
            }

            if (Fcb->ExactCaseLongName.Buffer) {

                ExFreePool( Fcb->ExactCaseLongName.Buffer );
                Fcb->ExactCaseLongName.Buffer = NULL;
            }
        }

        //
        //  Now we need to update the location of the file's directory
        //  offset and move the fcb from its current parent dcb to
        //  the target dcb.
        //

        Fcb->LfnOffsetWithinDirectory = NewOffset;
        Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;

        RemoveEntryList( &Fcb->ParentDcbLinks );

        //
        //  There is a deep reason we put files on the tail, others on the head,
        //  which is to allow us to easily enumerate all child directories before
        //  child files. This is important to let us maintain whole-volume lockorder
        //  via BottomUp enumeration.
        //

        if (NodeType(Fcb) == FAT_NTC_FCB) {

            InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
                            &Fcb->ParentDcbLinks );

        } else {

            InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
                            &Fcb->ParentDcbLinks );
        }

        OldParentDcb = Fcb->ParentDcb;
        Fcb->ParentDcb = TargetDcb;

        //
        //  If we renamed across directories, some cleanup is now in order.
        //

        if (RenamedAcrossDirectories) {

            //
            //  See if we need to uninitialize the cachemap for the source directory.
            //  Do this now in case we get unlucky and raise trying to finalize the
            //  operation.
            //

            if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) &&
                (OldParentDcb->OpenCount == 0) &&
                (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) {

                PFILE_OBJECT DirectoryFileObject;

                ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );

                DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;

                DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);

                CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );

                OldParentDcb->Specific.Dcb.DirectoryFile = NULL;

                ObDereferenceObject( DirectoryFileObject );
            }

            //
            //  If we move a directory across directories, we have to change
            //  the cluster number in its .. entry
            //

            if (NodeType(Fcb) == FAT_NTC_DCB) {

                FatPrepareWriteDirectoryFile( IrpContext,
                                              Fcb,
                                              sizeof(DIRENT),
                                              sizeof(DIRENT),
                                              &DotDotBcb,
                                              &DotDotDirent,
                                              FALSE,
                                              TRUE,
                                              &Status );

                ASSERT( NT_SUCCESS( Status ) );

                DotDotDirent->FirstClusterOfFile = (USHORT)
                    ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ?
                      0 : TargetDcb->FirstClusterOfFile);

                if (FatIsFat32( Vcb )) {

                    DotDotDirent->FirstClusterOfFileHi = (USHORT)
                    ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ?
                      0 : (TargetDcb->FirstClusterOfFile >> 16));
                }
            }
        }

        //
        //  Now we need to setup the splay table and the name within
        //  the fcb.  Free the old short name at this point.
        //

        ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
        Fcb->ShortName.Name.Oem.Buffer = NULL;

        FatConstructNamesInFcb( IrpContext,
                                Fcb,
                                ShortDirent,
                                CreateLfn ? &NewName : NULL );

        FatSetFullNameInFcb( IrpContext, Fcb, &NewName );

        //
        //  The rest of the actions taken are not related to correctness of
        //  the in-memory structures, so we shouldn't toast the Fcb if we
        //  raise from here to the end.
        //

        InvalidateFcbOnRaise = FALSE;

        //
        //  If a file, set the file as modified so that the archive bit
        //  is set.  We prevent this from adjusting the write time by
        //  indicating the user flag in the ccb.
        //

        if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {

            SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
            SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
        }

        //
        //  We have three cases to report.
        //
        //      1.  If we overwrote an existing file, we report this as
        //          a modified file.
        //
        //      2.  If we renamed to a new directory, then we added a file.
        //
        //      3.  If we renamed in the same directory, then we report the
        //          the renamednewname.
        //

        if (DeleteTarget) {

            FatNotifyReportChange( IrpContext,
                                   Vcb,
                                   Fcb,
                                   FILE_NOTIFY_CHANGE_ATTRIBUTES
                                   | FILE_NOTIFY_CHANGE_SIZE
                                   | FILE_NOTIFY_CHANGE_LAST_WRITE
                                   | FILE_NOTIFY_CHANGE_LAST_ACCESS
                                   | FILE_NOTIFY_CHANGE_CREATION
                                   | FILE_NOTIFY_CHANGE_EA,
                                   FILE_ACTION_MODIFIED );

        } else if (RenamedAcrossDirectories) {

            FatNotifyReportChange( IrpContext,
                                   Vcb,
                                   Fcb,
                                   ((NodeType( Fcb ) == FAT_NTC_FCB)
                                    ? FILE_NOTIFY_CHANGE_FILE_NAME
                                    : FILE_NOTIFY_CHANGE_DIR_NAME ),
                                   FILE_ACTION_ADDED );

        } else {

            FatNotifyReportChange( IrpContext,
                                   Vcb,
                                   Fcb,
                                   ((NodeType( Fcb ) == FAT_NTC_FCB)
                                    ? FILE_NOTIFY_CHANGE_FILE_NAME
                                    : FILE_NOTIFY_CHANGE_DIR_NAME ),
                                   FILE_ACTION_RENAMED_NEW_NAME );
        }

        //
        //  We need to update the file name in the dirent.  This value
        //  is never used elsewhere, so we don't concern ourselves
        //  with any error we may encounter.  We let chkdsk fix the
        //  disk at some later time.
        //

        if (!FatIsFat32(Vcb) &&
            ShortDirent->ExtendedAttributes != 0) {

            FatRenameEAs( IrpContext,
                          Fcb,
                          ShortDirent->ExtendedAttributes,
                          &OldOemName );
        }

        //
        //  Set our final status
        //

        Status = STATUS_SUCCESS;

    } finally {

        DebugUnwind( FatSetRenameInfo );

        ExFreePool( UnicodeBuffer );

        if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {

            //
            //  Free pool if the buffer was grown on tunneling lookup
            //

            ExFreePool(UniTunneledLongName.Buffer);
        }

        FatUnpinBcb( IrpContext, OldDirentBcb );
        FatUnpinBcb( IrpContext, TargetDirentBcb );
        FatUnpinBcb( IrpContext, NewDirentBcb );
        FatUnpinBcb( IrpContext, SecondPageBcb );
        FatUnpinBcb( IrpContext, DotDotBcb );


        //
        //  If this was an abnormal termination, then we are in trouble.
        //  Should the operation have been in a sensitive state there is
        //  nothing we can do but invalidate the Fcb.
        //

        if (AbnormalTermination() && InvalidateFcbOnRaise) {

            Fcb->FcbCondition = FcbBad;
        }

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

    return Status;
}


//
//  Internal Support Routine
//

NTSTATUS
FatSetPositionInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject
    )

/*++

Routine Description:

    This routine performs the set position information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    FileObject - Supplies the file object being processed

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    PFILE_POSITION_INFORMATION Buffer;

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

    Buffer = Irp->AssociatedIrp.SystemBuffer;

    //
    //  Check if the file does not use intermediate buffering.  If it
    //  does not use intermediate buffering then the new position we're
    //  supplied must be aligned properly for the device
    //

    if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {

        PDEVICE_OBJECT DeviceObject;

        DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;

        if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {

            DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0);
            DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER);

            return STATUS_INVALID_PARAMETER;
        }
    }

    //
    //  The input parameter is fine so set the current byte offset and
    //  complete the request
    //

    DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset);

    FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;

    DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS);

    UNREFERENCED_PARAMETER( IrpContext );

    return STATUS_SUCCESS;
}


//
//  Internal Support Routine
//

NTSTATUS
FatSetAllocationInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject
    )

/*++

Routine Description:

    This routine performs the set Allocation information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    Fcb - Supplies the Fcb or Dcb being processed, already known not to
        be the root dcb

    FileObject - Supplies the FileObject being processed, already known not to
        be the root dcb

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PFILE_ALLOCATION_INFORMATION Buffer;
    ULONG NewAllocationSize;

    BOOLEAN FileSizeTruncated = FALSE;
    BOOLEAN CacheMapInitialized = FALSE;
    BOOLEAN ResourceAcquired = FALSE;
    ULONG OriginalFileSize;
    ULONG OriginalValidDataLength;
    ULONG OriginalValidDataToDisk;

    Buffer = Irp->AssociatedIrp.SystemBuffer;

    NewAllocationSize = Buffer->AllocationSize.LowPart;

    DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize);

    //
    //  Allocation is only allowed on a file and not a directory
    //

    if (NodeType(Fcb) == FAT_NTC_DCB) {

        DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0);

        return STATUS_INVALID_DEVICE_REQUEST;
    }

    //
    //  Check that the new file allocation is legal
    //

    if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0 )) {

        DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);

        return STATUS_DISK_FULL;
    }

    //
    //  If we haven't yet looked up the correct AllocationSize, do so.
    //

    if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {

        FatLookupFileAllocationSize( IrpContext, Fcb );
    }

    //
    //  This is kinda gross, but if the file is not cached, but there is
    //  a data section, we have to cache the file to avoid a bunch of
    //  extra work.
    //

    if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
        (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
        !FlagOn(Irp->Flags, IRP_PAGING_IO)) {

        ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );

        //
        //  Now initialize the cache map.
        //

        CcInitializeCacheMap( FileObject,
                              (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
                              FALSE,
                              &FatData.CacheManagerCallbacks,
                              Fcb );

        CacheMapInitialized = TRUE;
    }

    //
    //  Now mark the fact that the file needs to be truncated on close
    //

    Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;

    //
    //  Now mark that the time on the dirent needs to be updated on close.
    //

    SetFlag( FileObject->Flags, FO_FILE_MODIFIED );

    try {

        //
        //  Increase or decrease the allocation size.
        //

        if (NewAllocationSize > Fcb->Header.AllocationSize.LowPart) {

            FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize);

        } else {

            //
            //  Check here if we will be decreasing file size and synchonize with
            //  paging IO.
            //

            if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) {

                //
                //  Before we actually truncate, check to see if the purge
                //  is going to fail.
                //

                if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
                                           &Buffer->AllocationSize )) {

                    try_return( Status = STATUS_USER_MAPPED_FILE );
                }

                FileSizeTruncated = TRUE;

                OriginalFileSize = Fcb->Header.FileSize.LowPart;
                OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart;
                OriginalValidDataToDisk = Fcb->ValidDataToDisk;

                (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
                ResourceAcquired = TRUE;

                Fcb->Header.FileSize.LowPart = NewAllocationSize;

                //
                //  If we reduced the file size to less than the ValidDataLength,
                //  adjust the VDL.  Likewise ValidDataToDisk.
                //

                if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) {

                    Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart;
                }
                if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) {

                    Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart;
                }

            }

            //
            //  Now that File Size is down, actually do the truncate.
            //

            FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize);

            //
            //  Now check if we needed to decrease the file size accordingly.
            //

            if ( FileSizeTruncated ) {

                //
                //  Tell the cache manager we reduced the file size.
                //  The call is unconditional, because MM always wants to know.
                //

#if DBG
                try {
#endif
                
                    CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );

#if DBG
                } except(FatBugCheckExceptionFilter( GetExceptionInformation() )) {

                      NOTHING;
                }
#endif

                ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );

                //
                //  There is no going back from this. If we run into problems updating
                //  the dirent we will have to live with the consequences. Not sending
                //  the notifies is likewise pretty benign compared to failing the entire
                //  operation and trying to back out everything, which could fail for the
                //  same reasons.
                //
                //  If you want a transacted filesystem, use NTFS ...
                //

                FileSizeTruncated = FALSE;

                FatSetFileSizeInDirent( IrpContext, Fcb, NULL );

                //
                //  Report that we just reduced the file size.
                //

                FatNotifyReportChange( IrpContext,
                                       Fcb->Vcb,
                                       Fcb,
                                       FILE_NOTIFY_CHANGE_SIZE,
                                       FILE_ACTION_MODIFIED );
            }
        }

    try_exit: NOTHING;

    } finally {

        if ( AbnormalTermination() && FileSizeTruncated ) {

            Fcb->Header.FileSize.LowPart = OriginalFileSize;
            Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength;
            Fcb->ValidDataToDisk = OriginalValidDataToDisk;

            //
            //  Make sure Cc knows the right filesize.
            //

            if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {

                *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
            }

            ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart );
        }

        if (CacheMapInitialized) {

            CcUninitializeCacheMap( FileObject, NULL, NULL );
        }

        if (ResourceAcquired) {

            ExReleaseResourceLite( Fcb->Header.PagingIoResource );

        }
        
    }

    DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);

    return Status;
}


//
//  Internal Support Routine
//

NTSTATUS
FatSetEndOfFileInfo (
    IN PIRP_CONTEXT IrpContext,
    IN PIRP Irp,
    IN PFILE_OBJECT FileObject,
    IN PVCB Vcb,
    IN PFCB Fcb
    )

/*++

Routine Description:

    This routine performs the set End of File information for fat.  It either
    completes the request or enqueues it off to the fsp.

Arguments:

    Irp - Supplies the irp being processed

    FileObject - Supplies the file object being processed

    Vcb - Supplies the Vcb being processed

    Fcb - Supplies the Fcb or Dcb being processed, already known not to
        be the root dcb

Return Value:

    NTSTATUS - The result of this operation if it completes without
               an exception.

--*/

{
    NTSTATUS Status;

    PFILE_END_OF_FILE_INFORMATION Buffer;

    ULONG NewFileSize;
    ULONG InitialFileSize;
    ULONG InitialValidDataLength;
    ULONG InitialValidDataToDisk;

    BOOLEAN CacheMapInitialized = FALSE;
    BOOLEAN UnwindFileSizes = FALSE;
    BOOLEAN ResourceAcquired = FALSE;

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

    Buffer = Irp->AssociatedIrp.SystemBuffer;

    try {

        //
        //  File Size changes are only allowed on a file and not a directory
        //

        if (NodeType(Fcb) != FAT_NTC_FCB) {

            DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);

            try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
        }

        //
        //  Check that the new file size is legal
        //

        if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0 )) {

            DebugTrace(0, Dbg, "Illegal allocation size\n", 0);

            try_return( Status = STATUS_DISK_FULL );
        }

        NewFileSize = Buffer->EndOfFile.LowPart;

        //
        //  If we haven't yet looked up the correct AllocationSize, do so.
        //

        if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {

            FatLookupFileAllocationSize( IrpContext, Fcb );
        }

        //
        //  This is kinda gross, but if the file is not cached, but there is
        //  a data section, we have to cache the file to avoid a bunch of
        //  extra work.
        //

        if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
            (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
            !FlagOn(Irp->Flags, IRP_PAGING_IO)) {

            if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ))  {

                //
                //  This IRP has raced (and lost) with a close (=>cleanup)
                //  on the same fileobject.  We don't want to reinitialise the
                //  cachemap here now because we'll leak it (unless we do so &
                //  then tear it down again here,  which is too much of a change at
                //  this stage).   So we'll just say the file is closed - which
                //  is arguably the right thing to do anyway,  since a caller
                //  racing operations in this way is broken.  The only stumbling
                //  block is possibly filters - do they operate on cleaned
                //  up fileobjects?
                //

                FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED);
            }

            //
            //  Now initialize the cache map.
            //

            CcInitializeCacheMap( FileObject,
                                  (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
                                  FALSE,
                                  &FatData.CacheManagerCallbacks,
                                  Fcb );

            CacheMapInitialized = TRUE;
        }

        //
        //  Do a special case here for the lazy write of file sizes.
        //

        if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {

            //
            //  Only attempt this if the file hasn't been "deleted on close" and
            //  this is a good FCB.
            //

            if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) {

                PDIRENT Dirent;
                PBCB DirentBcb;

                //
                //  Never have the dirent filesize larger than the fcb filesize
                //

                if (NewFileSize >= Fcb->Header.FileSize.LowPart) {

                    NewFileSize = Fcb->Header.FileSize.LowPart;
                }

                //
                //  Make sure we don't set anything higher than the alloc size.
                //

                ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );

                //
                //  Only advance the file size, never reduce it with this call
                //

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

                ASSERT( Dirent && DirentBcb );

                try {

                    if ( NewFileSize > Dirent->FileSize ) {

                        Dirent->FileSize = NewFileSize;

                        FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );

                        //
                        //  Report that we just changed the file size.
                        //

                        FatNotifyReportChange( IrpContext,
                                               Vcb,
                                               Fcb,
                                               FILE_NOTIFY_CHANGE_SIZE,
                                               FILE_ACTION_MODIFIED );
                    }

                } finally {

                    FatUnpinBcb( IrpContext, DirentBcb );
                }

            } else {

                DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0);
            }

            try_return( Status = STATUS_SUCCESS );
        }

        //
        //  Check if the new file size is greater than the current
        //  allocation size.  If it is then we need to increase the
        //  allocation size.
        //

        if ( NewFileSize > Fcb->Header.AllocationSize.LowPart ) {

            //
            //  Change the file allocation
            //

            FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize );
        }

        //
        //  At this point we have enough allocation for the file.
        //  So check if we are really changing the file size
        //

        if (Fcb->Header.FileSize.LowPart != NewFileSize) {

            if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {

                //
                //  Before we actually truncate, check to see if the purge
                //  is going to fail.
                //

                if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
                                           &Buffer->EndOfFile )) {

                    try_return( Status = STATUS_USER_MAPPED_FILE );
                }

                //
                //  This call is unconditional, because MM always wants to know.
                //  Also serialize here with paging io since we are truncating
                //  the file size.
                //

                ResourceAcquired =
                    ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
            }

            //
            //  Set the new file size
            //

            InitialFileSize = Fcb->Header.FileSize.LowPart;
            InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart;
            InitialValidDataToDisk = Fcb->ValidDataToDisk;
            UnwindFileSizes = TRUE;

            Fcb->Header.FileSize.LowPart = NewFileSize;

            //
            //  If we reduced the file size to less than the ValidDataLength,
            //  adjust the VDL.  Likewise ValidDataToDisk.
            //

            if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) {

                Fcb->Header.ValidDataLength.LowPart = NewFileSize;
            }

            if (Fcb->ValidDataToDisk > NewFileSize) {

                Fcb->ValidDataToDisk = NewFileSize;
            }

            DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize);

            //
            //  We must now update the cache mapping (benign if not cached).
            //

            CcSetFileSizes( FileObject,
                            (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );

            FatSetFileSizeInDirent( IrpContext, Fcb, NULL );

            //
            //  Report that we just changed the file size.
            //

            FatNotifyReportChange( IrpContext,
                                   Vcb,
                                   Fcb,
                                   FILE_NOTIFY_CHANGE_SIZE,
                                   FILE_ACTION_MODIFIED );

            //
            //  Mark the fact that the file will need to checked for
            //  truncation on cleanup.
            //

            SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE );
        }

        //
        //  Set this handle as having modified the file
        //

        FileObject->Flags |= FO_FILE_MODIFIED;

        //
        //  Set our return status to success
        //

        Status = STATUS_SUCCESS;

    try_exit: NOTHING;

        FatUnpinRepinnedBcbs( IrpContext );

    } finally {

        DebugUnwind( FatSetEndOfFileInfo );

        if (AbnormalTermination() && UnwindFileSizes) {

            Fcb->Header.FileSize.LowPart = InitialFileSize;
            Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
            Fcb->ValidDataToDisk = InitialValidDataToDisk;

            if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {

                *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
            }
        }

        if (CacheMapInitialized) {

            CcUninitializeCacheMap( FileObject, NULL, NULL );
        }

        if ( ResourceAcquired ) {

            ExReleaseResourceLite( Fcb->Header.PagingIoResource );
        }

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

    return Status;
}


//
//  Internal Support Routine
//

VOID
FatDeleteFile (
    IN PIRP_CONTEXT IrpContext,
    IN PDCB TargetDcb,
    IN ULONG LfnOffset,
    IN ULONG DirentOffset,
    IN PDIRENT Dirent,
    IN PUNICODE_STRING Lfn
    )
{
    PFCB Fcb;
    PLIST_ENTRY Links;

    //
    //  We can do the replace by removing the other Fcb(s) from
    //  the prefix table.
    //

    for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
         Links != &TargetDcb->Specific.Dcb.ParentDcbQueue;
         Links = Links->Flink) {

        Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );

        if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) &&
            (Fcb->DirentOffsetWithinDirectory == DirentOffset)) {

            ASSERT( NodeType(Fcb) == FAT_NTC_FCB );
            ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );

            if ( Fcb->UncleanCount != 0 ) {

                FatBugCheck(0,0,0);

            } else {

                PERESOURCE Resource;

                //
                //  Make this fcb "appear" deleted, synchronizing with
                //  paging IO.
                //

                FatRemoveNames( IrpContext, Fcb );

                Resource = Fcb->Header.PagingIoResource;

                (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE );

                SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);

                Fcb->ValidDataToDisk = 0;
                Fcb->Header.FileSize.QuadPart =
                Fcb->Header.ValidDataLength.QuadPart = 0;

                Fcb->FirstClusterOfFile = 0;

                ExReleaseResourceLite( Resource );
            }
        }
    }

    //
    //  The file is not currently opened so we can delete the file
    //  that is being overwritten.  To do the operation we dummy
    //  up an fcb, truncate allocation, delete the fcb, and delete
    //  the dirent.
    //

    Fcb = FatCreateFcb( IrpContext,
                        TargetDcb->Vcb,
                        TargetDcb,
                        LfnOffset,
                        DirentOffset,
                        Dirent,
                        Lfn,
                        FALSE,
                        FALSE );

    Fcb->Header.FileSize.LowPart = 0;

    try {

        FatTruncateFileAllocation( IrpContext, Fcb, 0 );

        FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );

    } finally {

        FatDeleteFcb( IrpContext, Fcb );
    }
}

//
//  Internal Support Routine
//

VOID
FatRenameEAs (
    IN PIRP_CONTEXT IrpContext,
    IN PFCB Fcb,
    IN USHORT ExtendedAttributes,
    IN POEM_STRING OldOemName
    )
{
    BOOLEAN LockedEaFcb = FALSE;

    PBCB EaBcb = NULL;
    PDIRENT EaDirent;
    EA_RANGE EaSetRange;
    PEA_SET_HEADER EaSetHeader;

    PVCB Vcb;

    RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));

    Vcb = Fcb->Vcb;

    try {

        //
        //  Use a try-except to catch any errors.
        //

        try {


            //
            //  Try to get the Ea file object.  Return FALSE on failure.
            //

            FatGetEaFile( IrpContext,
                          Vcb,
                          &EaDirent,
                          &EaBcb,
                          FALSE,
                          FALSE );

            LockedEaFcb = TRUE;

            //
            //  If we didn't get the file because it doesn't exist, then the
            //  disk is corrupted.  We do nothing here.
            //

            if (Vcb->VirtualEaFile != NULL) {

                //
                //  Try to pin down the Ea set header for the index in the
                //  dirent.  If the operation doesn't complete, return FALSE
                //  from this routine.
                //

                FatReadEaSet( IrpContext,
                              Vcb,
                              ExtendedAttributes,
                              OldOemName,
                              FALSE,
                              &EaSetRange );

                EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;

                //
                //  We now have the Ea set header for this file.  We simply
                //  overwrite the owning file name.
                //

                RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );

                RtlCopyMemory( EaSetHeader->OwnerFileName,
                               Fcb->ShortName.Name.Oem.Buffer,
                               Fcb->ShortName.Name.Oem.Length );

                FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
                FatUnpinEaRange( IrpContext, &EaSetRange );

                CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
            }

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

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

    } finally {

        //
        //  Unpin the EaDirent and the EaSetHeader if pinned.
        //

        FatUnpinBcb( IrpContext, EaBcb );
        FatUnpinEaRange( IrpContext, &EaSetRange );

        //
        //  Release the Fcb for the Ea file if locked.
        //

        if (LockedEaFcb) {

            FatReleaseFcb( IrpContext, Vcb->EaFcb );
        }
    }

    return;
}
