[fastfat_new]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / fcb.c
index 43e1fb5..e863984 100644 (file)
 /*
-* FILE:             drivers/fs/vfat/fcb.c
-* PURPOSE:          Routines to manipulate FCBs.
-* COPYRIGHT:        See COPYING in the top level directory
-* PROJECT:          ReactOS kernel
-* PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
-*                   Rex Jolliff (rex@lvcablemodem.com)
-*                   Herve Poussineau (reactos@poussine.freesurf.fr)
-*/
+ * PROJECT:         ReactOS FAT file system driver
+ * LICENSE:         GNU GPLv3 as published by the Free Software Foundation
+ * FILE:            drivers/filesystems/fastfat/fcb.c
+ * PURPOSE:         FCB manipulation routines.
+ * PROGRAMMERS:     Aleksey Bragin <aleksey@reactos.org>
+ */
 
-/*  -------------------------------------------------------  INCLUDES  */
-
-#ifdef __GNUC__
-#include <wctype.h> /* towlower prototype */
-#endif
+/* INCLUDES *****************************************************************/
 
 #define NDEBUG
 #include "fastfat.h"
 
-/*  --------------------------------------------------------  DEFINES  */
-
-#define TAG_FCB TAG('V', 'F', 'C', 'B')
+#define TAG_FILENAME 'fBnF'
 
-/*  --------------------------------------------------------  PUBLICS  */
+/* FUNCTIONS ****************************************************************/
 
-static ULONG vfatNameHash(ULONG hash, PUNICODE_STRING NameU)
+FSRTL_COMPARISON_RESULT
+NTAPI
+FatiCompareNames(PSTRING NameA,
+                 PSTRING NameB)
 {
-       PWCHAR last;
-       PWCHAR curr;
-       register WCHAR c;
-
-       ASSERT(NameU->Buffer[0] != L'.');
-       curr = NameU->Buffer;
-       last = NameU->Buffer + NameU->Length / sizeof(WCHAR);
-
-       while(curr < last)
-       {
-               c = towlower(*curr++);
-               hash = (hash + (c << 4) + (c >> 4)) * 11;
-       }
-       return hash;
+    ULONG MinimumLen, i;
+
+    /* Calc the minimum length */
+    MinimumLen = NameA->Length < NameB->Length ? NameA->Length :
+                                                NameB->Length;
+
+    /* Actually compare them */
+    i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinimumLen );
+
+    if (i < MinimumLen)
+    {
+        /* Compare prefixes */
+        if (NameA->Buffer[i] < NameB->Buffer[i])
+            return LessThan;
+        else
+            return GreaterThan;
+    }
+
+    /* Final comparison */
+    if (NameA->Length < NameB->Length)
+        return LessThan;
+    else if (NameA->Length > NameB->Length)
+        return GreaterThan;
+    else
+        return EqualTo;
 }
 
-VOID
-vfatSplitPathName(PUNICODE_STRING PathNameU, PUNICODE_STRING DirNameU, PUNICODE_STRING FileNameU)
+PFCB
+NTAPI
+FatFindFcb(PFAT_IRP_CONTEXT IrpContext,
+           PRTL_SPLAY_LINKS *RootNode,
+           PSTRING AnsiName,
+           PBOOLEAN IsDosName)
 {
-       PWCHAR pName;
-       USHORT Length = 0;
-       pName = PathNameU->Buffer + PathNameU->Length / sizeof(WCHAR) - 1;
-       while (*pName != L'\\' && pName >= PathNameU->Buffer)
-       {
-               pName--;
-               Length++;
-       }
-       ASSERT(*pName == L'\\' || pName < PathNameU->Buffer);
-       if (FileNameU)
-       {
-               FileNameU->Buffer = pName + 1;
-               FileNameU->Length = FileNameU->MaximumLength = Length * sizeof(WCHAR);
-       }
-       if (DirNameU)
-       {
-               DirNameU->Buffer = PathNameU->Buffer;
-               DirNameU->Length = (pName + 1 - PathNameU->Buffer) * sizeof(WCHAR);
-               DirNameU->MaximumLength = DirNameU->Length;
-       }
+    PFCB_NAME_LINK Node;
+    FSRTL_COMPARISON_RESULT Comparison;
+    PRTL_SPLAY_LINKS Links;
+
+    Links = *RootNode;
+
+    while (Links)
+    {
+        Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
+
+        /* Compare the prefix */
+        if (*(PUCHAR)Node->Name.Ansi.Buffer != *(PUCHAR)AnsiName->Buffer)
+        {
+            if (*(PUCHAR)Node->Name.Ansi.Buffer < *(PUCHAR)AnsiName->Buffer)
+                Comparison = LessThan;
+            else
+                Comparison = GreaterThan;
+        }
+        else
+        {
+            /* Perform real comparison */
+            Comparison = FatiCompareNames(&Node->Name.Ansi, AnsiName);
+        }
+
+        /* Do they match? */
+        if (Comparison == GreaterThan)
+        {
+            /* No, it's greater, go to the left child */
+            Links = RtlLeftChild(Links);
+        }
+        else if (Comparison == LessThan)
+        {
+            /* No, it's lesser, go to the right child */
+            Links = RtlRightChild(Links);
+        }
+        else
+        {
+            /* Exact match, balance the tree */
+            *RootNode = RtlSplay(Links);
+
+            /* Save type of the name, if needed */
+            if (IsDosName)
+                *IsDosName = Node->IsDosName;
+
+            /* Return the found fcb */
+            return Node->Fcb;
+        }
+    }
+
+    /* Nothing found */
+    return NULL;
 }
 
