* Sync up to trunk head (r65298).
[reactos.git] / drivers / filesystems / fastfat / fcb.c
index fad85a2..df4dd54 100644 (file)
@@ -1,11 +1,12 @@
 /*
-* FILE:             drivers/fs/vfat/fcb.c
+* FILE:             drivers/filesystems/fastfat/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)
+*                   Pierre Schweitzer (pierre@reactos.org)
 */
 
 /*  -------------------------------------------------------  INCLUDES  */
@@ -156,6 +157,100 @@ vfatNewFCB(
     return  rcFCB;
 }
 
+static
+VOID
+vfatDelFCBFromTable(
+    PDEVICE_EXTENSION pVCB,
+    PVFATFCB pFCB)
+{
+    ULONG Index;
+    ULONG ShortIndex;
+    HASHENTRY* entry;
+
+    Index = pFCB->Hash.Hash % pVCB->HashTableSize;
+    ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
+
+    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;
+    }
+
+    RemoveEntryList(&pFCB->FcbListEntry);
+}
+
+static
+NTSTATUS
+vfatMakeFullName(
+    PVFATFCB directoryFCB,
+    PUNICODE_STRING LongNameU,
+    PUNICODE_STRING ShortNameU,
+    PUNICODE_STRING NameU)
+{
+    PWCHAR PathNameBuffer;
+    USHORT PathNameLength;
+
+    PathNameLength = directoryFCB->PathNameU.Length + max(LongNameU->Length, 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"\\");
+    }
+    if (LongNameU->Length > 0)
+    {
+        RtlAppendUnicodeStringToString(NameU, LongNameU);
+    }
+    else
+    {
+        RtlAppendUnicodeStringToString(NameU, ShortNameU);
+    }
+    NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
+
+    return STATUS_SUCCESS;
+}
+
 VOID
 vfatDestroyCCB(
     PVFATCCB pCcb)
@@ -182,7 +277,7 @@ BOOLEAN
 vfatFCBIsDirectory(
     PVFATFCB FCB)
 {
-    return *FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY;
+    return ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY);
 }
 
 BOOLEAN
@@ -192,57 +287,40 @@ vfatFCBIsRoot(
     return FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE;
 }
 
