/** @file
  Function that deletes a file.

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


**/

#include "Fat.h"

/**

  Deletes the file & Closes the file handle.

  @param  FHand                    - Handle to the file to delete.

  @retval EFI_SUCCESS              - Delete the file successfully.
  @retval EFI_WARN_DELETE_FAILURE  - Fail to delete the file.

**/
EFI_STATUS
EFIAPI
FatDelete (
  IN EFI_FILE_PROTOCOL  *FHand
  )
{
  FAT_IFILE   *IFile;
  FAT_OFILE   *OFile;
  FAT_DIRENT  *DirEnt;
  EFI_STATUS  Status;
  UINTN       Round;

  IFile = IFILE_FROM_FHAND (FHand);
  OFile = IFile->OFile;

  FatWaitNonblockingTask (IFile);

  //
  // Lock the volume
  //
  FatAcquireLock ();

  //
  // If the file is read-only, then don't delete it
  //
  if (IFile->ReadOnly) {
    Status = EFI_WRITE_PROTECTED;
    goto Done;
  }
  //
  // If the file is the root dir, then don't delete it
  //
  if (OFile->Parent == NULL) {
    Status = EFI_ACCESS_DENIED;
    goto Done;
  }
  //
  // If the file has a permanent error, skip the delete
  //
  Status = OFile->Error;
  if (!EFI_ERROR (Status)) {
    //
    // If this is a directory, make sure it's empty before
    // allowing it to be deleted
    //
    if (OFile->ODir != NULL) {
      //
      // We do not allow to delete nonempty directory
      //
      FatResetODirCursor (OFile);
      for (Round = 0; Round < 3; Round++) {
        Status = FatGetNextDirEnt (OFile, &DirEnt);
        if ((EFI_ERROR (Status)) ||
            ((Round < 2) && (DirEnt == NULL || !FatIsDotDirEnt (DirEnt))) ||
            ((Round == 2) && (DirEnt != NULL))
            ) {
          Status = EFI_ACCESS_DENIED;
          goto Done;
        }
      }
    }
    //
    // Return the file's space by setting its size to 0
    //
    FatTruncateOFile (OFile, 0);
    //
    // Free the directory entry for this file
    //
    Status = FatRemoveDirEnt (OFile->Parent, OFile->DirEnt);
    if (EFI_ERROR (Status)) {
      goto Done;
    }
    //
    // Set a permanent error for this OFile in case there
    // are still opened IFiles attached
    //
    OFile->Error = EFI_NOT_FOUND;
  } else if (OFile->Error == EFI_NOT_FOUND) {
    Status = EFI_SUCCESS;
  }

Done:
  //
  // Always close the handle
  //
  FatIFileClose (IFile);
  //
  // Done
  //
  Status = FatCleanupVolume (OFile->Volume, NULL, Status, NULL);
  FatReleaseLock ();

  if (EFI_ERROR (Status)) {
    Status = EFI_WARN_DELETE_FAILURE;
  }

  return Status;
}