-static VOID
-vfatInitFcb(PVFATFCB Fcb, PUNICODE_STRING NameU)
+PFCB
+NTAPI
+FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext,
+             IN PVCB Vcb,
+             IN PFCB ParentDcb,
+             IN FF_FILE *FileHandle)
 {
-       USHORT PathNameBufferLength;
-
-       if (NameU)
-               PathNameBufferLength = NameU->Length + sizeof(WCHAR);
-       else
-               PathNameBufferLength = 0;
-
-       Fcb->PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength, TAG_FCB);
-       if (!Fcb->PathNameBuffer)
-       {
-               /* FIXME: what to do if no more memory? */
-               DPRINT1("Unable to initialize FCB for filename '%wZ'\n", NameU);
-               KeBugCheckEx(0, (ULONG_PTR)Fcb, (ULONG_PTR)NameU, 0, 0);
-       }
-
-       Fcb->PathNameU.Length = 0;
-       Fcb->PathNameU.Buffer = Fcb->PathNameBuffer;
-       Fcb->PathNameU.MaximumLength = PathNameBufferLength;
-       Fcb->ShortNameU.Length = 0;
-       Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer;
-       Fcb->ShortNameU.MaximumLength = sizeof(Fcb->ShortNameBuffer);
-       Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
-       if (NameU && NameU->Length)
-       {
-               RtlCopyUnicodeString(&Fcb->PathNameU, NameU);
-               vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
-       }
-       else
-       {
-               Fcb->DirNameU.Buffer = Fcb->LongNameU.Buffer = NULL;
-               Fcb->DirNameU.MaximumLength = Fcb->DirNameU.Length = 0;
-               Fcb->LongNameU.MaximumLength = Fcb->LongNameU.Length = 0;
-       }
-       RtlZeroMemory(&Fcb->FCBShareAccess, sizeof(SHARE_ACCESS));
-       Fcb->OpenHandleCount = 0;
+    PFCB Fcb;
+
+    /* Allocate it and zero it */
+    Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
+    RtlZeroMemory(Fcb, sizeof(FCB));
+
+    /* Set node types */
+    Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
+    Fcb->Header.NodeByteSize = sizeof(FCB);
+    Fcb->Condition = FcbGood;
+
+    /* Initialize resources */
+    Fcb->Header.Resource = &Fcb->Resource;
+    ExInitializeResourceLite(Fcb->Header.Resource);
+
+    Fcb->Header.PagingIoResource = &Fcb->PagingIoResource;
+    ExInitializeResourceLite(Fcb->Header.PagingIoResource);
+
+    /* Initialize mutexes */
+    Fcb->Header.FastMutex = &Fcb->HeaderMutex;
+    ExInitializeFastMutex(&Fcb->HeaderMutex);
+    FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
+
+    /* Insert into parent's DCB list */
+    InsertTailList(&ParentDcb->Dcb.ParentDcbList, &Fcb->ParentDcbLinks);
+
+    /* Set backlinks */
+    Fcb->ParentFcb = ParentDcb;
+    Fcb->Vcb = Vcb;
+
+    /* Set file handle and sizes */
+    Fcb->Header.FileSize.LowPart = FileHandle->Filesize;
+    Fcb->Header.ValidDataLength.LowPart = FileHandle->Filesize;
+    Fcb->FatHandle = FileHandle;
+
+    /* Set names */
+    FatSetFcbNames(IrpContext, Fcb);
+
+    return Fcb;
 }
 
-PVFATFCB
-vfatNewFCB(PDEVICE_EXTENSION  pVCB, PUNICODE_STRING pFileNameU)
+PCCB
+NTAPI
+FatCreateCcb()
 {
-       PVFATFCB  rcFCB;
-
-       DPRINT("'%wZ'\n", pFileNameU);
-
-       rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
-       if (rcFCB == NULL)
-       {
-               return NULL;
-       }
-       RtlZeroMemory(rcFCB, sizeof(VFATFCB));
-       vfatInitFcb(rcFCB, pFileNameU);
-       if (pVCB->Flags & VCB_IS_FATX)
-       {
-               rcFCB->Flags |= FCB_IS_FATX_ENTRY;
-               rcFCB->Attributes = &rcFCB->entry.FatX.Attrib;
-       }
-       else
-               rcFCB->Attributes = &rcFCB->entry.Fat.Attrib;
-       rcFCB->Hash.Hash = vfatNameHash(0, &rcFCB->PathNameU);
-       rcFCB->Hash.self = rcFCB;
-       rcFCB->ShortHash.self = rcFCB;
-       ExInitializeResourceLite(&rcFCB->PagingIoResource);
-       ExInitializeResourceLite(&rcFCB->MainResource);
-       FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
-       ExInitializeFastMutex(&rcFCB->LastMutex);
-       rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource;
-       rcFCB->RFCB.Resource = &rcFCB->MainResource;
-       rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
-
-       return  rcFCB;
+    PCCB Ccb;
+
+    /* Allocate the CCB and zero it */
+    Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
+    RtlZeroMemory(Ccb, sizeof(CCB));
+
+    /* Set mandatory header */
+    Ccb->NodeTypeCode = FAT_NTC_FCB;
+    Ccb->NodeByteSize = sizeof(CCB);
+
+    return Ccb;
 }
 