+VOID
+vfatGrabFCB(
+    PDEVICE_EXTENSION pVCB,
+    PVFATFCB pFCB)
+{
+    ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
+
+    ASSERT(pFCB != pVCB->VolumeFcb);
+    ASSERT(pFCB->RefCount > 0);
+    ++pFCB->RefCount;
+}
+
 VOID
 vfatReleaseFCB(
     PDEVICE_EXTENSION pVCB,
     PVFATFCB pFCB)
 {
-    HASHENTRY* entry;
-    ULONG Index;
-    ULONG ShortIndex;
     PVFATFCB tmpFcb;
 
     DPRINT("releasing FCB at %p: %wZ, refCount:%d\n",
            pFCB, &pFCB->PathNameU, pFCB->RefCount);
 
+    ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
+
     while (pFCB)
     {
-        Index = pFCB->Hash.Hash % pVCB->HashTableSize;
-        ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
+        ASSERT(pFCB != pVCB->VolumeFcb);
+        ASSERT(pFCB->RefCount > 0);
         pFCB->RefCount--;
         if (pFCB->RefCount == 0)
         {
+            ASSERT(pFCB->OpenHandleCount == 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;
-            }
+            vfatDelFCBFromTable(pVCB, pFCB);
             vfatDestroyFCB(pFCB);
         }
         else
@@ -253,6 +331,7 @@ vfatReleaseFCB(
     }
 }
 
+static
 VOID
 vfatAddFCBToTable(
     PDEVICE_EXTENSION pVCB,
@@ -275,10 +354,74 @@ vfatAddFCBToTable(
     }
     if (pFCB->parentFcb)
     {
-        pFCB->parentFcb->RefCount++;
+        vfatGrabFCB(pVCB, pFCB->parentFcb);
     }
 }
 
+NTSTATUS
+vfatUpdateFCB(
+    PDEVICE_EXTENSION pVCB,
+    PVFATFCB Fcb,
+    PUNICODE_STRING LongName,
+    PUNICODE_STRING ShortName,
+    PVFATFCB ParentFcb)
+{
+    NTSTATUS Status;
+    PVFATFCB OldParent;
+
+    DPRINT("vfatUpdateFCB(%p, %p, %wZ, %wZ, %p)\n", pVCB, Fcb, LongName, ShortName, ParentFcb);
+
+    /* Get full path name */
+    Status = vfatMakeFullName(ParentFcb, LongName, ShortName, &Fcb->PathNameU);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    /* Delete old name */
+    if (Fcb->PathNameBuffer)
+    {
+        ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB);
+    }
+
+    /* Delete from table */
+    vfatDelFCBFromTable(pVCB, Fcb);
+
+    /* Split it properly */
+    Fcb->PathNameBuffer = Fcb->PathNameU.Buffer;
+    Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
+    vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
+
+    /* Copy short name */
+    RtlCopyUnicodeString(&Fcb->ShortNameU, ShortName);
+
+    /* Recompute hashes */
+    Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
+    if (pVCB->Flags & VCB_IS_FATX)
+    {
+        Fcb->ShortHash.Hash = Fcb->Hash.Hash;
+    }
+    else
+    {
+        Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
+        Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
+    }
+
+    /* Set parent */
+    OldParent = Fcb->parentFcb;
+    Fcb->parentFcb = ParentFcb;
+
+    /* Add to the table */
+    vfatAddFCBToTable(pVCB, Fcb);
+
+    /* If we moved across directories, dereference our old parent
+     * We also dereference in case we're just renaming since AddFCBToTable references it
+     */
+    vfatReleaseFCB(pVCB, OldParent);
+
+    return STATUS_SUCCESS;
+}
+
 PVFATFCB
 vfatGrabFCBFromTable(
     PDEVICE_EXTENSION pVCB,
@@ -322,7 +465,7 @@ vfatGrabFCBFromTable(
                 DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
                 if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
                 {
-                    rcFCB->RefCount++;
+                    vfatGrabFCB(pVCB, rcFCB);
                     return rcFCB;
                 }
             }
@@ -356,7 +499,6 @@ vfatFCBInitializeCacheFromVolume(
     fileObject->FsContext = fcb;
     fileObject->FsContext2 = newCCB;
     fcb->FileObject = fileObject;
-    fcb->RefCount++;
 
     _SEH2_TRY
     {
@@ -369,7 +511,6 @@ vfatFCBInitializeCacheFromVolume(
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         status = _SEH2_GetExceptionCode();
-        fcb->RefCount--;
         fcb->FileObject = NULL;
         ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, newCCB);
         ObDereferenceObject(fileObject);
@@ -377,6 +518,7 @@ vfatFCBInitializeCacheFromVolume(
     }
     _SEH2_END;
 
+    vfatGrabFCB(vcb, fcb);
     fcb->Flags |= FCB_CACHE_INITIALIZED;
     return STATUS_SUCCESS;
 }
@@ -460,47 +602,15 @@ vfatMakeFCBFromDirEntry(
     PVFATFCB *fileFCB)
 {
     PVFATFCB rcFCB;
-    PWCHAR PathNameBuffer;
-    USHORT PathNameLength;
     ULONG Size;
-    ULONG hash;
-
     UNICODE_STRING NameU;
+    NTSTATUS Status;
 
-    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)
+    Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU);
+    if (!NT_SUCCESS(Status))
     {
-        RtlAppendUnicodeStringToString(&NameU, &DirContext->LongNameU);
+        return Status;
     }
-    else
-    {
-        RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU);
-    }
-    NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0;
 
     rcFCB = vfatNewFCB(vcb, &NameU);
     RtlCopyMemory(&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
@@ -511,7 +621,8 @@ vfatMakeFCBFromDirEntry(
     }
     else
     {
-        rcFCB->ShortHash.Hash = vfatNameHash(hash, &rcFCB->ShortNameU);
+        rcFCB->ShortHash.Hash = vfatNameHash(0, &rcFCB->DirNameU);
+        rcFCB->ShortHash.Hash = vfatNameHash(rcFCB->ShortHash.Hash, &rcFCB->ShortNameU);
     }
 
     if (vfatFCBIsDirectory(rcFCB))
@@ -553,7 +664,7 @@ vfatMakeFCBFromDirEntry(
     rcFCB->RFCB.FileSize.QuadPart = Size;
     rcFCB->RFCB.ValidDataLength.QuadPart = Size;
     rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
-    rcFCB->RefCount++;
+    rcFCB->RefCount = 1;
     if (vfatFCBIsDirectory(rcFCB))
     {
         vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
@@ -562,7 +673,7 @@ vfatMakeFCBFromDirEntry(
     vfatAddFCBToTable(vcb, rcFCB);
     *fileFCB = rcFCB;
 
-    ExFreePool(PathNameBuffer);
+    ExFreePool(NameU.Buffer);
     return STATUS_SUCCESS;
 }
 
@@ -715,7 +826,7 @@ vfatGetFCBForFile(
         {
             *pFCB = FCB;
             *pParentFCB = FCB->parentFcb;
-            (*pParentFCB)->RefCount++;
+            vfatGrabFCB(pVCB, *pParentFCB);
             return STATUS_SUCCESS;
         }
 
@@ -776,7 +887,7 @@ vfatGetFCBForFile(
         if (parentFCB)
         {
             vfatReleaseFCB(pVCB, parentFCB);
-            parentFCB = 0;
+            parentFCB = NULL;
         }
         //  fail if element in FCB is not a directory
         if (!vfatFCBIsDirectory(FCB))
@@ -799,6 +910,8 @@ vfatGetFCBForFile(
                 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
                 {
                     vfatReleaseFCB(pVCB, parentFCB);
+                    *pParentFCB = NULL;
+                    *pFCB = NULL;
                     return STATUS_OBJECT_NAME_INVALID;
                 }
                 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,