/*
 * COPYRIGHT:        See COPYRIGHT.TXT
 * PROJECT:          Ext2 File System Driver for WinNT/2K/XP
 * FILE:             fileinfo.c
 * PROGRAMMER:       Matt Wu <mattwu@163.com>
 * HOMEPAGE:         http://www.ext2fsd.com
 * UPDATE HISTORY:
 */

/* INCLUDES *****************************************************************/

#include "ext2fs.h"
#include "linux\ext4.h"
#include "linux\ext4_xattr.h"

/* GLOBALS ***************************************************************/

extern PEXT2_GLOBAL Ext2Global;

/* DEFINITIONS *************************************************************/

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, Ext2QueryFileInformation)
#pragma alloc_text(PAGE, Ext2SetFileInformation)
#pragma alloc_text(PAGE, Ext2ExpandFile)
#pragma alloc_text(PAGE, Ext2TruncateFile)
#pragma alloc_text(PAGE, Ext2SetDispositionInfo)
#pragma alloc_text(PAGE, Ext2SetRenameInfo)
#pragma alloc_text(PAGE, Ext2SetLinkInfo)
#pragma alloc_text(PAGE, Ext2DeleteFile)
#endif

static int Ext2IterateAllEa(struct ext4_xattr_ref *xattr_ref, struct ext4_xattr_item *item, BOOL is_last)
{
    PULONG EaSize = xattr_ref->iter_arg;
    ULONG EaEntrySize = 4 + 1 + 1 + 2 + item->name_len + 1 + item->data_size;

    *EaSize += EaEntrySize - 4;
    return EXT4_XATTR_ITERATE_CONT;
}