-VOID
-vfatDestroyCCB(PVFATCCB pCcb)
+IO_STATUS_BLOCK
+NTAPI
+FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext,
+                    IN PFILE_OBJECT FileObject,
+                    IN PVCB Vcb,
+                    IN PFCB Fcb,
+                    IN PACCESS_MASK DesiredAccess,
+                    IN USHORT ShareAccess,
+                    IN ULONG AllocationSize,
+                    IN PFILE_FULL_EA_INFORMATION EaBuffer,
+                    IN ULONG EaLength,
+                    IN UCHAR FileAttributes,
+                    IN ULONG CreateDisposition,
+                    IN BOOLEAN NoEaKnowledge,
+                    IN BOOLEAN DeleteOnClose,
+                    IN BOOLEAN OpenedAsDos,
+                    OUT PBOOLEAN OplockPostIrp)
 {
-       if (pCcb->SearchPattern.Buffer)
-       {
-               ExFreePool(pCcb->SearchPattern.Buffer);
-       }
-       ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
+    IO_STATUS_BLOCK Iosb = {{0}};
+    ACCESS_MASK AddedAccess = 0;
+    BOOLEAN Hidden;
+    BOOLEAN System;
+    PCCB Ccb = NULL;
+    NTSTATUS Status;
+
+    /* Acquire exclusive FCB lock */
+    (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);
+
+    *OplockPostIrp = FALSE;
+
+    /* Check if there is a batch oplock */
+    if (FsRtlCurrentBatchOplock(&Fcb->Fcb.Oplock))
+    {
+        /* Return with a special information field */
+        Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;
+
+        /* Check the oplock */
+        Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
+                                       IrpContext->Irp,
+                                       IrpContext,
+                                       FatOplockComplete,
+                                       FatPrePostIrp);
+
+        if (Iosb.Status != STATUS_SUCCESS &&
+            Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
+        {
+            /* The Irp needs to be queued */
+            *OplockPostIrp = TRUE;
+
+            /* Release the FCB and return */
+            FatReleaseFcb(IrpContext, Fcb);
+            return Iosb;
+        }
+    }
+
+    /* Validate parameters and modify access */
+    if (CreateDisposition == FILE_CREATE)
+    {
+        Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+
+        /* Release the FCB and return */
+        FatReleaseFcb(IrpContext, Fcb);
+        return Iosb;
+    }
+    else if (CreateDisposition == FILE_SUPERSEDE)
+    {
+        SetFlag(AddedAccess, DELETE & ~(*DesiredAccess));
+        *DesiredAccess |= DELETE;
+    }
+    else if ((CreateDisposition == FILE_OVERWRITE) ||
+             (CreateDisposition == FILE_OVERWRITE_IF))
+    {
+        SetFlag(AddedAccess,
+                (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
+                & ~(*DesiredAccess) );
+
+        *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
+    }
+
+    // TODO: Check desired access
+
+    // TODO: Check if this file is readonly and DeleteOnClose is set
+
+    /* Validate disposition information */
+    if ((CreateDisposition == FILE_SUPERSEDE) ||
+        (CreateDisposition == FILE_OVERWRITE) ||
+        (CreateDisposition == FILE_OVERWRITE_IF))
+    {
+        // TODO: Get this attributes from the dirent
+        Hidden = FALSE;
+        System = FALSE;
+
+        if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
+            (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM)))
+        {
+            DPRINT1("Hidden/system attributes don't match\n");
+
+            Iosb.Status = STATUS_ACCESS_DENIED;
+
+            /* Release the FCB and return */
+            FatReleaseFcb(IrpContext, Fcb);
+            return Iosb;
+        }
+
+        // TODO: Check for write protected volume
+    }
+
+    /* Check share access */
+    Iosb.Status = IoCheckShareAccess(*DesiredAccess,
+                                     ShareAccess,
+                                     FileObject,
+                                     &Fcb->ShareAccess,
+                                     FALSE);
+    if (!NT_SUCCESS(Iosb.Status))
+    {
+        /* Release the FCB and return */
+        FatReleaseFcb(IrpContext, Fcb);
+        return Iosb;
+    }
+
+    /* Check the oplock status after checking for share access */
+    Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
+                                   IrpContext->Irp,
+                                   IrpContext,
+                                   FatOplockComplete,
+                                   FatPrePostIrp );
+
+    if (Iosb.Status != STATUS_SUCCESS &&
+        Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
+    {
+        /* The Irp needs to be queued */
+        *OplockPostIrp = TRUE;
+
+        /* Release the FCB and return */
+        FatReleaseFcb(IrpContext, Fcb);
+        return Iosb;
+    }
+
+    /* Set Fast I/O flag */
+    Fcb->Header.IsFastIoPossible = FALSE; //FatiIsFastIoPossible(Fcb);
+
+    /* Make sure image is not mapped */
+    if (DeleteOnClose || FlagOn(*DesiredAccess, FILE_WRITE_DATA))
+    {
+        /* Try to flush the image section */
+        if (!MmFlushImageSection(&Fcb->SectionObjectPointers, MmFlushForWrite))
+        {
+            /* Yes, image section exists, set correct status code */
+            if (DeleteOnClose)
+                Iosb.Status = STATUS_CANNOT_DELETE;
+            else
+                Iosb.Status = STATUS_SHARING_VIOLATION;
+
+            /* Release the FCB and return */
+            FatReleaseFcb(IrpContext, Fcb);
+            return Iosb;
+        }
+    }
+
+    /* Flush the cache if it's non-cached non-pagefile access */
+    if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) &&
+        Fcb->SectionObjectPointers.DataSectionObject &&
+        !FlagOn(Fcb->State, FCB_STATE_PAGEFILE))
+    {
+        /* Set the flag that create is in progress */
+        SetFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
+
+        /* Flush the cache */
+        CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
+
+        /* Acquire and release Paging I/O resource before purging the cache section
+           to let lazy writer finish */
+        ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
+        ExReleaseResourceLite( Fcb->Header.PagingIoResource );
+
+        /* Delete the cache section */
+        CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
+
+        /* Clear the flag */
+        ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
+    }
+
+    /* Check create disposition flags and branch accordingly */
+    if (CreateDisposition == FILE_OPEN ||
+        CreateDisposition == FILE_OPEN_IF)
+    {
+        DPRINT("Opening a file\n");
+
+        /* Check if we need to bother with EA */
+        if (NoEaKnowledge && FALSE /* FatIsFat32(Vcb)*/)
+        {
+            UNIMPLEMENTED;
+        }
+
+        /* Set up file object */
+        Ccb = FatCreateCcb(IrpContext);
+        FatSetFileObject(FileObject,
+                         UserFileOpen,
+                         Fcb,
+                         Ccb);
+
+        FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
+
+        /* The file is opened */
+        Iosb.Information = FILE_OPENED;
+        goto SuccComplete;
+    }
+    else if ((CreateDisposition == FILE_SUPERSEDE) ||
+             (CreateDisposition == FILE_OVERWRITE) ||
+             (CreateDisposition == FILE_OVERWRITE_IF))
+    {
+        UNIMPLEMENTED;
+        ASSERT(FALSE);
+    }
+    else
+    {
+        /* We can't get here */
+        KeBugCheckEx(0x23, CreateDisposition, 0, 0, 0);
+    }
+
+
+SuccComplete:
+    /* If all is fine */
+    if (Iosb.Status != STATUS_PENDING &&
+        NT_SUCCESS(Iosb.Status))
+    {
+        /* Update access if needed */
+        if (AddedAccess)
+        {
+            /* Remove added access flags from desired access */
+            ClearFlag(*DesiredAccess, AddedAccess);
+
+            /* Check share access */
+            Status = IoCheckShareAccess(*DesiredAccess,
+                                        ShareAccess,
+                                        FileObject,
+                                        &Fcb->ShareAccess,
+                                        TRUE);
+
+            /* Make sure it's success */
+            ASSERT(Status == STATUS_SUCCESS);
+        }
+        else
+        {
+            /* Update the share access */
+            IoUpdateShareAccess(FileObject, &Fcb->ShareAccess);
+        }
+
+        /* Clear the delay close */
+        ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
+
+        /* Increase counters */
+        Fcb->OpenCount++;
+        Vcb->OpenFileCount++;
+
+        // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB
+    }
+
+    return Iosb;
 }
 
 VOID
-vfatDestroyFCB(PVFATFCB pFCB)
+NTAPI
+FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
+                     IN PFCB Fcb,
+                     OUT PUNICODE_STRING LongName)
 {
-       FsRtlUninitializeFileLock(&pFCB->FileLock);
-       ExFreePool(pFCB->PathNameBuffer);
-       ExDeleteResourceLite(&pFCB->PagingIoResource);
-       ExDeleteResourceLite(&pFCB->MainResource);
-       ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
+    FF_DIRENT DirEnt;
+    FF_ERROR Err;
+    OEM_STRING ShortName;
+    CHAR ShortNameBuf[13];
+    UCHAR EntryBuffer[32];
+    UCHAR NumLFNs;
+    OEM_STRING LongNameOem;
+    NTSTATUS Status;
+
+    /* Make sure this FCB has a FullFAT handle associated with it */
+    if (Fcb->FatHandle == NULL &&
+        FatNodeType(Fcb) == FAT_NTC_DCB)
+    {
+        /* Open the dir with FullFAT */
+        Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
+        if (!Fcb->FatHandle)
+        {
+            ASSERT(FALSE);
+        }
+    }
+
+    /* Get the dir entry */
+    Err = FF_GetEntry(Fcb->Vcb->Ioman,
+                      Fcb->FatHandle->DirEntry,
+                      Fcb->FatHandle->DirCluster,
+                      &DirEnt);
+
+    if (Err != FF_ERR_NONE)
+    {
+        DPRINT1("Error %d getting dirent of a file\n", Err);
+        return;
+    }
+
+    /* Read the dirent to fetch the raw short name */
+    FF_FetchEntry(Fcb->Vcb->Ioman,
+                  Fcb->FatHandle->DirCluster,
+                  Fcb->FatHandle->DirEntry,
+                  EntryBuffer);
+    NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
+
+    /* Check if we only have a short name.
+       Convert it to unicode and return if that's the case */
+    if (NumLFNs == 0)
+    {
+        /* Initialize short name string */
+        ShortName.Buffer = ShortNameBuf;
+        ShortName.Length = 0;
+        ShortName.MaximumLength = 12;
+
+        /* Convert raw short name to a proper string */
+        Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);
+
+        /* Convert it to unicode */
+        Status = RtlOemStringToCountedUnicodeString(LongName,
+                                                    &ShortName,
+                                                    FALSE);
+
+        /* Ensure conversion was successful */
+        ASSERT(Status == STATUS_SUCCESS);
+
+        /* Exit */
+        return;
+    }
+
+    /* Convert LFN from OEM to unicode and return */
+    LongNameOem.Buffer = DirEnt.FileName;
+    LongNameOem.MaximumLength = FF_MAX_FILENAME;
+    LongNameOem.Length = strlen(DirEnt.FileName);
+
+    /* Convert it to unicode */
+    Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);
+
+    /* Ensure conversion was successful */
+    ASSERT(Status == STATUS_SUCCESS);
 }
 
