/** @file
  Functions for directory cache operation.

Copyright (c) 2005, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "Fat.h"

/**

  Free the directory structure and release the memory.

  @param  ODir                  - The directory to be freed.

**/
STATIC
VOID
FatFreeODir (
  IN FAT_ODIR  *ODir
  )
{
  FAT_DIRENT  *DirEnt;

  //
  // Release Directory Entry Nodes
  //
  while (!IsListEmpty (&ODir->ChildList)) {
    DirEnt = DIRENT_FROM_LINK (ODir->ChildList.ForwardLink);
    RemoveEntryList (&DirEnt->Link);
    //
    // Make sure the OFile has been closed
    //
    ASSERT (DirEnt->OFile == NULL);
    FatFreeDirEnt (DirEnt);
  }

  FreePool (ODir);
}

/**

  Allocate the directory structure.

  @param  OFile                   - The corresponding OFile.

**/
STATIC
FAT_ODIR *
FatAllocateODir (
  IN FAT_OFILE  *OFile
  )
{
  FAT_ODIR  *ODir;

  ODir = AllocateZeroPool (sizeof (FAT_ODIR));
  if (ODir != NULL) {
    //
    // Initialize the directory entry list
    //
    ODir->Signature = FAT_ODIR_SIGNATURE;
    InitializeListHead (&ODir->ChildList);
    ODir->CurrentCursor = &ODir->ChildList;
  }

  return ODir;
}

/**

  Discard the directory structure when an OFile will be freed.
  Volume will cache this directory if the OFile does not represent a deleted file.

  @param  OFile                 - The OFile whose directory structure is to be discarded.

**/
VOID
FatDiscardODir (
  IN FAT_OFILE  *OFile
  )
{
  FAT_ODIR    *ODir;
  FAT_VOLUME  *Volume;

  Volume = OFile->Volume;
  ODir   = OFile->ODir;
  if (!OFile->DirEnt->Invalid) {
    //
    // If OFile does not represent a deleted file, then we will cache the directory
    // We use OFile's first cluster as the directory's tag
    //
    ODir->DirCacheTag = OFile->FileCluster;
    InsertHeadList (&Volume->DirCacheList, &ODir->DirCacheLink);
    if (Volume->DirCacheCount == FAT_MAX_DIR_CACHE_COUNT) {
      //
      // Replace the least recent used directory
      //
      ODir = ODIR_FROM_DIRCACHELINK (Volume->DirCacheList.BackLink);
      RemoveEntryList (&ODir->DirCacheLink);
    } else {
      //
      // No need to find a replace
      //
      Volume->DirCacheCount++;
      ODir = NULL;
    }
  }

  //
  // Release ODir Structure
  //
  if (ODir != NULL) {
    FatFreeODir (ODir);
  }
}

/**


  Request the directory structure when an OFile is newly generated.
  If the directory structure is cached by volume, then just return this directory;
  Otherwise, allocate a new one for OFile.

  @param  OFile                 - The OFile which requests directory structure.

**/
VOID
FatRequestODir (
  IN FAT_OFILE  *OFile
  )
{
  UINTN       DirCacheTag;
  FAT_VOLUME  *Volume;
  FAT_ODIR    *ODir;
  FAT_ODIR    *CurrentODir;
  LIST_ENTRY  *CurrentODirLink;

  Volume      = OFile->Volume;
  ODir        = NULL;
  DirCacheTag = OFile->FileCluster;
  for (CurrentODirLink  = Volume->DirCacheList.ForwardLink;
       CurrentODirLink != &Volume->DirCacheList;
       CurrentODirLink  = CurrentODirLink->ForwardLink
       )
  {
    CurrentODir = ODIR_FROM_DIRCACHELINK (CurrentODirLink);
    if (CurrentODir->DirCacheTag == DirCacheTag) {
      RemoveEntryList (&CurrentODir->DirCacheLink);
      Volume->DirCacheCount--;
      ODir = CurrentODir;
      break;
    }
  }

  if (ODir == NULL) {
    //
    // This directory is not cached, then allocate a new one
    //
    ODir = FatAllocateODir (OFile);
  }

  OFile->ODir = ODir;
}

/**

  Clean up all the cached directory structures when the volume is going to be abandoned.

  @param  Volume                - FAT file system volume.

**/
VOID
FatCleanupODirCache (
  IN FAT_VOLUME  *Volume
  )
{
  FAT_ODIR  *ODir;

  while (Volume->DirCacheCount > 0) {
    ODir = ODIR_FROM_DIRCACHELINK (Volume->DirCacheList.BackLink);
    RemoveEntryList (&ODir->DirCacheLink);
    FatFreeODir (ODir);
    Volume->DirCacheCount--;
  }
}