NTSTATUS
Ext2QueryFileInformation (IN PEXT2_IRP_CONTEXT IrpContext)
{
    PDEVICE_OBJECT          DeviceObject;
    NTSTATUS                Status = STATUS_UNSUCCESSFUL;
    PFILE_OBJECT            FileObject;
    PEXT2_VCB               Vcb = NULL;
    PEXT2_FCB               Fcb = NULL;
    PEXT2_MCB               Mcb = NULL;
    PEXT2_CCB               Ccb = NULL;
    PIRP                    Irp = NULL;
    PIO_STACK_LOCATION      IoStackLocation;
    FILE_INFORMATION_CLASS  FileInformationClass;
    ULONG                   Length;
    PVOID                   Buffer;
    BOOLEAN                 FcbResourceAcquired = FALSE;

    __try {

        ASSERT(IrpContext != NULL);
        ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
               (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT)));

        DeviceObject = IrpContext->DeviceObject;

        //
        // This request is not allowed on the main device object
        //
        if (IsExt2FsDevice(DeviceObject)) {
            Status = STATUS_INVALID_DEVICE_REQUEST;
            __leave;
        }

        FileObject = IrpContext->FileObject;
        Fcb = (PEXT2_FCB) FileObject->FsContext;
        if (Fcb == NULL) {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        //
        // This request is not allowed on volumes
        //
        if (Fcb->Identifier.Type == EXT2VCB) {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        if (!((Fcb->Identifier.Type == EXT2FCB) &&
                (Fcb->Identifier.Size == sizeof(EXT2_FCB)))) {
            Status = STATUS_INVALID_PARAMETER;
            __leave;
        }

        Vcb = Fcb->Vcb;

        {
            if (!ExAcquireResourceSharedLite(
                        &Fcb->MainResource,
                        IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)
                    )) {

                Status = STATUS_PENDING;
                __leave;
            }

            FcbResourceAcquired = TRUE;
        }

        Ccb = (PEXT2_CCB) FileObject->FsContext2;
        ASSERT(Ccb != NULL);
        ASSERT((Ccb->Identifier.Type == EXT2CCB) &&
               (Ccb->Identifier.Size == sizeof(EXT2_CCB)));
        Mcb = Ccb->SymLink;
        if (!Mcb)
            Mcb = Fcb->Mcb;

        Irp = IrpContext->Irp;
        IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
        FileInformationClass =
            IoStackLocation->Parameters.QueryFile.FileInformationClass;

        Length = IoStackLocation->Parameters.QueryFile.Length;
        Buffer = Irp->AssociatedIrp.SystemBuffer;
        RtlZeroMemory(Buffer, Length);

        switch (FileInformationClass) {

        case FileBasicInformation:
        {
            PFILE_BASIC_INFORMATION FileBasicInformation;

            if (Length < sizeof(FILE_BASIC_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FileBasicInformation = (PFILE_BASIC_INFORMATION) Buffer;

            FileBasicInformation->CreationTime = Mcb->CreationTime;
            FileBasicInformation->LastAccessTime = Mcb->LastAccessTime;
            FileBasicInformation->LastWriteTime = Mcb->LastWriteTime;
            FileBasicInformation->ChangeTime = Mcb->ChangeTime;

            FileBasicInformation->FileAttributes = Mcb->FileAttr;
            if (IsLinkInvalid(Mcb)) {
                ClearFlag(FileBasicInformation->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
            }
            if (FileBasicInformation->FileAttributes == 0) {
                FileBasicInformation->FileAttributes = FILE_ATTRIBUTE_NORMAL;
            }

            Irp->IoStatus.Information = sizeof(FILE_BASIC_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;

        case FileStandardInformation:
        {
            PFILE_STANDARD_INFORMATION FSI;

            if (Length < sizeof(FILE_STANDARD_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FSI = (PFILE_STANDARD_INFORMATION) Buffer;

            FSI->NumberOfLinks = Mcb->Inode.i_nlink;

            if (IsVcbReadOnly(Fcb->Vcb))
                FSI->DeletePending = FALSE;
            else
                FSI->DeletePending = IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING);

            if (IsLinkInvalid(Mcb)) {
                FSI->Directory = FALSE;
                FSI->AllocationSize.QuadPart = 0;
                FSI->EndOfFile.QuadPart = 0;
            } else if (IsMcbDirectory(Mcb)) {
                FSI->Directory = TRUE;
                FSI->AllocationSize.QuadPart = 0;
                FSI->EndOfFile.QuadPart = 0;
            } else {
                FSI->Directory = FALSE;
                FSI->AllocationSize = Fcb->Header.AllocationSize;
                FSI->EndOfFile = Fcb->Header.FileSize;
            }

            Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;

        case FileInternalInformation:
        {
            PFILE_INTERNAL_INFORMATION FileInternalInformation;

            if (Length < sizeof(FILE_INTERNAL_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FileInternalInformation = (PFILE_INTERNAL_INFORMATION) Buffer;

            /* we use the inode number as the internal index */
            FileInternalInformation->IndexNumber.QuadPart = (LONGLONG)Mcb->Inode.i_ino;

            Irp->IoStatus.Information = sizeof(FILE_INTERNAL_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;


        case FileEaInformation:
        {
            struct ext4_xattr_ref xattr_ref;
            PFILE_EA_INFORMATION FileEaInformation;

            if (Length < sizeof(FILE_EA_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FileEaInformation = (PFILE_EA_INFORMATION) Buffer;
            FileEaInformation->EaSize = 0;

            Status = Ext2WinntError(ext4_fs_get_xattr_ref(IrpContext, Vcb, Fcb->Mcb, &xattr_ref));
            if (!NT_SUCCESS(Status))
                __leave;

            xattr_ref.iter_arg = &FileEaInformation->EaSize;
            ext4_fs_xattr_iterate(&xattr_ref, Ext2IterateAllEa);
            ext4_fs_put_xattr_ref(&xattr_ref);

            if (FileEaInformation->EaSize)
                FileEaInformation->EaSize += 4;

            Irp->IoStatus.Information = sizeof(FILE_EA_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;

        case FileNameInformation:
        {
            PFILE_NAME_INFORMATION FileNameInformation;
            ULONG   BytesToCopy = 0;

            if (Length < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) +
                    Mcb->FullName.Length) {
                BytesToCopy = Length - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
                Status = STATUS_BUFFER_OVERFLOW;
            } else {
                BytesToCopy = Mcb->FullName.Length;
                Status = STATUS_SUCCESS;
            }

            FileNameInformation = (PFILE_NAME_INFORMATION) Buffer;
            FileNameInformation->FileNameLength = Mcb->FullName.Length;

            RtlCopyMemory(
                FileNameInformation->FileName,
                Mcb->FullName.Buffer,
                BytesToCopy );

            Irp->IoStatus.Information = BytesToCopy +
                                        + FIELD_OFFSET(FILE_NAME_INFORMATION, FileName);
        }
        break;

        case FilePositionInformation:
        {
            PFILE_POSITION_INFORMATION FilePositionInformation;

            if (Length < sizeof(FILE_POSITION_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FilePositionInformation = (PFILE_POSITION_INFORMATION) Buffer;
            FilePositionInformation->CurrentByteOffset =
                FileObject->CurrentByteOffset;

            Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;

        case FileAllInformation:
        {
            PFILE_ALL_INFORMATION       FileAllInformation;
            PFILE_BASIC_INFORMATION     FileBasicInformation;
            PFILE_STANDARD_INFORMATION  FSI;
            PFILE_INTERNAL_INFORMATION  FileInternalInformation;
            PFILE_EA_INFORMATION        FileEaInformation;
            PFILE_POSITION_INFORMATION  FilePositionInformation;
            PFILE_NAME_INFORMATION      FileNameInformation;

            if (Length < sizeof(FILE_ALL_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FileAllInformation = (PFILE_ALL_INFORMATION) Buffer;

            FileBasicInformation =
                &FileAllInformation->BasicInformation;

            FSI =
                &FileAllInformation->StandardInformation;

            FileInternalInformation =
                &FileAllInformation->InternalInformation;

            FileEaInformation =
                &FileAllInformation->EaInformation;

            FilePositionInformation =
                &FileAllInformation->PositionInformation;

            FileNameInformation =
                &FileAllInformation->NameInformation;

            FileBasicInformation->CreationTime = Mcb->CreationTime;
            FileBasicInformation->LastAccessTime = Mcb->LastAccessTime;
            FileBasicInformation->LastWriteTime = Mcb->LastWriteTime;
            FileBasicInformation->ChangeTime = Mcb->ChangeTime;

            FileBasicInformation->FileAttributes = Mcb->FileAttr;
            if (IsMcbSymLink(Mcb) && IsFileDeleted(Mcb->Target)) {
                ClearFlag(FileBasicInformation->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
            }
            if (FileBasicInformation->FileAttributes == 0) {
                FileBasicInformation->FileAttributes = FILE_ATTRIBUTE_NORMAL;
            }

            FSI->NumberOfLinks = Mcb->Inode.i_nlink;

            if (IsVcbReadOnly(Fcb->Vcb))
                FSI->DeletePending = FALSE;
            else
                FSI->DeletePending = IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING);

            if (IsLinkInvalid(Mcb)) {
                FSI->Directory = FALSE;
                FSI->AllocationSize.QuadPart = 0;
                FSI->EndOfFile.QuadPart = 0;
            } else if (IsDirectory(Fcb)) {
                FSI->Directory = TRUE;
                FSI->AllocationSize.QuadPart = 0;
                FSI->EndOfFile.QuadPart = 0;
            } else {
                FSI->Directory = FALSE;
                FSI->AllocationSize = Fcb->Header.AllocationSize;
                FSI->EndOfFile = Fcb->Header.FileSize;
            }

            // The "inode number"
            FileInternalInformation->IndexNumber.QuadPart = (LONGLONG)Mcb->Inode.i_ino;

            FileEaInformation->EaSize = 0;

            FilePositionInformation->CurrentByteOffset =
                FileObject->CurrentByteOffset;

            FileNameInformation->FileNameLength = Mcb->ShortName.Length;

            if (Length < sizeof(FILE_ALL_INFORMATION) +
                    Mcb->ShortName.Length - sizeof(WCHAR)) {
                Irp->IoStatus.Information = sizeof(FILE_ALL_INFORMATION);
                Status = STATUS_BUFFER_OVERFLOW;
                RtlCopyMemory(
                    FileNameInformation->FileName,
                    Mcb->ShortName.Buffer,
                    Length - FIELD_OFFSET(FILE_ALL_INFORMATION,
                                          NameInformation.FileName)
                );
                __leave;
            }

            RtlCopyMemory(
                FileNameInformation->FileName,
                Mcb->ShortName.Buffer,
                Mcb->ShortName.Length
            );

            Irp->IoStatus.Information = sizeof(FILE_ALL_INFORMATION) +
                                        Mcb->ShortName.Length - sizeof(WCHAR);

            Status = STATUS_SUCCESS;
        }
        break;

        /*
        case FileAlternateNameInformation:
        {
            // TODO: Handle FileAlternateNameInformation
        }
        */

        case FileNetworkOpenInformation:
        {
            PFILE_NETWORK_OPEN_INFORMATION PFNOI;

            if (Length < sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            PFNOI = (PFILE_NETWORK_OPEN_INFORMATION) Buffer;

            PFNOI->FileAttributes = Mcb->FileAttr;
            if (IsLinkInvalid(Mcb)) {
                ClearFlag(PFNOI->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
                PFNOI->AllocationSize.QuadPart = 0;
                PFNOI->EndOfFile.QuadPart = 0;
            } else if (IsDirectory(Fcb)) {
                PFNOI->AllocationSize.QuadPart = 0;
                PFNOI->EndOfFile.QuadPart = 0;
            } else {
                PFNOI->AllocationSize = Fcb->Header.AllocationSize;
                PFNOI->EndOfFile      = Fcb->Header.FileSize;
            }

            if (PFNOI->FileAttributes == 0) {
                PFNOI->FileAttributes = FILE_ATTRIBUTE_NORMAL;
            }

            PFNOI->CreationTime   = Mcb->CreationTime;
            PFNOI->LastAccessTime = Mcb->LastAccessTime;
            PFNOI->LastWriteTime  = Mcb->LastWriteTime;
            PFNOI->ChangeTime     = Mcb->ChangeTime;


            Irp->IoStatus.Information =
                sizeof(FILE_NETWORK_OPEN_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;

#if (_WIN32_WINNT >= 0x0500)

        case FileAttributeTagInformation:
        {
            PFILE_ATTRIBUTE_TAG_INFORMATION FATI;

            if (Length < sizeof(FILE_ATTRIBUTE_TAG_INFORMATION)) {
                Status = STATUS_BUFFER_OVERFLOW;
                __leave;
            }

            FATI = (PFILE_ATTRIBUTE_TAG_INFORMATION) Buffer;
            FATI->FileAttributes = Mcb->FileAttr;
            if (IsLinkInvalid(Mcb)) {
                ClearFlag(FATI->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
            }
            if (FATI->FileAttributes == 0) {
                FATI->FileAttributes = FILE_ATTRIBUTE_NORMAL;
            }
            FATI->ReparseTag = IO_REPARSE_TAG_RESERVED_ZERO;
            Irp->IoStatus.Information = sizeof(FILE_ATTRIBUTE_TAG_INFORMATION);
            Status = STATUS_SUCCESS;
        }
        break;
#endif // (_WIN32_WINNT >= 0x0500)

        case FileStreamInformation:
            Status = STATUS_INVALID_PARAMETER;
            break;

        default:
            DEBUG(DL_WRN, ( "Ext2QueryInformation: invalid class: %d\n",
                            FileInformationClass));
            Status = STATUS_INVALID_PARAMETER; /* STATUS_INVALID_INFO_CLASS; */
            break;
        }

    } __finally {

        if (FcbResourceAcquired) {
            ExReleaseResourceLite(&Fcb->MainResource);
        }

        if (!IrpContext->ExceptionInProgress) {
            if (Status == STATUS_PENDING ||
                    Status == STATUS_CANT_WAIT) {
                Status = Ext2QueueRequest(IrpContext);
            } else {
                Ext2CompleteIrpContext(IrpContext,  Status);
            }
        }
    }

    return Status;
}

NTSTATUS
Ext2SetFileInformation (IN PEXT2_IRP_CONTEXT IrpContext)
{
    PDEVICE_OBJECT          DeviceObject;
    NTSTATUS                Status = STATUS_UNSUCCESSFUL;
    PEXT2_VCB               Vcb = NULL;
    PFILE_OBJECT            FileObject = NULL;
    PEXT2_FCB               Fcb = NULL;
    PEXT2_CCB               Ccb = NULL;
    PEXT2_MCB               Mcb = NULL;
    PIRP                    Irp = NULL;
    PIO_STACK_LOCATION      IoStackLocation = NULL;
    FILE_INFORMATION_CLASS  FileInformationClass;

    ULONG                   NotifyFilter = 0;

    ULONG                   Length;
    PVOID                   Buffer;

    BOOLEAN                 FcbMainResourceAcquired = FALSE;
    BOOLEAN                 FcbPagingIoResourceAcquired = FALSE;

    __try {

        ASSERT(IrpContext != NULL);

        ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
               (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT)));
        DeviceObject = IrpContext->DeviceObject;

        //
        // This request is not allowed on the main device object
        //
        if (IsExt2FsDevice(DeviceObject)) {
            Status = STATUS_INVALID_DEVICE_REQUEST;
            __leave;
        }

        /* check io stack location of irp stack */
        Irp = IrpContext->Irp;
        IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
        FileInformationClass =
            IoStackLocation->Parameters.SetFile.FileInformationClass;
        Length = IoStackLocation->Parameters.SetFile.Length;
        Buffer = Irp->AssociatedIrp.SystemBuffer;

        /* check Vcb */
        Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension;
        ASSERT(Vcb != NULL);
        ASSERT((Vcb->Identifier.Type == EXT2VCB) &&
               (Vcb->Identifier.Size == sizeof(EXT2_VCB)));
        if (!IsMounted(Vcb)) {
            Status = STATUS_INVALID_DEVICE_REQUEST;
            __leave;
        }

        if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) {
            Status = STATUS_ACCESS_DENIED;
            __leave;
        }

        FileObject = IrpContext->FileObject;
        Fcb = (PEXT2_FCB) FileObject->FsContext;

        // This request is issued to volumes, just return success
        if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) {
            Status = STATUS_SUCCESS;
            __leave;
        }
        ASSERT((Fcb->Identifier.Type == EXT2FCB) &&
               (Fcb->Identifier.Size == sizeof(EXT2_FCB)));

        if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) {
            Status = STATUS_FILE_DELETED;
            __leave;
        }

        Ccb = (PEXT2_CCB) FileObject->FsContext2;
        ASSERT(Ccb != NULL);
        ASSERT((Ccb->Identifier.Type == EXT2CCB) &&
               (Ccb->Identifier.Size == sizeof(EXT2_CCB)));
        Mcb = Ccb->SymLink;
        if (Mcb) {
            if (IsFlagOn(Mcb->Flags, MCB_FILE_DELETED)) {
                Status = STATUS_FILE_DELETED;
                __leave;
            }
        } else {
            Mcb = Fcb->Mcb;
        }

        if (FileInformationClass != FilePositionInformation) {
            if (IsVcbReadOnly(Vcb)) {
                Status = STATUS_MEDIA_WRITE_PROTECTED;
                __leave;
            }
            if (!Ext2CheckFileAccess(Vcb, Mcb, Ext2FileCanWrite)) {
                Status = STATUS_ACCESS_DENIED;
                __leave;
            }
        }

        if ( !IsDirectory(Fcb) && !FlagOn(Fcb->Flags, FCB_PAGE_FILE) &&
                ((FileInformationClass == FileEndOfFileInformation) ||
                 (FileInformationClass == FileValidDataLengthInformation) ||
                 (FileInformationClass == FileAllocationInformation))) {

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

            if (Status != STATUS_SUCCESS) {
                __leave;
            }

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

            Fcb->Header.IsFastIoPossible = Ext2IsFastIoPossible(Fcb);
        }

        /* for renaming or set link, we must not grab any Fcb locks,
           and later we will get Dcb or Fcb resources exclusively.  */
        if (!IsFlagOn(Fcb->Flags, FCB_PAGE_FILE) &&
            FileInformationClass != FileRenameInformation &&
            FileInformationClass != FileLinkInformation) {

            if (!ExAcquireResourceExclusiveLite(
                        &Fcb->MainResource,
                        IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) {
                Status = STATUS_PENDING;
                __leave;
            }

            FcbMainResourceAcquired = TRUE;

            if ( FileInformationClass == FileAllocationInformation ||
                 FileInformationClass == FileEndOfFileInformation ||
                 FileInformationClass == FileValidDataLengthInformation) {

                if (!ExAcquireResourceExclusiveLite(
                            &Fcb->PagingIoResource,
                            IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) {
                    Status = STATUS_PENDING;
                    DbgBreak();
                    __leave;
                }
                FcbPagingIoResourceAcquired = TRUE;
            }
        }

        switch (FileInformationClass) {

        case FileBasicInformation:
        {
            PFILE_BASIC_INFORMATION FBI = (PFILE_BASIC_INFORMATION) Buffer;
            struct inode *Inode = &Mcb->Inode;

            if (FBI->CreationTime.QuadPart != 0 && FBI->CreationTime.QuadPart != -1) {
                Ext2SetInodeTime(&FBI->CreationTime, &Inode->i_crtime, &Inode->i_crtime_extra);
                Mcb->CreationTime = Ext2GetInodeTime(Inode->i_crtime, Inode->i_crtime_extra);
                NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
            }

            if (FBI->LastAccessTime.QuadPart != 0 && FBI->LastAccessTime.QuadPart != -1) {
                Ext2SetInodeTime(&FBI->LastAccessTime, &Inode->i_atime, &Inode->i_atime_extra);
                Mcb->LastAccessTime = Ext2GetInodeTime(Inode->i_atime, Inode->i_atime_extra);
                NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
            }

            if (FBI->LastWriteTime.QuadPart != 0 && FBI->LastWriteTime.QuadPart != -1) {
                Ext2SetInodeTime(&FBI->LastWriteTime, &Inode->i_mtime, &Inode->i_mtime_extra);
                Mcb->LastWriteTime = Ext2GetInodeTime(Inode->i_mtime, Inode->i_mtime_extra);
                NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
                SetFlag(Ccb->Flags, CCB_LAST_WRITE_UPDATED);
            }

            if (FBI->ChangeTime.QuadPart !=0 && FBI->ChangeTime.QuadPart != -1) {
                Ext2SetInodeTime(&FBI->ChangeTime, &Inode->i_ctime, &Inode->i_ctime_extra);
                Mcb->ChangeTime = Ext2GetInodeTime(Inode->i_ctime, Inode->i_ctime_extra);
                NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
            }

            if (FBI->FileAttributes != 0) {

                BOOLEAN bIsDirectory = IsDirectory(Fcb);
                NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;

                if (IsFlagOn(FBI->FileAttributes, FILE_ATTRIBUTE_READONLY)) {
                    Ext2SetOwnerReadOnly(Inode->i_mode);
                } else {
                    Ext2SetOwnerWritable(Inode->i_mode);
                }

                if (FBI->FileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
                    SetFlag(FileObject->Flags, FO_TEMPORARY_FILE);
                } else {
                    ClearFlag(FileObject->Flags, FO_TEMPORARY_FILE);
                }

                Mcb->FileAttr = FBI->FileAttributes;
                if (bIsDirectory) {
                    SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY);
                    ClearFlag(Mcb->FileAttr, FILE_ATTRIBUTE_NORMAL);
                }
            }

            if (NotifyFilter != 0) {
                if (Ext2SaveInode(IrpContext, Vcb, Inode)) {
                    Status = STATUS_SUCCESS;
                }
            }

            ClearFlag(NotifyFilter, FILE_NOTIFY_CHANGE_LAST_ACCESS);
            Status = STATUS_SUCCESS;
        }

        break;

        case FileAllocationInformation:
        {
            PFILE_ALLOCATION_INFORMATION FAI = (PFILE_ALLOCATION_INFORMATION)Buffer;
            LARGE_INTEGER  AllocationSize;

            if (IsMcbDirectory(Mcb) || IsMcbSpecialFile(Mcb)) {
                Status = STATUS_INVALID_DEVICE_REQUEST;
                __leave;
            } else {
                Status = STATUS_SUCCESS;
            }

            /* set Mcb to it's target */
            if (IsMcbSymLink(Mcb)) {
                ASSERT(Fcb->Mcb == Mcb->Target);
            }
            Mcb = Fcb->Mcb;

            /* get user specified allocationsize aligned with BLOCK_SIZE */
            AllocationSize.QuadPart = CEILING_ALIGNED(ULONGLONG,
                                      (ULONGLONG)FAI->AllocationSize.QuadPart,
                                      (ULONGLONG)BLOCK_SIZE);

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

                Status = Ext2ExpandFile(IrpContext, Vcb, Mcb, &AllocationSize); 
                Fcb->Header.AllocationSize = AllocationSize;
                NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
                SetLongFlag(Fcb->Flags, FCB_ALLOC_IN_SETINFO);

            } else if (AllocationSize.QuadPart < Fcb->Header.AllocationSize.QuadPart) {

                if (MmCanFileBeTruncated(&(Fcb->SectionObject), &AllocationSize))  {

                    /* truncate file blocks */
                    Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &AllocationSize);

                    if (NT_SUCCESS(Status)) {
                        ClearLongFlag(Fcb->Flags, FCB_ALLOC_IN_CREATE);
                    }

                    NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
                    Fcb->Header.AllocationSize.QuadPart = AllocationSize.QuadPart;
                    if (Mcb->Inode.i_size > (loff_t)AllocationSize.QuadPart) {
                        Mcb->Inode.i_size = AllocationSize.QuadPart;
                    }
                    Fcb->Header.FileSize.QuadPart = Mcb->Inode.i_size;
                    if (Fcb->Header.ValidDataLength.QuadPart > Fcb->Header.FileSize.QuadPart) {
                        Fcb->Header.ValidDataLength.QuadPart = Fcb->Header.FileSize.QuadPart;
                    }

                } else {

                    Status = STATUS_USER_MAPPED_FILE;
                    DbgBreak();
                    __leave;
                }
            }

            if (NotifyFilter) {

                SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
                SetLongFlag(Fcb->Flags, FCB_FILE_MODIFIED);
                Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode);
                if (CcIsFileCached(FileObject)) {
                    CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
                }
            }

            DEBUG(DL_IO, ("Ext2SetInformation: %wZ NewSize=%I64xh AllocationSize=%I64xh "
                          "FileSize=%I64xh VDL=%I64xh i_size=%I64xh status = %xh\n",
                          &Fcb->Mcb->ShortName, AllocationSize.QuadPart,
                          Fcb->Header.AllocationSize.QuadPart,
                          Fcb->Header.FileSize.QuadPart, Fcb->Header.ValidDataLength.QuadPart,
                          Mcb->Inode.i_size, Status));
        }

        break;

        case FileEndOfFileInformation:
        {
            PFILE_END_OF_FILE_INFORMATION FEOFI = (PFILE_END_OF_FILE_INFORMATION) Buffer;
            LARGE_INTEGER NewSize, OldSize, EndOfFile;

            if (IsMcbDirectory(Mcb) || IsMcbSpecialFile(Mcb)) {
                Status = STATUS_INVALID_DEVICE_REQUEST;
                __leave;
            } else {
                Status = STATUS_SUCCESS;
            }

            /* set Mcb to it's target */
            if (IsMcbSymLink(Mcb)) {
                ASSERT(Fcb->Mcb == Mcb->Target);
            }
            Mcb = Fcb->Mcb;

            OldSize = Fcb->Header.AllocationSize;
            EndOfFile = FEOFI->EndOfFile;

            if (IoStackLocation->Parameters.SetFile.AdvanceOnly) {

                if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING)) {
                    __leave;
                }

                if (EndOfFile.QuadPart > Fcb->Header.FileSize.QuadPart) {
                    EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart;
                }

                if (EndOfFile.QuadPart > Fcb->Header.ValidDataLength.QuadPart) {
                    Fcb->Header.ValidDataLength.QuadPart = EndOfFile.QuadPart;
                    NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
                }

                __leave;
            }

            NewSize.QuadPart = CEILING_ALIGNED(ULONGLONG,
                                               EndOfFile.QuadPart, BLOCK_SIZE);

            if (NewSize.QuadPart > OldSize.QuadPart) {

                Fcb->Header.AllocationSize = NewSize;
                Status = Ext2ExpandFile(
                                 IrpContext,
                                 Vcb,
                                 Mcb,
                                 &(Fcb->Header.AllocationSize)
                             );
                NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
                SetLongFlag(Fcb->Flags, FCB_ALLOC_IN_SETINFO);


            } else if (NewSize.QuadPart == OldSize.QuadPart) {

                /* we are luck ;) */
                Status = STATUS_SUCCESS;

            } else {

                /* don't truncate file data since it's still being written */
                if (IsFlagOn(Fcb->Flags, FCB_ALLOC_IN_WRITE)) {

                    Status = STATUS_SUCCESS;

                } else {

                    if (!MmCanFileBeTruncated(&(Fcb->SectionObject), &NewSize)) {
                        Status = STATUS_USER_MAPPED_FILE;
                        DbgBreak();
                        __leave;
                    }

                    /* truncate file blocks */
                    Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &NewSize);

                    /* restore original file size */
                    if (NT_SUCCESS(Status)) {
                        ClearLongFlag(Fcb->Flags, FCB_ALLOC_IN_CREATE);
                    }

                    /* update file allocateion size */
                    Fcb->Header.AllocationSize.QuadPart = NewSize.QuadPart;

                    ASSERT((loff_t)NewSize.QuadPart >= Mcb->Inode.i_size);
                    if ((loff_t)Fcb->Header.FileSize.QuadPart < Mcb->Inode.i_size) {
                        Fcb->Header.FileSize.QuadPart = Mcb->Inode.i_size;
                    }
                    if (Fcb->Header.ValidDataLength.QuadPart > Fcb->Header.FileSize.QuadPart) {
                        Fcb->Header.ValidDataLength.QuadPart = Fcb->Header.FileSize.QuadPart;
                    }

                    SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
                    SetLongFlag(Fcb->Flags, FCB_FILE_MODIFIED);
                }

                NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
            }

            if (NT_SUCCESS(Status)) {

                Fcb->Header.FileSize.QuadPart = Mcb->Inode.i_size = EndOfFile.QuadPart;
                if (CcIsFileCached(FileObject)) {
                    CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
                }

                if (Fcb->Header.FileSize.QuadPart >= 0x80000000 &&
                        !IsFlagOn(SUPER_BLOCK->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
                    SetFlag(SUPER_BLOCK->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
                    Ext2SaveSuper(IrpContext, Vcb);
                }

                SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
                SetLongFlag(Fcb->Flags, FCB_FILE_MODIFIED);
                NotifyFilter = FILE_NOTIFY_CHANGE_SIZE;
            }


            Ext2SaveInode( IrpContext, Vcb, &Mcb->Inode);

            DEBUG(DL_IO, ("Ext2SetInformation: FileEndOfFileInformation %wZ EndofFile=%I64xh "
                          "AllocatieonSize=%I64xh FileSize=%I64xh VDL=%I64xh i_size=%I64xh status = %xh\n",
                          &Fcb->Mcb->ShortName, EndOfFile.QuadPart, Fcb->Header.AllocationSize.QuadPart,
                          Fcb->Header.FileSize.QuadPart, Fcb->Header.ValidDataLength.QuadPart,
                          Mcb->Inode.i_size, Status));
        }

        break;

        case FileValidDataLengthInformation:
        {
            PFILE_VALID_DATA_LENGTH_INFORMATION FVDL = (PFILE_VALID_DATA_LENGTH_INFORMATION) Buffer;
            LARGE_INTEGER NewVDL;

            if (IsMcbDirectory(Mcb) || IsMcbSpecialFile(Mcb)) {
                Status = STATUS_INVALID_DEVICE_REQUEST;
                __leave;
            } else {
                Status = STATUS_SUCCESS;
            }

            NewVDL = FVDL->ValidDataLength;
            if ((NewVDL.QuadPart < Fcb->Header.ValidDataLength.QuadPart)) {
                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }
            if (NewVDL.QuadPart > Fcb->Header.FileSize.QuadPart)
                NewVDL = Fcb->Header.FileSize;

            if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
                                      &NewVDL)) {
                Status = STATUS_USER_MAPPED_FILE;
                __leave;
            }

            Fcb->Header.ValidDataLength = NewVDL;
            FileObject->Flags |= FO_FILE_MODIFIED;
            if (CcIsFileCached(FileObject)) {
                CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
            }
        }

        break;

        case FileDispositionInformation:
        {
            PFILE_DISPOSITION_INFORMATION FDI = (PFILE_DISPOSITION_INFORMATION)Buffer;

            Status = Ext2SetDispositionInfo(IrpContext, Vcb, Fcb, Ccb, FDI->DeleteFile);

            DEBUG(DL_INF, ( "Ext2SetInformation: SetDispositionInformation: DeleteFile=%d %wZ status = %xh\n",
                            FDI->DeleteFile, &Mcb->ShortName, Status));
        }

        break;

        case FileRenameInformation:
        {
            Status = Ext2SetRenameInfo(IrpContext, Vcb, Fcb, Ccb);
        }

        break;


        case FileLinkInformation:
        {
            Status = Ext2SetLinkInfo(IrpContext, Vcb, Fcb, Ccb);
        }

        break;

        //
        // This is the only set file information request supported on read
        // only file systems
        //
        case FilePositionInformation:
        {
            PFILE_POSITION_INFORMATION FilePositionInformation;

            if (Length < sizeof(FILE_POSITION_INFORMATION)) {
                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }

            FilePositionInformation = (PFILE_POSITION_INFORMATION) Buffer;

            if ((FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) &&
                    (FilePositionInformation->CurrentByteOffset.LowPart &
                     DeviceObject->AlignmentRequirement) ) {
                Status = STATUS_INVALID_PARAMETER;
                __leave;
            }

            FileObject->CurrentByteOffset =
                FilePositionInformation->CurrentByteOffset;

            Status = STATUS_SUCCESS;
            __leave;
        }

        break;

        default:
            DEBUG(DL_WRN, ( "Ext2SetInformation: invalid class: %d\n",
                            FileInformationClass));
            Status = STATUS_INVALID_PARAMETER;/* STATUS_INVALID_INFO_CLASS; */
        }

    } __finally {

        if (FcbPagingIoResourceAcquired) {
            ExReleaseResourceLite(&Fcb->PagingIoResource);
        }

        if (NT_SUCCESS(Status) && (NotifyFilter != 0)) {
            Ext2NotifyReportChange(
                IrpContext,
                Vcb,
                Mcb,
                NotifyFilter,
                FILE_ACTION_MODIFIED );

        }

        if (FcbMainResourceAcquired) {
            ExReleaseResourceLite(&Fcb->MainResource);
        }

        if (!IrpContext->ExceptionInProgress) {
            if (Status == STATUS_PENDING ||
                    Status == STATUS_CANT_WAIT ) {
                DbgBreak();
                Status = Ext2QueueRequest(IrpContext);
            } else {
                Ext2CompleteIrpContext(IrpContext,  Status);
            }
        }
    }

    return Status;
}

ULONG
Ext2TotalBlocks(
    PEXT2_VCB         Vcb,
    PLARGE_INTEGER    Size,
    PULONG            pMeta
)
{
    ULONG Blocks, Meta =0, Remain;

    Blocks = (ULONG)((Size->QuadPart + BLOCK_SIZE - 1) >> BLOCK_BITS);
    if (Blocks <= EXT2_NDIR_BLOCKS)
        goto errorout;
    Blocks -= EXT2_NDIR_BLOCKS;

    Meta += 1;
    if (Blocks <= Vcb->max_blocks_per_layer[1]) {
        goto errorout;
    }
    Blocks -= Vcb->max_blocks_per_layer[1];

level2:

    if (Blocks <= Vcb->max_blocks_per_layer[2]) {
        Meta += 1 + ((Blocks + BLOCK_SIZE/4 - 1) >> (BLOCK_BITS - 2));
        goto errorout;
    }
    Meta += 1 + BLOCK_SIZE/4;
    Blocks -= Vcb->max_blocks_per_layer[2];

    if (Blocks > Vcb->max_blocks_per_layer[3]) {
        Blocks  = Vcb->max_blocks_per_layer[3];
    }

    ASSERT(Vcb->max_blocks_per_layer[2]);
    Remain = Blocks % Vcb->max_blocks_per_layer[2];
    Blocks = Blocks / Vcb->max_blocks_per_layer[2];
    Meta += 1 + Blocks * (1 + BLOCK_SIZE/4);
    if (Remain) {
        Blocks = Remain;
        goto level2;
    }

errorout:

    if (pMeta)
        *pMeta = Meta;
    Blocks = (ULONG)((Size->QuadPart + BLOCK_SIZE - 1) >> BLOCK_BITS);
    return (Blocks + Meta);
}

NTSTATUS
Ext2BlockMap(
    IN PEXT2_IRP_CONTEXT    IrpContext,
    IN PEXT2_VCB            Vcb,
    IN PEXT2_MCB            Mcb,
    IN ULONG                Index,
    IN BOOLEAN              bAlloc,
    OUT PULONG              pBlock,
    OUT PULONG              Number
)
{
	NTSTATUS status;

	if (INODE_HAS_EXTENT(&Mcb->Inode)) {
        status = Ext2MapExtent(IrpContext, Vcb, Mcb, Index,
                               bAlloc, pBlock, Number );
	} else {
        status = Ext2MapIndirect(IrpContext, Vcb, Mcb, Index,
                                 bAlloc, pBlock, Number );
    }

	return status;
}

NTSTATUS
Ext2ExpandFile(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB         Vcb,
    PEXT2_MCB         Mcb,
    PLARGE_INTEGER    Size
)
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG    Start = 0;
    ULONG    End = 0;

    Start = (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS);
    End = (ULONG)((Size->QuadPart + BLOCK_SIZE - 1) >> BLOCK_BITS);

    /* it's a truncate operation, not expanding */
    if (Start >= End) {
        Size->QuadPart = ((LONGLONG) Start) << BLOCK_BITS;
        return STATUS_SUCCESS;
    }

	/* ignore special files */
	if (IsMcbSpecialFile(Mcb)) {
        return STATUS_INVALID_DEVICE_REQUEST;
    }

	/* expandind file extents */ 
    if (INODE_HAS_EXTENT(&Mcb->Inode)) {

        status = Ext2ExpandExtent(IrpContext, Vcb, Mcb, Start, End, Size);

    } else {

        BOOLEAN do_expand;

#if EXT2_PRE_ALLOCATION_SUPPORT
        do_expand = TRUE;
#else
        do_expand = (IrpContext->MajorFunction == IRP_MJ_WRITE) ||
                    IsMcbDirectory(Mcb);
#endif
        if (!do_expand)
            goto errorout;

        status = Ext2ExpandIndirect(IrpContext, Vcb, Mcb, Start, End, Size);
    }

errorout:
    return status;
}

NTSTATUS
Ext2TruncateFile(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB         Vcb,
    PEXT2_MCB         Mcb,
    PLARGE_INTEGER    Size
)
{
    NTSTATUS status = STATUS_SUCCESS;

    if (INODE_HAS_EXTENT(&Mcb->Inode)) {
		status = Ext2TruncateExtent(IrpContext, Vcb, Mcb, Size);
    } else {
		status = Ext2TruncateIndirect(IrpContext, Vcb, Mcb, Size);
	}

    /* check and clear data/meta mcb extents */
    if (Size->QuadPart == 0) {

        /* check and remove all data extents */
        if (Ext2ListExtents(&Mcb->Extents)) {
            DbgBreak();
        }
        Ext2ClearAllExtents(&Mcb->Extents);
        /* check and remove all meta extents */
        if (Ext2ListExtents(&Mcb->MetaExts)) {
            DbgBreak();
        }
        Ext2ClearAllExtents(&Mcb->MetaExts);
        ClearLongFlag(Mcb->Flags, MCB_ZONE_INITED);
    }

    return status;
}

NTSTATUS
Ext2IsFileRemovable(
    IN PEXT2_IRP_CONTEXT    IrpContext,
    IN PEXT2_VCB            Vcb,
    IN PEXT2_FCB            Fcb,
    IN PEXT2_CCB            Ccb
)
{
    PEXT2_MCB Mcb = Fcb->Mcb;

    if (Mcb->Inode.i_ino == EXT2_ROOT_INO) {
        return STATUS_CANNOT_DELETE;
    }

    if (IsMcbDirectory(Mcb)) {
        if (!Ext2IsDirectoryEmpty(IrpContext, Vcb, Mcb)) {
            return STATUS_DIRECTORY_NOT_EMPTY;
        }
    }

    if (!MmFlushImageSection(&Fcb->SectionObject,
                             MmFlushForDelete )) {
        return STATUS_CANNOT_DELETE;
    }

    if (IsMcbDirectory(Mcb)) {
        FsRtlNotifyFullChangeDirectory(
            Vcb->NotifySync,
            &Vcb->NotifyList,
            Ccb,
            NULL,
            FALSE,
            FALSE,
            0,
            NULL,
            NULL,
            NULL
        );
    }

    return STATUS_SUCCESS;
}

NTSTATUS
Ext2SetDispositionInfo(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB Vcb,
    PEXT2_FCB Fcb,
    PEXT2_CCB Ccb,
    BOOLEAN bDelete
)
{
    PIRP    Irp = IrpContext->Irp;
    PIO_STACK_LOCATION IrpSp;
    NTSTATUS status = STATUS_SUCCESS;
    PEXT2_MCB  Mcb = Fcb->Mcb;

    IrpSp = IoGetCurrentIrpStackLocation(Irp);

    DEBUG(DL_INF, ( "Ext2SetDispositionInfo: bDelete=%x\n", bDelete));

    if (bDelete) {

        DEBUG(DL_INF, ( "Ext2SetDispositionInformation: Removing %wZ.\n",
                        &Mcb->FullName));

        if (Ccb->SymLink || IsInodeSymLink(&Mcb->Inode)) {
            /* always allow deleting on symlinks */
        } else {
            status = Ext2IsFileRemovable(IrpContext, Vcb, Fcb, Ccb);
        }

        if (NT_SUCCESS(status)) {
            SetLongFlag(Fcb->Flags, FCB_DELETE_PENDING);
            IrpSp->FileObject->DeletePending = TRUE;
        }

    } else {

        ClearLongFlag(Fcb->Flags, FCB_DELETE_PENDING);
        IrpSp->FileObject->DeletePending = FALSE;
    }

    return status;
}

NTSTATUS
Ext2SetRenameInfo(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB         Vcb,
    PEXT2_FCB         Fcb,
    PEXT2_CCB         Ccb
)
{
    PEXT2_MCB               Mcb = Fcb->Mcb;

    PEXT2_FCB               TargetDcb = NULL;   /* Dcb of target directory */
    PEXT2_MCB               TargetMcb = NULL;
    PEXT2_FCB               ParentDcb = NULL;   /* Dcb of it's current parent */
    PEXT2_MCB               ParentMcb = NULL;

    PEXT2_FCB               ExistingFcb = NULL; /* Target file Fcb if it exists*/
    PEXT2_MCB               ExistingMcb = NULL;

    UNICODE_STRING          FileName;

    NTSTATUS                Status;

    PIRP                    Irp;
    PIO_STACK_LOCATION      IrpSp;

    PFILE_OBJECT            FileObject;
    PFILE_OBJECT            TargetObject;

    struct dentry          *NewEntry = NULL;

    BOOLEAN                 ReplaceIfExists;
    BOOLEAN                 bMove = FALSE;
    BOOLEAN                 bTargetRemoved = FALSE;

    BOOLEAN                 bFcbLockAcquired = FALSE;

    PFILE_RENAME_INFORMATION    FRI;

    if (Ccb->SymLink) {
        Mcb = Ccb->SymLink;
    }

    if (Mcb->Inode.i_ino == EXT2_ROOT_INO) {
        Status = STATUS_INVALID_PARAMETER;
        goto errorout;
    }

    Irp = IrpContext->Irp;
    IrpSp = IoGetCurrentIrpStackLocation(Irp);

    FileObject = IrpSp->FileObject;
    TargetObject = IrpSp->Parameters.SetFile.FileObject;
    ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;

    FRI = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

    if (TargetObject == NULL) {

        UNICODE_STRING  NewName;

        NewName.Buffer = FRI->FileName;
        NewName.MaximumLength = NewName.Length = (USHORT)FRI->FileNameLength;

        while (NewName.Length > 0 && NewName.Buffer[NewName.Length/2 - 1] == L'\\') {
            NewName.Buffer[NewName.Length/2 - 1] = 0;
            NewName.Length -= 2;
        }

        while (NewName.Length > 0 && NewName.Buffer[NewName.Length/2 - 1] != L'\\') {
            NewName.Length -= 2;
        }

        NewName.Buffer = (USHORT *)((UCHAR *)NewName.Buffer + NewName.Length);
        NewName.Length = (USHORT)(FRI->FileNameLength - NewName.Length);

        FileName = NewName;

        TargetMcb = Mcb->Parent;
        if (IsMcbSymLink(TargetMcb)) {
            TargetMcb = TargetMcb->Target;
            ASSERT(!IsMcbSymLink(TargetMcb));
        }

        if (TargetMcb == NULL || FileName.Length >= EXT2_NAME_LEN*2) {
            Status = STATUS_OBJECT_NAME_INVALID;
            goto errorout;
        }

    } else {

        TargetDcb = (PEXT2_FCB)(TargetObject->FsContext);

        if (!TargetDcb || TargetDcb->Vcb != Vcb) {

            DbgBreak();

            Status = STATUS_INVALID_PARAMETER;
            goto errorout;
        }

        TargetMcb = TargetDcb->Mcb;
        FileName = TargetObject->FileName;
    }

    if (FsRtlDoesNameContainWildCards(&FileName)) {
        Status = STATUS_OBJECT_NAME_INVALID;
        goto errorout;
    }

    if (TargetMcb->Inode.i_ino == Mcb->Parent->Inode.i_ino) {
        if (FsRtlAreNamesEqual( &FileName,
                                &(Mcb->ShortName),
                                FALSE,
                                NULL )) {
            Status = STATUS_SUCCESS;
            goto errorout;
        }
    } else {
        bMove = TRUE;
    }

    if (!bFcbLockAcquired) {
        ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE);
        bFcbLockAcquired = TRUE;
    }

    TargetDcb = TargetMcb->Fcb;
    if (TargetDcb == NULL) {
        TargetDcb = Ext2AllocateFcb(Vcb, TargetMcb);
    }
    if (TargetDcb) {
        Ext2ReferXcb(&TargetDcb->ReferenceCount);
    }

    ParentMcb = Mcb->Parent;
    ParentDcb = ParentMcb->Fcb;

    if ((TargetMcb->Inode.i_ino != ParentMcb->Inode.i_ino)) {

        if (ParentDcb == NULL) {
            ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb);
        }
    }
    if (ParentDcb) {
        Ext2ReferXcb(&ParentDcb->ReferenceCount);
    }

    if (bFcbLockAcquired) {
        ExReleaseResourceLite(&Vcb->FcbLock);
        bFcbLockAcquired = FALSE;
    }

    if (!TargetDcb || !ParentDcb) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto errorout;
    }

    DEBUG(DL_RES, ("Ext2SetRenameInfo: rename %wZ to %wZ\\%wZ\n",
                   &Mcb->FullName, &TargetMcb->FullName, &FileName));

    Status = Ext2LookupFile(
                 IrpContext,
                 Vcb,
                 &FileName,
                 TargetMcb,
                 &ExistingMcb,
                 0
             );

    if (NT_SUCCESS(Status) && ExistingMcb != Mcb) {

        if (!ReplaceIfExists) {

            Status = STATUS_OBJECT_NAME_COLLISION;
            DEBUG(DL_RES, ("Ext2SetRenameInfo: Target file %wZ exists\n",
                           &ExistingMcb->FullName));
            goto errorout;

        } else {

            if ( (ExistingFcb = ExistingMcb->Fcb) && !IsMcbSymLink(ExistingMcb) ) {

                Status = Ext2IsFileRemovable(IrpContext, Vcb, ExistingFcb, Ccb);
                if (!NT_SUCCESS(Status)) {
                    DEBUG(DL_REN, ("Ext2SetRenameInfo: Target file %wZ cannot be removed.\n",
                                   &ExistingMcb->FullName));
                    goto errorout;
                }
            }

            Status = Ext2DeleteFile(IrpContext, Vcb, ExistingFcb, ExistingMcb);
            if (!NT_SUCCESS(Status)) {
                DEBUG(DL_REN, ("Ext2SetRenameInfo: Failed to delete %wZ with status: %xh.\n",
                               &FileName, Status));

                goto errorout;
            }

            bTargetRemoved = TRUE;
        }
    }

    /* remove directory entry of old name */
    Status = Ext2RemoveEntry(IrpContext, Vcb, ParentDcb, Mcb);
    if (!NT_SUCCESS(Status)) {
        DEBUG(DL_REN, ("Ext2SetRenameInfo: Failed to remove entry %wZ with status %xh.\n",
                       &Mcb->FullName, Status));
        DbgBreak();
        goto errorout;
    }

    /* add new entry for new target name */
    Status = Ext2AddEntry(IrpContext, Vcb, TargetDcb, &Mcb->Inode, &FileName, &NewEntry);
    if (!NT_SUCCESS(Status)) {
        DEBUG(DL_REN, ("Ext2SetRenameInfo: Failed to add entry for %wZ with status: %xh.\n",
                       &FileName, Status));
        Ext2AddEntry(IrpContext, Vcb, ParentDcb, &Mcb->Inode, &Mcb->ShortName, &NewEntry);
        goto errorout;
    }

    /* correct the inode number in ..  entry */
    if (IsMcbDirectory(Mcb)) {
        Status = Ext2SetParentEntry(
                     IrpContext, Vcb, Fcb,
                     ParentMcb->Inode.i_ino,
                     TargetMcb->Inode.i_ino );
        if (!NT_SUCCESS(Status)) {
            DEBUG(DL_REN, ("Ext2SetRenameInfo: Failed to set parent refer of %wZ with %xh.\n",
                           &Mcb->FullName, Status));
            DbgBreak();
            goto errorout;
        }
    }

    /* Update current dentry from the newly created one. We need keep the original
       dentry to assure children's links are valid if current entry is a directory */
    if (Mcb->de) {
        char *np = Mcb->de->d_name.name;
        *(Mcb->de) = *NewEntry;
        NewEntry->d_name.name = np;
    }

    if (bTargetRemoved) {
        Ext2NotifyReportChange(
            IrpContext,
            Vcb,
            ExistingMcb,
            (IsMcbDirectory(ExistingMcb) ?
             FILE_NOTIFY_CHANGE_DIR_NAME :
             FILE_NOTIFY_CHANGE_FILE_NAME ),
            FILE_ACTION_REMOVED);
    }

    if (NT_SUCCESS(Status)) {

        if (bMove) {
            Ext2NotifyReportChange(
                IrpContext,
                Vcb,
                Mcb,
                (IsDirectory(Fcb) ?
                 FILE_NOTIFY_CHANGE_DIR_NAME :
                 FILE_NOTIFY_CHANGE_FILE_NAME ),
                FILE_ACTION_REMOVED);

        } else {
            Ext2NotifyReportChange(
                IrpContext,
                Vcb,
                Mcb,
                (IsDirectory(Fcb) ?
                 FILE_NOTIFY_CHANGE_DIR_NAME :
                 FILE_NOTIFY_CHANGE_FILE_NAME ),
                FILE_ACTION_RENAMED_OLD_NAME);

        }

        if (TargetMcb->Inode.i_ino != ParentMcb->Inode.i_ino) {
            Ext2RemoveMcb(Vcb, Mcb);
            Ext2InsertMcb(Vcb, TargetMcb, Mcb);
        }

        if (!Ext2BuildName( &Mcb->ShortName,
                            &FileName, NULL     )) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto errorout;
        }

        if (!Ext2BuildName( &Mcb->FullName,
                            &FileName,
                            &TargetMcb->FullName)) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto errorout;
        }

        if (bMove) {
            Ext2NotifyReportChange(
                IrpContext,
                Vcb,
                Mcb,
                (IsDirectory(Fcb) ?
                 FILE_NOTIFY_CHANGE_DIR_NAME :
                 FILE_NOTIFY_CHANGE_FILE_NAME ),
                FILE_ACTION_ADDED);
        } else {
            Ext2NotifyReportChange(
                IrpContext,
                Vcb,
                Mcb,
                (IsDirectory(Fcb) ?
                 FILE_NOTIFY_CHANGE_DIR_NAME :
                 FILE_NOTIFY_CHANGE_FILE_NAME ),
                FILE_ACTION_RENAMED_NEW_NAME  );
        }
    }

errorout:

    if (bFcbLockAcquired) {
        ExReleaseResourceLite(&Vcb->FcbLock);
        bFcbLockAcquired = FALSE;
    }

    if (NewEntry)
        Ext2FreeEntry(NewEntry);

    if (TargetDcb) {
        Ext2ReleaseFcb(TargetDcb);
    }

    if (ParentDcb) {
        Ext2ReleaseFcb(ParentDcb);
    }

    if (ExistingMcb)
        Ext2DerefMcb(ExistingMcb);

    return Status;
}

NTSTATUS
Ext2SetLinkInfo(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB         Vcb,
    PEXT2_FCB         Fcb,
    PEXT2_CCB         Ccb
)
{
    PEXT2_MCB               Mcb = Fcb->Mcb;

    PEXT2_FCB               TargetDcb = NULL;   /* Dcb of target directory */
    PEXT2_MCB               TargetMcb = NULL;
    PEXT2_FCB               ParentDcb = NULL;   /* Dcb of it's current parent */
    PEXT2_MCB               ParentMcb = NULL;

    PEXT2_FCB               ExistingFcb = NULL; /* Target file Fcb if it exists*/
    PEXT2_MCB               ExistingMcb = NULL;
    PEXT2_MCB               LinkMcb = NULL;     /* Mcb for new hardlink */

    UNICODE_STRING          FileName;

    NTSTATUS                Status;

    PIRP                    Irp;
    PIO_STACK_LOCATION      IrpSp;

    PFILE_OBJECT            FileObject;
    PFILE_OBJECT            TargetObject;

    BOOLEAN                 ReplaceIfExists;
    BOOLEAN                 bTargetRemoved = FALSE;

    BOOLEAN                 bFcbLockAcquired = FALSE;

    PFILE_LINK_INFORMATION  FLI;

    if (Ccb->SymLink) {
        Mcb = Ccb->SymLink;
    }

    if (IsMcbDirectory(Mcb)) {
        Status = STATUS_INVALID_PARAMETER;
        goto errorout;
    }

    Irp = IrpContext->Irp;
    IrpSp = IoGetCurrentIrpStackLocation(Irp);

    FileObject = IrpSp->FileObject;
    TargetObject = IrpSp->Parameters.SetFile.FileObject;
    ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;

    FLI = (PFILE_LINK_INFORMATION)Irp->AssociatedIrp.SystemBuffer;

    if (TargetObject == NULL) {

        UNICODE_STRING  NewName;

        NewName.Buffer = FLI->FileName;
        NewName.MaximumLength = NewName.Length = (USHORT)FLI->FileNameLength;

        while (NewName.Length > 0 && NewName.Buffer[NewName.Length/2 - 1] == L'\\') {
            NewName.Buffer[NewName.Length/2 - 1] = 0;
            NewName.Length -= 2;
        }

        while (NewName.Length > 0 && NewName.Buffer[NewName.Length/2 - 1] != L'\\') {
            NewName.Length -= 2;
        }

        NewName.Buffer = (USHORT *)((UCHAR *)NewName.Buffer + NewName.Length);
        NewName.Length = (USHORT)(FLI->FileNameLength - NewName.Length);

        FileName = NewName;

        TargetMcb = Mcb->Parent;
        if (IsMcbSymLink(TargetMcb)) {
            TargetMcb = TargetMcb->Target;
            ASSERT(!IsMcbSymLink(TargetMcb));
        }

        if (TargetMcb == NULL || FileName.Length >= EXT2_NAME_LEN*2) {
            Status = STATUS_OBJECT_NAME_INVALID;
            goto errorout;
        }

    } else {

        TargetDcb = (PEXT2_FCB)(TargetObject->FsContext);
        if (!TargetDcb || TargetDcb->Vcb != Vcb) {
            DbgBreak();
            Status = STATUS_INVALID_PARAMETER;
            goto errorout;
        }

        TargetMcb = TargetDcb->Mcb;
        FileName = TargetObject->FileName;
    }

    if (FsRtlDoesNameContainWildCards(&FileName)) {
        Status = STATUS_OBJECT_NAME_INVALID;
        goto errorout;
    }

    if (TargetMcb->Inode.i_ino == Mcb->Parent->Inode.i_ino) {
        if (FsRtlAreNamesEqual( &FileName,
                                &(Mcb->ShortName),
                                FALSE,
                                NULL )) {
            Status = STATUS_SUCCESS;
            goto errorout;
        }
    }

    ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE);
    bFcbLockAcquired = TRUE;

    TargetDcb = TargetMcb->Fcb;
    if (TargetDcb == NULL) {
        TargetDcb = Ext2AllocateFcb(Vcb, TargetMcb);
    }
    if (TargetDcb) {
        Ext2ReferXcb(&TargetDcb->ReferenceCount);
    }

    ParentMcb = Mcb->Parent;
    ParentDcb = ParentMcb->Fcb;

    if ((TargetMcb->Inode.i_ino != ParentMcb->Inode.i_ino)) {

        if (ParentDcb == NULL) {
            ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb);
        }
    }
    if (ParentDcb) {
        Ext2ReferXcb(&ParentDcb->ReferenceCount);
    }

    if (bFcbLockAcquired) {
        ExReleaseResourceLite(&Vcb->FcbLock);
        bFcbLockAcquired = FALSE;
    }

    if (!TargetDcb || !ParentDcb) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto errorout;
    }

    DEBUG(DL_RES, ("Ext2SetLinkInfo: %wZ\\%wZ -> %wZ\n",
                   &TargetMcb->FullName, &FileName, &Mcb->FullName));

    Status = Ext2LookupFile(IrpContext, Vcb, &FileName,
                            TargetMcb, &ExistingMcb, 0);
    if (NT_SUCCESS(Status) && ExistingMcb != Mcb) {

        if (!ReplaceIfExists) {

            Status = STATUS_OBJECT_NAME_COLLISION;
            DEBUG(DL_RES, ("Ext2SetRenameInfo: Target file %wZ exists\n",
                           &ExistingMcb->FullName));
            goto errorout;

        } else {

            if ( (ExistingFcb = ExistingMcb->Fcb) && !IsMcbSymLink(ExistingMcb) ) {
                Status = Ext2IsFileRemovable(IrpContext, Vcb, ExistingFcb, Ccb);
                if (!NT_SUCCESS(Status)) {
                    DEBUG(DL_REN, ("Ext2SetRenameInfo: Target file %wZ cannot be removed.\n",
                                   &ExistingMcb->FullName));
                    goto errorout;
                }
            }

            Status = Ext2DeleteFile(IrpContext, Vcb, ExistingFcb, ExistingMcb);
            if (!NT_SUCCESS(Status)) {
                DEBUG(DL_REN, ("Ext2SetRenameInfo: Failed to delete %wZ with status: %xh.\n",
                               &FileName, Status));

                goto errorout;
            }
            bTargetRemoved = TRUE;
        }
    }

    /* add new entry for new target name */
    Status = Ext2AddEntry(IrpContext, Vcb, TargetDcb, &Mcb->Inode, &FileName, NULL);
    if (!NT_SUCCESS(Status)) {
        DEBUG(DL_REN, ("Ext2SetLinkInfo: Failed to add entry for %wZ with status: %xh.\n",
                       &FileName, Status));
        goto errorout;
    }

    if (bTargetRemoved) {
        Ext2NotifyReportChange(
            IrpContext,
            Vcb,
            ExistingMcb,
            (IsMcbDirectory(ExistingMcb) ?
             FILE_NOTIFY_CHANGE_DIR_NAME :
             FILE_NOTIFY_CHANGE_FILE_NAME ),
            FILE_ACTION_REMOVED);
    }

    if (NT_SUCCESS(Status)) {

        Ext2LookupFile(IrpContext, Vcb, &FileName, TargetMcb, &LinkMcb, 0);
        if (!LinkMcb)
            goto errorout;

        Ext2NotifyReportChange(
            IrpContext,
            Vcb,
            LinkMcb,
            FILE_NOTIFY_CHANGE_FILE_NAME,
            FILE_ACTION_ADDED);
    }

errorout:

    if (bFcbLockAcquired) {
        ExReleaseResourceLite(&Vcb->FcbLock);
        bFcbLockAcquired = FALSE;
    }

    if (TargetDcb) {
        Ext2ReleaseFcb(TargetDcb);
    }

    if (ParentDcb) {
        Ext2ReleaseFcb(ParentDcb);
    }

    if (ExistingMcb)
        Ext2DerefMcb(ExistingMcb);

    if (LinkMcb)
        Ext2DerefMcb(LinkMcb);

    return Status;
}

ULONG
Ext2InodeType(PEXT2_MCB Mcb)
{
    if (IsMcbSymLink(Mcb)) {
        return EXT2_FT_SYMLINK;
    }

    if (IsMcbDirectory(Mcb)) {
        return EXT2_FT_DIR;
    }

    return EXT2_FT_REG_FILE;
}

NTSTATUS
Ext2DeleteFile(
    PEXT2_IRP_CONTEXT IrpContext,
    PEXT2_VCB         Vcb,
    PEXT2_FCB         Fcb,
    PEXT2_MCB         Mcb
)
{
    PEXT2_FCB       Dcb = NULL;

    NTSTATUS        Status = STATUS_UNSUCCESSFUL;

    BOOLEAN         VcbResourceAcquired = FALSE;
    BOOLEAN         FcbPagingIoAcquired = FALSE;
    BOOLEAN         FcbResourceAcquired = FALSE;
    BOOLEAN         DcbResourceAcquired = FALSE;

    LARGE_INTEGER   Size;
    LARGE_INTEGER   SysTime;

    BOOLEAN         bFcbLockAcquired = FALSE;

    DEBUG(DL_INF, ( "Ext2DeleteFile: File %wZ (%xh) will be deleted!\n",
                    &Mcb->FullName, Mcb->Inode.i_ino));

    if (IsFlagOn(Mcb->Flags, MCB_FILE_DELETED)) {
        return STATUS_SUCCESS;
    }

    if (!IsMcbSymLink(Mcb) && IsMcbDirectory(Mcb)) {
        if (!Ext2IsDirectoryEmpty(IrpContext, Vcb, Mcb)) {
            return STATUS_DIRECTORY_NOT_EMPTY;
        }
    }

    __try {

        Ext2ReferMcb(Mcb);

        ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
        VcbResourceAcquired = TRUE;

        ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE);
        bFcbLockAcquired = TRUE;

        /* Mcb->Parent could be NULL when working with layered file systems */
        if (Mcb->Parent) {
            Dcb = Mcb->Parent->Fcb;
            if (!Dcb)
                Dcb = Ext2AllocateFcb(Vcb, Mcb->Parent);
        }
        if (Dcb)
            Ext2ReferXcb(&Dcb->ReferenceCount);

        if (bFcbLockAcquired) {
            ExReleaseResourceLite(&Vcb->FcbLock);
            bFcbLockAcquired = FALSE;
        }

        if (Dcb) {
            DcbResourceAcquired =
                ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE);

            /* remove it's entry form it's parent */
            Status = Ext2RemoveEntry(IrpContext, Vcb, Dcb, Mcb);
        }

        if (NT_SUCCESS(Status)) {

            SetFlag(Mcb->Flags, MCB_FILE_DELETED);
            Ext2RemoveMcb(Vcb, Mcb);

            if (Fcb) {
                FcbResourceAcquired =
                    ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE);

                FcbPagingIoAcquired =
                    ExAcquireResourceExclusiveLite(&Fcb->PagingIoResource, TRUE);
            }

            if (DcbResourceAcquired) {
                ExReleaseResourceLite(&Dcb->MainResource);
                DcbResourceAcquired = FALSE;
            }

            if (VcbResourceAcquired) {
                ExReleaseResourceLite(&Vcb->MainResource);
                VcbResourceAcquired = FALSE;
            }

            if (IsMcbSymLink(Mcb)) {
                if (Mcb->Inode.i_nlink > 0) {
                    Status = STATUS_CANNOT_DELETE;
                    __leave;
                }
            } else if (!IsMcbDirectory(Mcb)) {
                if (Mcb->Inode.i_nlink > 0) {
                    __leave;
                }
            } else {
                if (Mcb->Inode.i_nlink >= 2) {
                    __leave;
                }
            }

            if (S_ISLNK(Mcb->Inode.i_mode)) {

                /* for symlink, we should do differenctly  */
                if (Mcb->Inode.i_size > EXT2_LINKLEN_IN_INODE) {
                    Size.QuadPart = (LONGLONG)0;
                    Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &Size);
                }

            } else {

                /* truncate file size */
                Size.QuadPart = (LONGLONG)0;
                Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &Size);

                /* check file offset mappings */
                DEBUG(DL_EXT, ("Ext2DeleteFile ...: %wZ\n", &Mcb->FullName));

                if (Fcb) {
                    Fcb->Header.AllocationSize.QuadPart = Size.QuadPart;
                    if (Fcb->Header.FileSize.QuadPart > Size.QuadPart) {
                        Fcb->Header.FileSize.QuadPart = Size.QuadPart;
                        Fcb->Mcb->Inode.i_size = Size.QuadPart;
                    }
                    if (Fcb->Header.ValidDataLength.QuadPart > Fcb->Header.FileSize.QuadPart) {
                        Fcb->Header.ValidDataLength.QuadPart = Fcb->Header.FileSize.QuadPart;
                    }
                } else if (Mcb) {
                    /* Update the inode's data length . It should be ZERO if succeeds. */
                    if (Mcb->Inode.i_size > (loff_t)Size.QuadPart) {
                        Mcb->Inode.i_size = Size.QuadPart;
                    }
                }
            }

            /* set delete time and free the inode */
            KeQuerySystemTime(&SysTime);
            Mcb->Inode.i_nlink = 0;
            /* i_dtime is only the lower 32-bits because it is used as a relative time */
            { ULONG dummy; Ext2TimeToSecondsSince1970(&SysTime, &Mcb->Inode.i_dtime, &dummy); }
            Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode);
            Ext2FreeInode(IrpContext, Vcb, Mcb->Inode.i_ino, Ext2InodeType(Mcb));
        }

    } __finally {

        if (FcbPagingIoAcquired) {
            ExReleaseResourceLite(&Fcb->PagingIoResource);
        }

        if (FcbResourceAcquired) {
            ExReleaseResourceLite(&Fcb->MainResource);
        }

        if (DcbResourceAcquired) {
            ExReleaseResourceLite(&Dcb->MainResource);
        }

        if (bFcbLockAcquired) {
            ExReleaseResourceLite(&Vcb->FcbLock);
        }

        if (VcbResourceAcquired) {
            ExReleaseResourceLite(&Vcb->MainResource);
        }

        if (Dcb) {
            Ext2ReleaseFcb(Dcb);
        }

        Ext2DerefMcb(Mcb);
    }

    DEBUG(DL_INF, ( "Ext2DeleteFile: %wZ Succeed... EXT2SB->S_FREE_BLOCKS = %I64xh .\n",
                    &Mcb->FullName, ext3_free_blocks_count(SUPER_BLOCK)));

    return Status;
}