-BOOLEAN
-vfatFCBIsDirectory(PVFATFCB FCB)
-{
-       return  *FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY;
-}
 
-BOOLEAN
-vfatFCBIsRoot(PVFATFCB FCB)
+VOID
+NTAPI
+FatSetFullNameInFcb(PFCB Fcb,
+                    PUNICODE_STRING Name)
 {
-       return  FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE;
+    PUNICODE_STRING ParentName;
+
+    /* Make sure this FCB's name wasn't already set */
+    ASSERT(Fcb->FullFileName.Buffer == NULL);
+
+    /* First of all, check exact case name */
+    if (Fcb->ExactCaseLongName.Buffer)
+    {
+        ASSERT(Fcb->ExactCaseLongName.Length != 0);
+
+        /* Use exact case name */
+        Name = &Fcb->ExactCaseLongName;
+    }
+
+    /* Treat root dir different */
+    if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
+    {
+        /* Set lengths */
+        Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
+        Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
+
+        /* Allocate a buffer */
+        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
+                                                            Fcb->FullFileName.Length,
+                                                            TAG_FILENAME);
+
+        /* Prefix with a backslash */
+        Fcb->FullFileName.Buffer[0] = L'\\';
+
+        /* Copy the name here */
+        RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
+                      &Name->Buffer[0],
+                       Name->Length );
+    }
+    else
+    {
+        ParentName = &Fcb->ParentFcb->FullFileName;
+
+        /* Check if parent's name is set */
+        if (!ParentName->Buffer)
+            return;
+
+        /* Set lengths */
+        Fcb->FullFileName.MaximumLength =
+            ParentName->Length + sizeof(WCHAR) + Name->Length;
+        Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
+
+        /* Allocate a buffer */
+        Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
+                                                            Fcb->FullFileName.Length,
+                                                            TAG_FILENAME );
+
+        /* Copy parent's name here */
+        RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
+                      &ParentName->Buffer[0],
+                      ParentName->Length );
+
+        /* Add a backslash */
+        Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
+
+        /* Copy given name here */
+        RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
+                      &Name->Buffer[0],
+                      Name->Length );
+    }
 }
 
 VOID
-vfatReleaseFCB(PDEVICE_EXTENSION  pVCB,  PVFATFCB  pFCB)
+NTAPI
+FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext,
+                        IN PFCB Fcb)
 {
-       HASHENTRY* entry;
-       ULONG Index;
-       ULONG ShortIndex;
-       PVFATFCB tmpFcb;
-
-       DPRINT ("releasing FCB at %p: %wZ, refCount:%d\n",
-               pFCB,
-               &pFCB->PathNameU,
-               pFCB->RefCount);
-
-       while (pFCB)
-       {
-               Index = pFCB->Hash.Hash % pVCB->HashTableSize;
-               ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
-               pFCB->RefCount--;
-               if (pFCB->RefCount == 0)
-               {
-                       tmpFcb = pFCB->parentFcb;
-                       RemoveEntryList (&pFCB->FcbListEntry);
-                       if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
-                       {
-                               entry = pVCB->FcbHashTable[ShortIndex];
-                               if (entry->self == pFCB)
-                               {
-                                       pVCB->FcbHashTable[ShortIndex] = entry->next;
-                               }
-                               else
-                               {
-                                       while (entry->next->self != pFCB)
-                                       {
-                                               entry = entry->next;
-                                       }
-                                       entry->next = pFCB->ShortHash.next;
-                               }
-                       }
-                       entry = pVCB->FcbHashTable[Index];
-                       if (entry->self == pFCB)
-                       {
-                               pVCB->FcbHashTable[Index] = entry->next;
-                       }
-                       else
-                       {
-                               while (entry->next->self != pFCB)
-                               {
-                                       entry = entry->next;
-                               }
-                               entry->next = pFCB->Hash.next;
-                       }
-                       vfatDestroyFCB (pFCB);
-               }
-               else
-               {
-                       tmpFcb = NULL;
-               }
-               pFCB = tmpFcb;
-       }
+    UNICODE_STRING LongName;
+    PFCB CurFcb = Fcb;
+    PFCB StopFcb;
+    PWCHAR TmpBuffer;
+    ULONG PathLength = 0;
+
+    /* Do nothing if it's already set */
+    if (Fcb->FullFileName.Buffer) return;
+
+    /* Allocate a temporary buffer */
+    LongName.Length = 0;
+    LongName.MaximumLength = FF_MAX_FILENAME * sizeof(WCHAR);
+    LongName.Buffer =
+        FsRtlAllocatePoolWithTag(PagedPool,
+                                 FF_MAX_FILENAME * sizeof(WCHAR),
+                                 TAG_FILENAME);
+
+    /* Go through all parents to calculate needed length */
+    while (CurFcb != Fcb->Vcb->RootDcb)
+    {
+        /* Does current FCB have FullFileName set? */
+        if (CurFcb != Fcb &&
+            CurFcb->FullFileName.Buffer)
+        {
+            /* Yes, just use it! */
+            PathLength += CurFcb->FullFileName.Length;
+
+            Fcb->FullFileName.Buffer =
+                FsRtlAllocatePoolWithTag(PagedPool,
+                                         PathLength,
+                                         TAG_FILENAME);
+
+            RtlCopyMemory(Fcb->FullFileName.Buffer,
+                          CurFcb->FullFileName.Buffer,
+                          CurFcb->FullFileName.Length);
+
+            break;
+        }
+
+        /* Sum up length of a current item */
+        PathLength += CurFcb->FileNameLength + sizeof(WCHAR);
+
+        /* Go to the parent */
+        CurFcb = CurFcb->ParentFcb;
+    }
+
+    /* Allocate FullFileName if it wasn't already allocated above */
+    if (!Fcb->FullFileName.Buffer)
+    {
+        Fcb->FullFileName.Buffer =
+            FsRtlAllocatePoolWithTag(PagedPool,
+                                     PathLength,
+                                     TAG_FILENAME);
+    }
+
+    StopFcb = CurFcb;
+
+    CurFcb = Fcb;
+    TmpBuffer =  Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
+
+    /* Set lengths */
+    Fcb->FullFileName.Length = PathLength;
+    Fcb->FullFileName.MaximumLength = PathLength;
+
+    while (CurFcb != StopFcb)
+    {
+        /* Get its unicode name */
+        FatGetFcbUnicodeName(IrpContext,
+                             CurFcb,
+                             &LongName);
+
+        /* Copy it */
+        TmpBuffer -= LongName.Length / sizeof(WCHAR);
+        RtlCopyMemory(TmpBuffer, LongName.Buffer, LongName.Length);
+
+        /* Append with a backslash */
+        TmpBuffer -= 1;
+        *TmpBuffer = L'\\';
+
+        /* Go to the parent */
+        CurFcb = CurFcb->ParentFcb;
+    }
+
+    /* Free the temp buffer */
+    ExFreePool(LongName.Buffer);
 }
 
