[FASTFAT]
[reactos.git] / reactos / drivers / filesystems / fastfat / fcb.c
index 251821b..f8b0a48 100644 (file)
@@ -153,6 +153,7 @@ vfatNewFCB(
     rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource;
     rcFCB->RFCB.Resource = &rcFCB->MainResource;
     rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
+    InitializeListHead(&rcFCB->ParentListHead);
 
     return  rcFCB;
 }
@@ -271,6 +272,8 @@ vfatDestroyFCB(
     ExDeleteResourceLite(&pFCB->PagingIoResource);
     ExDeleteResourceLite(&pFCB->MainResource);
     ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
+    RemoveEntryList(&pFCB->ParentListEntry);
+    ASSERT(IsListEmpty(&pFCB->ParentListHead));
 }
 
 BOOLEAN
@@ -287,6 +290,18 @@ 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,
@@ -297,8 +312,12 @@ vfatReleaseFCB(
     DPRINT("releasing FCB at %p: %wZ, refCount:%d\n",
            pFCB, &pFCB->PathNameU, pFCB->RefCount);
 
+    ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
+
     while (pFCB)
     {
+        ASSERT(pFCB != pVCB->VolumeFcb);
+        ASSERT(pFCB->RefCount > 0);
         pFCB->RefCount--;
         if (pFCB->RefCount == 0)
         {
@@ -338,22 +357,90 @@ vfatAddFCBToTable(
     }
     if (pFCB->parentFcb)
     {
-        pFCB->parentFcb->RefCount++;
+        vfatGrabFCB(pVCB, pFCB->parentFcb);
+    }
+}
+
+static
+VOID
+vfatInitFCBFromDirEntry(
+    PDEVICE_EXTENSION Vcb,
+    PVFATFCB Fcb,
+    PVFAT_DIRENTRY_CONTEXT DirContext)
+{
+    ULONG Size;
+
+    RtlCopyMemory(&Fcb->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
+    RtlCopyUnicodeString(&Fcb->ShortNameU, &DirContext->ShortNameU);
+    if (Vcb->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);
+    }
+
+    if (vfatFCBIsDirectory(Fcb))
+    {
+        ULONG FirstCluster, CurrentCluster;
+        NTSTATUS Status = STATUS_SUCCESS;
+        Size = 0;
+        FirstCluster = vfatDirEntryGetFirstCluster(Vcb, &Fcb->entry);
+        if (FirstCluster == 1)
+        {
+            Size = Vcb->FatInfo.rootDirectorySectors * Vcb->FatInfo.BytesPerSector;
+        }
+        else if (FirstCluster != 0)
+        {
+            CurrentCluster = FirstCluster;
+            while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
+            {
+                Size += Vcb->FatInfo.BytesPerCluster;
+                Status = NextCluster(Vcb, FirstCluster, &CurrentCluster, FALSE);
+            }
+        }
+    }
+    else if (Fcb->Flags & FCB_IS_FATX_ENTRY)
+    {
+        Size = Fcb->entry.FatX.FileSize;
     }
+    else
+    {
+        Size = Fcb->entry.Fat.FileSize;
+    }
+    Fcb->dirIndex = DirContext->DirIndex;
+    Fcb->startIndex = DirContext->StartIndex;
+    if ((Fcb->Flags & FCB_IS_FATX_ENTRY) && !vfatFCBIsRoot(Fcb))
+    {
+        ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
+        Fcb->dirIndex = DirContext->DirIndex-2;
+        Fcb->startIndex = DirContext->StartIndex-2;
+    }
+    Fcb->RFCB.FileSize.QuadPart = Size;
+    Fcb->RFCB.ValidDataLength.QuadPart = Size;
+    Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, Vcb->FatInfo.BytesPerCluster);
 }
 
 NTSTATUS
 vfatUpdateFCB(
     PDEVICE_EXTENSION pVCB,
     PVFATFCB Fcb,
-    PUNICODE_STRING LongName,
-    PUNICODE_STRING ShortName,
+    PVFAT_DIRENTRY_CONTEXT DirContext,
     PVFATFCB ParentFcb)
 {
     NTSTATUS Status;
     PVFATFCB OldParent;
 
-    DPRINT("vfatUpdateFCB(%p, %p, %wZ, %wZ, %p)\n", pVCB, Fcb, LongName, ShortName, ParentFcb);
+    DPRINT("vfatUpdateFCB(%p, %p, %p, %p)\n", pVCB, Fcb, DirContext, ParentFcb);
+
+    /* Get full path name */
+    Status = vfatMakeFullName(ParentFcb, &DirContext->LongNameU, &DirContext->ShortNameU, &Fcb->PathNameU);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
     /* Delete old name */
     if (Fcb->PathNameBuffer)
@@ -364,51 +451,31 @@ vfatUpdateFCB(
     /* Delete from table */
     vfatDelFCBFromTable(pVCB, Fcb);
 
-    /* Get full path name */
-    Status = vfatMakeFullName(ParentFcb, LongName, ShortName, &Fcb->PathNameU);
-    if (!NT_SUCCESS(Status))
-    {
-        return Status;
-    }
-
     /* 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);
+    /* Save old parent */
+    OldParent = Fcb->parentFcb;
+    RemoveEntryList(&Fcb->ParentListEntry);
 
-    /* Recompute hashes */
-    Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
-    if (pVCB->Flags & VCB_IS_FATX)
-    {
-        Fcb->ShortHash.Hash = Fcb->Hash.Hash;
-    }
-    else
+    /* Reinit FCB */
+    vfatInitFCBFromDirEntry(pVCB, Fcb, DirContext);
+
+    if (vfatFCBIsDirectory(Fcb))
     {
-        Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
-        Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
+        CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
     }
-
-    /* Set parent */
-    OldParent = Fcb->parentFcb;
     Fcb->parentFcb = ParentFcb;
-
-    /* Add to the table */
+    InsertTailList(&ParentFcb->ParentListHead, &Fcb->ParentListEntry);
     vfatAddFCBToTable(pVCB, Fcb);
 
-    /* If we moved accross directories, dereferenced our old parent
-     * We also derefence in case we're just renaming since AddFCBToTable references it
+    /* 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);
 
-    /* In case we were moving accross directories, reset caching on old parent */
-    //if (OldParent != ParentFcb)
-    //{
-    //    CcUninitializeCacheMap(OldParent->FileObject, NULL, NULL);
-    //}
-
     return STATUS_SUCCESS;
 }
 
@@ -427,6 +494,7 @@ vfatGrabFCBFromTable(
 
     DPRINT("'%wZ'\n", PathNameU);
 
+    ASSERT(PathNameU->Length >= sizeof(WCHAR) && PathNameU->Buffer[0] == L'\\');
     Hash = vfatNameHash(0, PathNameU);
 
     entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize];
@@ -455,7 +523,7 @@ vfatGrabFCBFromTable(
                 DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
                 if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
                 {
-                    rcFCB->RefCount++;
+                    vfatGrabFCB(pVCB, rcFCB);
                     return rcFCB;
                 }
             }
@@ -489,7 +557,6 @@ vfatFCBInitializeCacheFromVolume(
     fileObject->FsContext = fcb;
     fileObject->FsContext2 = newCCB;
     fcb->FileObject = fileObject;
-    fcb->RefCount++;
 
     _SEH2_TRY
     {
@@ -502,7 +569,6 @@ vfatFCBInitializeCacheFromVolume(
     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
         status = _SEH2_GetExceptionCode();
-        fcb->RefCount--;
         fcb->FileObject = NULL;
         ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, newCCB);
         ObDereferenceObject(fileObject);
@@ -510,6 +576,7 @@ vfatFCBInitializeCacheFromVolume(
     }
     _SEH2_END;
 
+    vfatGrabFCB(vcb, fcb);
     fcb->Flags |= FCB_CACHE_INITIALIZED;
     return STATUS_SUCCESS;
 }
@@ -593,7 +660,6 @@ vfatMakeFCBFromDirEntry(
     PVFATFCB *fileFCB)
 {
     PVFATFCB rcFCB;
-    ULONG Size;
     UNICODE_STRING NameU;
     NTSTATUS Status;
 
@@ -604,63 +670,15 @@ vfatMakeFCBFromDirEntry(
     }
 
     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(0, &rcFCB->DirNameU);
-        rcFCB->ShortHash.Hash = vfatNameHash(rcFCB->ShortHash.Hash, &rcFCB->ShortNameU);
-    }
+    vfatInitFCBFromDirEntry(vcb, rcFCB, DirContext);
 
-    if (vfatFCBIsDirectory(rcFCB))
-    {
-        ULONG FirstCluster, CurrentCluster;
-        NTSTATUS Status = STATUS_SUCCESS;
-        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 && NT_SUCCESS(Status))
-            {
-                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++;
+    rcFCB->RefCount = 1;
     if (vfatFCBIsDirectory(rcFCB))
     {
         vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
     }
     rcFCB->parentFcb = directoryFCB;
+    InsertTailList(&directoryFCB->ParentListHead, &rcFCB->ParentListEntry);
     vfatAddFCBToTable(vcb, rcFCB);
     *fileFCB = rcFCB;
 
@@ -750,6 +768,18 @@ vfatDirFindFile(
 
         if (!ENTRY_VOLUME(pDeviceExt, &DirContext.DirEntry))
         {
+            if (DirContext.LongNameU.Length == 0 ||
+                DirContext.ShortNameU.Length == 0)
+            {
+                DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
+                if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
+                {
+                    ASSERT(DirContext.LongNameU.Length != 0 &&
+                           DirContext.ShortNameU.Length != 0);
+                }
+                DirContext.DirIndex++;
+                continue;
+            }
             FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
             if (FoundLong == FALSE)
             {
@@ -791,14 +821,15 @@ vfatGetFCBForFile(
     DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
            pVCB, pParentFCB, pFCB, pFileNameU);
 
-    FileNameU.Buffer = NameBuffer;
-    FileNameU.MaximumLength = sizeof(NameBuffer);
-    RtlCopyUnicodeString(&FileNameU, pFileNameU);
+    RtlInitEmptyUnicodeString(&FileNameU, NameBuffer, sizeof(NameBuffer));
 
     parentFCB = *pParentFCB;
 
     if (parentFCB == NULL)
     {
+        /* Passed-in name is the full name */
+        RtlCopyUnicodeString(&FileNameU, pFileNameU);
+
         //  Trivial case, open of the root directory on volume
         if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
         {
@@ -817,7 +848,7 @@ vfatGetFCBForFile(
         {
             *pFCB = FCB;
             *pParentFCB = FCB->parentFcb;
-            (*pParentFCB)->RefCount++;
+            vfatGrabFCB(pVCB, *pParentFCB);
             return STATUS_SUCCESS;
         }
 
@@ -867,9 +898,20 @@ vfatGetFCBForFile(
     }
     else
     {
+        /* Make absolute path */
+        RtlCopyUnicodeString(&FileNameU, &parentFCB->PathNameU);
+        curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
+        if (*curr != L'\\')
+        {
+            RtlAppendUnicodeToString(&FileNameU, L"\\");
+            curr++;
+        }
+        ASSERT(*curr == L'\\');
+        RtlAppendUnicodeStringToString(&FileNameU, pFileNameU);
+
         FCB = parentFCB;
         parentFCB = NULL;
-        prev = curr = FileNameU.Buffer - 1;
+        prev = curr;
         last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
     }
 
@@ -878,7 +920,7 @@ vfatGetFCBForFile(
         if (parentFCB)
         {
             vfatReleaseFCB(pVCB, parentFCB);
-            parentFCB = 0;
+            parentFCB = NULL;
         }
         //  fail if element in FCB is not a directory
         if (!vfatFCBIsDirectory(FCB))
@@ -901,6 +943,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,