+
 VOID
-vfatAddFCBToTable(PDEVICE_EXTENSION  pVCB,  PVFATFCB  pFCB)
+NTAPI
+FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
+               IN PFCB Fcb)
 {
-       ULONG Index;
-       ULONG ShortIndex;
-
-       Index = pFCB->Hash.Hash % pVCB->HashTableSize;
-       ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
-
-       InsertTailList (&pVCB->FcbListHead, &pFCB->FcbListEntry);
-
-       pFCB->Hash.next = pVCB->FcbHashTable[Index];
-       pVCB->FcbHashTable[Index] = &pFCB->Hash;
-       if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
-       {
-               pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
-               pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
-       }
-       if (pFCB->parentFcb)
-       {
-               pFCB->parentFcb->RefCount++;
-       }
+    FF_DIRENT DirEnt;
+    FF_ERROR Err;
+    POEM_STRING ShortName;
+    CHAR ShortNameRaw[13];
+    UCHAR EntryBuffer[32];
+    UCHAR NumLFNs;
+    PUNICODE_STRING UnicodeName;
+    OEM_STRING LongNameOem;
+    NTSTATUS Status;
+
+    /* Get the dir entry */
+    Err = FF_GetEntry(Fcb->Vcb->Ioman,
+                      Fcb->FatHandle->DirEntry,
+                      Fcb->FatHandle->DirCluster,
+                      &DirEnt);
+
+    if (Err != FF_ERR_NONE)
+    {
+        DPRINT1("Error %d getting dirent of a file\n", Err);
+        return;
+    }
+
+    /* Read the dirent to fetch the raw short name */
+    FF_FetchEntry(Fcb->Vcb->Ioman,
+                  Fcb->FatHandle->DirCluster,
+                  Fcb->FatHandle->DirEntry,
+                  EntryBuffer);
+    NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
+    RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
+
+    /* Initialize short name string */
+    ShortName = &Fcb->ShortName.Name.Ansi;
+    ShortName->Buffer = Fcb->ShortNameBuffer;
+    ShortName->Length = 0;
+    ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
+
+    /* Convert raw short name to a proper string */
+    Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
+
+    /* Add the short name link */
+    FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksAnsi, &Fcb->ShortName);
+    Fcb->ShortName.Fcb = Fcb;
+
+    /* Get the long file name (if any) */
+    if (NumLFNs > 0)
+    {
+        /* Prepare the oem string */
+        LongNameOem.Buffer = DirEnt.FileName;
+        LongNameOem.MaximumLength = FF_MAX_FILENAME;
+        LongNameOem.Length = strlen(DirEnt.FileName);
+
+        /* Prepare the unicode string */
+        UnicodeName = &Fcb->LongName.Name.String;
+        UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
+        UnicodeName->MaximumLength = UnicodeName->Length;
+        UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
+
+        /* Convert it to unicode */
+        Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
+        if (!NT_SUCCESS(Status))
+        {
+            ASSERT(FALSE);
+        }
+
+        /* Set its length */
+        Fcb->FileNameLength = UnicodeName->Length;
+
+        /* Save case-preserved copy */
+        Fcb->ExactCaseLongName.Length = UnicodeName->Length;
+        Fcb->ExactCaseLongName.MaximumLength = UnicodeName->Length;
+        Fcb->ExactCaseLongName.Buffer =
+            FsRtlAllocatePoolWithTag(PagedPool, UnicodeName->Length, TAG_FILENAME);
+
+        RtlCopyMemory(Fcb->ExactCaseLongName.Buffer,
+                      UnicodeName->Buffer,
+                      UnicodeName->Length);
+
+        /* Perform a trick which is done by MS's FASTFAT driver to monocase
+           the filename */
+        RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
+        RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
+
+        DPRINT("Converted long name: %wZ\n", UnicodeName);
+
+        /* Add the long unicode name link */
+        FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
+        Fcb->LongName.Fcb = Fcb;
+
+        /* Indicate that this FCB has a unicode long name */
+        SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
+    }
+    else
+    {
+        /* No LFN, set exact case name to 0 length */
+        Fcb->ExactCaseLongName.Length = 0;
+        Fcb->ExactCaseLongName.MaximumLength = 0;
+
+        /* Set the length based on the short name */
+        Fcb->FileNameLength = RtlOemStringToCountedUnicodeSize(ShortName);
+    }
+
+    /* Mark the fact that names were added to splay trees*/
+    SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
 }
 
-PVFATFCB
-vfatGrabFCBFromTable(PDEVICE_EXTENSION  pVCB, PUNICODE_STRING  PathNameU)
+VOID
+NTAPI
+Fati8dot3ToString(IN PCHAR FileName,
+                  IN BOOLEAN DownCase,
+                  OUT POEM_STRING OutString)
 {
-       PVFATFCB  rcFCB;
-       ULONG Hash;
-       UNICODE_STRING DirNameU;
-       UNICODE_STRING FileNameU;
-       PUNICODE_STRING FcbNameU;
-
-       HASHENTRY* entry;
-
-       DPRINT("'%wZ'\n", PathNameU);
-
-       Hash = vfatNameHash(0, PathNameU);
-
-       entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize];
-       if (entry)
-       {
-               vfatSplitPathName(PathNameU, &DirNameU, &FileNameU);
-       }
-
-       while (entry)
-       {
-               if (entry->Hash == Hash)
-               {
-                       rcFCB = entry->self;
-                       DPRINT("'%wZ' '%wZ'\n", &DirNameU, &rcFCB->DirNameU);
-                       if (RtlEqualUnicodeString(&DirNameU, &rcFCB->DirNameU, TRUE))
-                       {
-                               if (rcFCB->Hash.Hash == Hash)
-                               {
-                                       FcbNameU = &rcFCB->LongNameU;
-                               }
-                               else
-                               {
-                                       FcbNameU = &rcFCB->ShortNameU;
-                               }
-                               /* compare the file name */
-                               DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
-                               if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
-                               {
-                                       rcFCB->RefCount++;
-                                       return rcFCB;
-                               }
-                       }
-               }
-               entry = entry->next;
-       }
-       return  NULL;
+    ULONG BaseLen, ExtLen;
+    CHAR  *cString = OutString->Buffer;
+    ULONG i;
+
+    /* Calc base and ext lens */
+    for (BaseLen = 8; BaseLen > 0; BaseLen--)
+    {
+        if (FileName[BaseLen - 1] != ' ') break;
+    }
+
+    for (ExtLen = 3; ExtLen > 0; ExtLen--)
+    {
+        if (FileName[8 + ExtLen - 1] != ' ') break;
+    }
+
+    /* Process base name */
+    if (BaseLen)
+    {
+        RtlCopyMemory(cString, FileName, BaseLen);
+
+        /* Substitute the e5 thing */
+        if (cString[0] == 0x05) cString[0] = 0xe5;
+
+        /* Downcase if asked to */
+        if (DownCase)
+        {
+            /* Do it manually */
+            for (i = 0; i < BaseLen; i++)
+            {
+                if (cString[i] >= 'A' &&
+                    cString[i] <= 'Z')
+                {
+                    /* Lowercase it */
+                    cString[i] += 'a' - 'A';
+                }
+
+            }
+        }
+    }
+
+    /* Process extension */
+    if (ExtLen)
+    {
+        /* Add the dot */
+        cString[BaseLen] = '.';
+        BaseLen++;
+
+        /* Copy the extension */
+        for (i = 0; i < ExtLen; i++)
+        {
+            cString[BaseLen + i] = FileName[8 + i];
+        }
+
+        /* Lowercase the extension if asked to */
+        if (DownCase)
+        {
+            /* Do it manually */
+            for (i = BaseLen; i < BaseLen + ExtLen; i++)
+            {
+                if (cString[i] >= 'A' &&
+                    cString[i] <= 'Z')
+                {
+                    /* Lowercase it */
+                    cString[i] += 'a' - 'A';
+                }
+            }
+        }
+    }
+
+    /* Set the length */
+    OutString->Length = BaseLen + ExtLen;
+
+    DPRINT("'%s', len %d\n", OutString->Buffer, OutString->Length);
 }
 
-static NTSTATUS
-vfatFCBInitializeCacheFromVolume (PVCB  vcb, PVFATFCB  fcb)
+VOID
+NTAPI
+FatInsertName(IN PFAT_IRP_CONTEXT IrpContext,
+              IN PRTL_SPLAY_LINKS *RootNode,
+              IN PFCB_NAME_LINK Name)
 {
-       PFILE_OBJECT  fileObject;
-       PVFATCCB  newCCB;
-
-       fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
-
-       newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
-       if (newCCB == NULL)
-       {
-               return  STATUS_INSUFFICIENT_RESOURCES;
-       }
-       RtlZeroMemory(newCCB, sizeof (VFATCCB));
-
-       fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
-       fileObject->FsContext = fcb;
-       fileObject->FsContext2 = newCCB;
-       fcb->FileObject = fileObject;
-       fcb->RefCount++;
-
-       CcInitializeCacheMap(fileObject,
-               (PCC_FILE_SIZES)(&fcb->RFCB.AllocationSize),
-               TRUE,
-               &VfatGlobalData->CacheMgrCallbacks,
-               fcb);
-
-       fcb->Flags |= FCB_CACHE_INITIALIZED;
-       return STATUS_SUCCESS;
+    PFCB_NAME_LINK NameLink;
+    FSRTL_COMPARISON_RESULT Comparison;
+
+    /* Initialize the splay links */
+    RtlInitializeSplayLinks(&Name->Links);
+
+    /* Is this the first entry? */
+    if (*RootNode == NULL)
+    {
+        /* Yes, become root and return */
+        *RootNode = &Name->Links;
+        return;
+    }
+
+    /* Get the name link */
+    NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
+    while (TRUE)
+    {
+        /* Compare the prefix */
+        if (*(PUCHAR)NameLink->Name.Ansi.Buffer != *(PUCHAR)&Name->Name.Ansi.Buffer)
+        {
+            if (*(PUCHAR)NameLink->Name.Ansi.Buffer < *(PUCHAR)&Name->Name.Ansi.Buffer)
+                Comparison = LessThan;
+            else
+                Comparison = GreaterThan;
+        }
+        else
+        {
+            /* Perform real comparison */
+            Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
+        }
+
+        /* Check the bad case first */
+        if (Comparison == EqualTo)
+        {
+            /* Must not happen */
+            ASSERT(FALSE);
+        }
+
+        /* Check comparison result */
+        if (Comparison == GreaterThan)
+        {
+            /* Go to the left child */
+            if (!RtlLeftChild(&NameLink->Links))
+            {
+                /* It's absent, insert here and break */
+                RtlInsertAsLeftChild(&NameLink->Links, &Name->Links);
+                break;
+            }
+            else
+            {
+                /* It's present, go inside it */
+                NameLink = CONTAINING_RECORD(RtlLeftChild(&NameLink->Links),
+                                             FCB_NAME_LINK,
+                                             Links);
+            }
+        }
+        else
+        {
+            /* Go to the right child */
+            if (!RtlRightChild(&NameLink->Links))
+            {
+                /* It's absent, insert here and break */
+                RtlInsertAsRightChild(&NameLink->Links, &Name->Links);
+                break;
+            }
+            else
+            {
+                /* It's present, go inside it */
+                NameLink = CONTAINING_RECORD(RtlRightChild(&NameLink->Links),
+                                             FCB_NAME_LINK,
+                                             Links);
+            }
+        }
+    }
 }
 
-PVFATFCB
-vfatMakeRootFCB(PDEVICE_EXTENSION  pVCB)
+VOID
+NTAPI
+FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext,
+               IN PFCB Fcb)
 {
-       PVFATFCB  FCB;
-       ULONG FirstCluster, CurrentCluster, Size = 0;
-       NTSTATUS Status = STATUS_SUCCESS;
-       UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
-
-       FCB = vfatNewFCB(pVCB, &NameU);
-       if (FCB->Flags & FCB_IS_FATX_ENTRY)
-       {
-               memset(FCB->entry.FatX.Filename, ' ', 42);
-               FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
-               FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
-               FCB->entry.FatX.FirstCluster = 1;
-               Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
-       }
-       else
-       {
-               memset(FCB->entry.Fat.ShortName, ' ', 11);
-               FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
-               FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
-               if (pVCB->FatInfo.FatType == FAT32)
-               {
-                       CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
-                       FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
-                       FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
-
-                       while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
-                       {
-                               Size += pVCB->FatInfo.BytesPerCluster;
-                               Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
-                       }
-               }
-               else
-               {
-                       FCB->entry.Fat.FirstCluster = 1;
-                       Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
-               }
-       }
-       FCB->ShortHash.Hash = FCB->Hash.Hash;
-       FCB->RefCount = 2;
-       FCB->dirIndex = 0;
-       FCB->RFCB.FileSize.QuadPart = Size;
-       FCB->RFCB.ValidDataLength.QuadPart = Size;
-       FCB->RFCB.AllocationSize.QuadPart = Size;
-       FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
-
-       vfatFCBInitializeCacheFromVolume(pVCB, FCB);
-       vfatAddFCBToTable(pVCB, FCB);
-
-       return(FCB);
-}
+    PRTL_SPLAY_LINKS RootNew;
+    PFCB Parent;
 
-PVFATFCB
-vfatOpenRootFCB(PDEVICE_EXTENSION  pVCB)
-{
-       PVFATFCB  FCB;
-       UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
+    /* Reference the parent for simplicity */
+    Parent = Fcb->ParentFcb;
 
-       FCB = vfatGrabFCBFromTable (pVCB, &NameU);
-       if (FCB == NULL)
-       {
-               FCB = vfatMakeRootFCB (pVCB);
-       }
+    /* If this FCB hasn't been added to splay trees - just return */
+    if (!FlagOn( Fcb->State, FCB_STATE_HAS_NAMES ))
+        return;
 
-       return  FCB;
-}
+    /* Delete the short name link */
+    RootNew = RtlDelete(&Fcb->ShortName.Links);
 
-NTSTATUS
-vfatMakeFCBFromDirEntry(
-       PVCB  vcb,
-       PVFATFCB  directoryFCB,
-       PVFAT_DIRENTRY_CONTEXT DirContext,
-       PVFATFCB* fileFCB)
-{
-       PVFATFCB  rcFCB;
-       PWCHAR PathNameBuffer;
-       USHORT PathNameLength;
-       ULONG Size;
-       ULONG hash;
-
-       UNICODE_STRING NameU;
-
-       PathNameLength = directoryFCB->PathNameU.Length + max(DirContext->LongNameU.Length, DirContext->ShortNameU.Length);
-       if (!vfatFCBIsRoot (directoryFCB))
-       {
-               PathNameLength += sizeof(WCHAR);
-       }
-
-       if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR))
-       {
-               return  STATUS_OBJECT_NAME_INVALID;
-       }
-       PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB);
-       if (!PathNameBuffer)
-       {
-               return STATUS_INSUFFICIENT_RESOURCES;
-       }
-       NameU.Buffer = PathNameBuffer;
-       NameU.Length = 0;
-       NameU.MaximumLength = PathNameLength;
-
-       RtlCopyUnicodeString(&NameU, &directoryFCB->PathNameU);
-       if (!vfatFCBIsRoot (directoryFCB))
-       {
-               RtlAppendUnicodeToString(&NameU, L"\\");
-       }
-       hash = vfatNameHash(0, &NameU);
-       if (DirContext->LongNameU.Length > 0)
-       {
-               RtlAppendUnicodeStringToString(&NameU, &DirContext->LongNameU);
-       }
-       else
-       {
-               RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU);
-       }
-       NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0;
-
-       rcFCB = vfatNewFCB (vcb, &NameU);
-       RtlCopyMemory (&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
-       RtlCopyUnicodeString(&rcFCB->ShortNameU, &DirContext->ShortNameU);
-       if (vcb->Flags & VCB_IS_FATX)
-       {
-               rcFCB->ShortHash.Hash = rcFCB->Hash.Hash;
-       }
-       else
-       {
-               rcFCB->ShortHash.Hash = vfatNameHash(hash, &rcFCB->ShortNameU);
-       }
-
-       if (vfatFCBIsDirectory(rcFCB))
-       {
-               ULONG FirstCluster, CurrentCluster;
-               NTSTATUS Status;
-               Size = 0;
-               FirstCluster = vfatDirEntryGetFirstCluster (vcb, &rcFCB->entry);
-               if (FirstCluster == 1)
-               {
-                       Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
-               }
-               else if (FirstCluster != 0)
-               {
-                       CurrentCluster = FirstCluster;
-                       while (CurrentCluster != 0xffffffff)
-                       {
-                               Size += vcb->FatInfo.BytesPerCluster;
-                               Status = NextCluster (vcb, FirstCluster, &CurrentCluster, FALSE);
-                       }
-               }
-       }
-       else if (rcFCB->Flags & FCB_IS_FATX_ENTRY)
-       {
-               Size = rcFCB->entry.FatX.FileSize;
-       }
-       else
-       {
-               Size = rcFCB->entry.Fat.FileSize;
-       }
-       rcFCB->dirIndex = DirContext->DirIndex;
-       rcFCB->startIndex = DirContext->StartIndex;
-       if ((rcFCB->Flags & FCB_IS_FATX_ENTRY) && !vfatFCBIsRoot (directoryFCB))
-       {
-               ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
-               rcFCB->dirIndex = DirContext->DirIndex-2;
-               rcFCB->startIndex = DirContext->StartIndex-2;
-       }
-       rcFCB->RFCB.FileSize.QuadPart = Size;
-       rcFCB->RFCB.ValidDataLength.QuadPart = Size;
-       rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
-       rcFCB->RefCount++;
-       if (vfatFCBIsDirectory(rcFCB))
-       {
-               vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
-       }
-       rcFCB->parentFcb = directoryFCB;
-       vfatAddFCBToTable (vcb, rcFCB);
-       *fileFCB = rcFCB;
-
-       ExFreePool(PathNameBuffer);
-       return  STATUS_SUCCESS;
-}
+    /* Set the new root */
+    Parent->Dcb.SplayLinksAnsi = RootNew;
 
-NTSTATUS
-vfatAttachFCBToFileObject (
-       PDEVICE_EXTENSION  vcb,
-       PVFATFCB  fcb,
-       PFILE_OBJECT  fileObject)
-{
-       PVFATCCB  newCCB;
+    /* Deal with a unicode name if it exists */
+    if (FlagOn( Fcb->State, FCB_STATE_HAS_UNICODE_NAME ))
+    {
+        /* Delete the long unicode name link */
+        RootNew = RtlDelete(&Fcb->LongName.Links);
 
-       newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
-       if (newCCB == NULL)
-       {
-               return  STATUS_INSUFFICIENT_RESOURCES;
-       }
-       RtlZeroMemory (newCCB, sizeof (VFATCCB));
+        /* Set the new root */
+        Parent->Dcb.SplayLinksUnicode = RootNew;
 
-       fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
-       fileObject->FsContext = fcb;
-       fileObject->FsContext2 = newCCB;
-       DPRINT ("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
+        /* Free the long name string's buffer*/
+        RtlFreeUnicodeString(&Fcb->LongName.Name.String);
 
-       return  STATUS_SUCCESS;
-}
+        /* Clear the "has unicode name" flag */
+        ClearFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
+    }
 
-NTSTATUS
-vfatDirFindFile (
-       PDEVICE_EXTENSION  pDeviceExt,
-       PVFATFCB  pDirectoryFCB,
-       PUNICODE_STRING FileToFindU,
-       PVFATFCB * pFoundFCB)
-{
-       NTSTATUS  status;
-       PVOID Context = NULL;
-       PVOID Page = NULL;
-       BOOLEAN First = TRUE;
-       VFAT_DIRENTRY_CONTEXT DirContext;
-       /* This buffer must have a size of 260 characters, because
-       vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
-       WCHAR LongNameBuffer[260];
-       WCHAR ShortNameBuffer[13];
-       BOOLEAN FoundLong = FALSE;
-       BOOLEAN FoundShort = FALSE;
-
-       ASSERT(pDeviceExt);
-       ASSERT(pDirectoryFCB);
-       ASSERT(FileToFindU);
-
-       DPRINT ("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
-               pDeviceExt,
-               pDirectoryFCB,
-               FileToFindU);
-       DPRINT ("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
-
-       DirContext.DirIndex = 0;
-       DirContext.LongNameU.Buffer = LongNameBuffer;
-       DirContext.LongNameU.Length = 0;
-       DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
-       DirContext.ShortNameU.Buffer = ShortNameBuffer;
-       DirContext.ShortNameU.Length = 0;
-       DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
-
-       while (TRUE)
-       {
-               status = pDeviceExt->GetNextDirEntry(&Context,
-                       &Page,
-                       pDirectoryFCB,
-                       &DirContext,
-                       First);
-               First = FALSE;
-               if (status == STATUS_NO_MORE_ENTRIES)
-               {
-                       return STATUS_OBJECT_NAME_NOT_FOUND;
-               }
-               if (!NT_SUCCESS(status))
-               {
-                       return status;
-               }
-
-               DPRINT ("  Index:%d  longName:%wZ\n",
-                       DirContext.DirIndex,
-                       &DirContext.LongNameU);
-               DirContext.LongNameU.Buffer[DirContext.LongNameU.Length / sizeof(WCHAR)] = 0;
-               DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
-               if (!ENTRY_VOLUME(pDeviceExt, &DirContext.DirEntry))
-               {
-                       FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
-                       if (FoundLong == FALSE)
-                       {
-                               FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
-                       }
-                       if (FoundLong || FoundShort)
-                       {
-                               status = vfatMakeFCBFromDirEntry (pDeviceExt,
-                                       pDirectoryFCB,
-                                       &DirContext,
-                                       pFoundFCB);
-                               CcUnpinData(Context);
-                               return  status;
-                       }
-               }
-               DirContext.DirIndex++;
-       }
-
-       return  STATUS_OBJECT_NAME_NOT_FOUND;
+    /* This FCB has no names added to splay trees now */
+    ClearFlag(Fcb->State, FCB_STATE_HAS_NAMES);
 }
 
-NTSTATUS
-vfatGetFCBForFile (
-       PDEVICE_EXTENSION  pVCB,
-       PVFATFCB  *pParentFCB,
-       PVFATFCB  *pFCB,
-       PUNICODE_STRING pFileNameU)
-{
-       NTSTATUS  status;
-       PVFATFCB  FCB = NULL;
-       PVFATFCB  parentFCB;
-       UNICODE_STRING NameU;
-       UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
-        UNICODE_STRING FileNameU;
-        WCHAR NameBuffer[260];
-       PWCHAR curr, prev, last;
-       ULONG Length;
-
-       DPRINT ("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
-               pVCB,
-               pParentFCB,
-               pFCB,
-               pFileNameU);
-
-        FileNameU.Buffer = NameBuffer;
-        FileNameU.MaximumLength = sizeof(NameBuffer);
-        RtlCopyUnicodeString(&FileNameU, pFileNameU);
-
-       parentFCB = *pParentFCB;
-
-       if (parentFCB == NULL)
-       {
-               //  Trivial case, open of the root directory on volume
-               if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
-               {
-                       DPRINT ("returning root FCB\n");
-
-                       FCB = vfatOpenRootFCB (pVCB);
-                       *pFCB = FCB;
-                       *pParentFCB = NULL;
-
-                       return  (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-
-               /* Check for an existing FCB */
-               FCB = vfatGrabFCBFromTable (pVCB, &FileNameU);
-               if (FCB)
-               {
-                       *pFCB = FCB;
-                       *pParentFCB = FCB->parentFcb;
-                       (*pParentFCB)->RefCount++;
-                       return STATUS_SUCCESS;
-               }
-
-               last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
-               while (*curr != L'\\' && curr > FileNameU.Buffer)
-               {
-                       curr--;
-               }
-
-               if (curr > FileNameU.Buffer)
-               {
-                       NameU.Buffer = FileNameU.Buffer;
-                       NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
-                       FCB = vfatGrabFCBFromTable(pVCB, &NameU);
-                       if (FCB)
-                       {
-                               Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
-                               if (Length != FCB->PathNameU.Length)
-                               {
-                                       if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
-                                       {
-                                               vfatReleaseFCB (pVCB, FCB);
-                                               return STATUS_OBJECT_NAME_INVALID;
-                                       }
-                                       RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
-                                               curr, FileNameU.Length - Length);
-                                       FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
-                                       curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
-                                       last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
-                               }
-                               RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
-                       }
-               }
-               else
-               {
-                       FCB = NULL;
-               }
-
-               if (FCB == NULL)
-               {
-                       FCB = vfatOpenRootFCB(pVCB);
-                       curr = FileNameU.Buffer;
-               }
-
-               parentFCB = NULL;
-               prev = curr;
-       }
-       else
-       {
-               FCB = parentFCB;
-               parentFCB = NULL;
-               prev = curr = FileNameU.Buffer - 1;
-               last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
-       }
-
-       while (curr <= last)
-       {
-               if (parentFCB)
-               {
-                       vfatReleaseFCB (pVCB, parentFCB);
-                       parentFCB = 0;
-               }
-               //  fail if element in FCB is not a directory
-               if (!vfatFCBIsDirectory (FCB))
-               {
-                       DPRINT ("Element in requested path is not a directory\n");
-
-                       vfatReleaseFCB (pVCB, FCB);
-                       FCB = NULL;
-                       *pParentFCB = NULL;
-                       *pFCB = NULL;
-
-                       return  STATUS_OBJECT_PATH_NOT_FOUND;
-               }
-               parentFCB = FCB;
-               if (prev < curr)
-               {
-                       Length = (curr - prev) * sizeof(WCHAR);
-                       if (Length != parentFCB->LongNameU.Length)
-                       {
-                               if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
-                               {
-                                       vfatReleaseFCB (pVCB, parentFCB);
-                                       return STATUS_OBJECT_NAME_INVALID;
-                               }
-                               RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
-                                       FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
-                               FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
-                               curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
-                               last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
-                       }
-                       RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
-               }
-               curr++;
-               prev = curr;
-               while (*curr != L'\\' && curr <= last)
-               {
-                       curr++;
-               }
-               NameU.Buffer = FileNameU.Buffer;
-               NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
-               NameU.MaximumLength = FileNameU.MaximumLength;
-               DPRINT("%wZ\n", &NameU);
-               FCB = vfatGrabFCBFromTable(pVCB, &NameU);
-               if (FCB == NULL)
-               {
-                       NameU.Buffer = prev;
-                       NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
-                       status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
-                       if (status == STATUS_OBJECT_NAME_NOT_FOUND)
-                       {
-                               *pFCB = NULL;
-                               if (curr > last)
-                               {
-                                       *pParentFCB = parentFCB;
-                                       return  STATUS_OBJECT_NAME_NOT_FOUND;
-                               }
-                               else
-                               {
-                                       vfatReleaseFCB (pVCB, parentFCB);
-                                       *pParentFCB = NULL;
-                                       return  STATUS_OBJECT_PATH_NOT_FOUND;
-                               }
-                       }
-                       else if (!NT_SUCCESS (status))
-                       {
-                               vfatReleaseFCB (pVCB, parentFCB);
-                               *pParentFCB = NULL;
-                               *pFCB = NULL;
-
-                               return  status;
-                       }
-               }
-       }
-
-       *pParentFCB = parentFCB;
-       *pFCB = FCB;
-
-       return  STATUS_SUCCESS;
-}
 
+/* EOF */