text: /** @file
    Routines dealing with setting/getting file/volume info
  
  Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
  
  
  
  **/
  
  #include "Fat.h"
  
  /**
  
    Get the volume's info into Buffer.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing volume info.
  
    @retval EFI_SUCCESS           - Get the volume info successfully.
    @retval EFI_BUFFER_TOO_SMALL  - The buffer is too small.
  
  **/
  EFI_STATUS
  FatGetVolumeInfo (
    IN FAT_VOLUME  *Volume,
    IN OUT UINTN   *BufferSize,
    OUT VOID       *Buffer
    );
  
  /**
  
    Set the volume's info.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing the new volume info.
  
    @retval EFI_SUCCESS           - Set the volume info successfully.
    @retval EFI_BAD_BUFFER_SIZE   - The buffer size is error.
    @retval EFI_WRITE_PROTECTED   - The volume is read only.
    @return other                 - An error occurred when operation the disk.
  
  **/
  EFI_STATUS
  FatSetVolumeInfo (
    IN FAT_VOLUME  *Volume,
    IN UINTN       BufferSize,
    IN VOID        *Buffer
    );
  
  /**
  
    Set or Get the some types info of the file into Buffer.
  
    @param  IsSet      - TRUE:The access is set, else is get
    @param  FHand      - The handle of file
    @param  Type       - The type of the info
    @param  BufferSize - Size of Buffer
    @param  Buffer     - Buffer containing volume info
  
    @retval EFI_SUCCESS       - Get the info successfully
    @retval EFI_DEVICE_ERROR  - Can not find the OFile for the file
  
  **/
  EFI_STATUS
  FatSetOrGetInfo (
    IN BOOLEAN            IsSet,
    IN EFI_FILE_PROTOCOL  *FHand,
    IN EFI_GUID           *Type,
    IN OUT UINTN          *BufferSize,
    IN OUT VOID           *Buffer
    );
  
  /**
  
    Get the open file's info into Buffer.
  
    @param  OFile                 - The open file.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing file info.
  
    @retval EFI_SUCCESS           - Get the file info successfully.
    @retval EFI_BUFFER_TOO_SMALL  - The buffer is too small.
  
  **/
  EFI_STATUS
  FatGetFileInfo (
    IN FAT_OFILE  *OFile,
    IN OUT UINTN  *BufferSize,
    OUT VOID      *Buffer
    )
  {
    return FatGetDirEntInfo (OFile->Volume, OFile->DirEnt, BufferSize, Buffer);
  }
  
  /**
  
    Get the volume's info into Buffer.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing volume info.
  
    @retval EFI_SUCCESS           - Get the volume info successfully.
    @retval EFI_BUFFER_TOO_SMALL  - The buffer is too small.
  
  **/
  EFI_STATUS
  FatGetVolumeInfo (
    IN     FAT_VOLUME  *Volume,
    IN OUT UINTN       *BufferSize,
    OUT VOID           *Buffer
    )
  {
    UINTN                 Size;
    UINTN                 NameSize;
    UINTN                 ResultSize;
    CHAR16                Name[FAT_NAME_LEN + 1];
    EFI_STATUS            Status;
    EFI_FILE_SYSTEM_INFO  *Info;
    UINT8                 ClusterAlignment;
  
    Size             = SIZE_OF_EFI_FILE_SYSTEM_INFO;
    Status           = FatGetVolumeEntry (Volume, Name);
    NameSize         = StrSize (Name);
    ResultSize       = Size + NameSize;
    ClusterAlignment = Volume->ClusterAlignment;
  
    //
    // If we don't have valid info, compute it now
    //
    FatComputeFreeInfo (Volume);
  
    Status = EFI_BUFFER_TOO_SMALL;
    if (*BufferSize >= ResultSize) {
      Status = EFI_SUCCESS;
  
      Info = Buffer;
      ZeroMem (Info, SIZE_OF_EFI_FILE_SYSTEM_INFO);
  
      Info->Size       = ResultSize;
      Info->ReadOnly   = Volume->ReadOnly;
      Info->BlockSize  = (UINT32)Volume->ClusterSize;
      Info->VolumeSize = LShiftU64 (Volume->MaxCluster, ClusterAlignment);
      Info->FreeSpace  = LShiftU64 (
                           Volume->FatInfoSector.FreeInfo.ClusterCount,
                           ClusterAlignment
                           );
      CopyMem ((CHAR8 *)Buffer + Size, Name, NameSize);
    }
  
    *BufferSize = ResultSize;
    return Status;
  }
  
  /**
  
    Get the volume's label info into Buffer.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing volume's label info.
  
    @retval EFI_SUCCESS           - Get the volume's label info successfully.
    @retval EFI_BUFFER_TOO_SMALL  - The buffer is too small.
  
  **/
  EFI_STATUS
  FatGetVolumeLabelInfo (
    IN FAT_VOLUME  *Volume,
    IN OUT UINTN   *BufferSize,
    OUT VOID       *Buffer
    )
  {
    UINTN       Size;
    UINTN       NameSize;
    UINTN       ResultSize;
    CHAR16      Name[FAT_NAME_LEN + 1];
    EFI_STATUS  Status;
  
    Size       = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL;
    Status     = FatGetVolumeEntry (Volume, Name);
    NameSize   = StrSize (Name);
    ResultSize = Size + NameSize;
  
    Status = EFI_BUFFER_TOO_SMALL;
    if (*BufferSize >= ResultSize) {
      Status = EFI_SUCCESS;
      CopyMem ((CHAR8 *)Buffer + Size, Name, NameSize);
    }
  
    *BufferSize = ResultSize;
    return Status;
  }
  
  /**
  
    Set the volume's info.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing the new volume info.
  
    @retval EFI_SUCCESS           - Set the volume info successfully.
    @retval EFI_BAD_BUFFER_SIZE   - The buffer size is error.
    @retval EFI_WRITE_PROTECTED   - The volume is read only.
    @return other                 - An error occurred when operation the disk.
  
  **/
  EFI_STATUS
  FatSetVolumeInfo (
    IN FAT_VOLUME  *Volume,
    IN UINTN       BufferSize,
    IN VOID        *Buffer
    )
  {
    EFI_FILE_SYSTEM_INFO  *Info;
  
    Info = (EFI_FILE_SYSTEM_INFO *)Buffer;
  
    if ((BufferSize < SIZE_OF_EFI_FILE_SYSTEM_INFO + 2) || (Info->Size > BufferSize)) {
      return EFI_BAD_BUFFER_SIZE;
    }
  
    return FatSetVolumeEntry (Volume, Info->VolumeLabel);
  }
  
  /**
  
    Set the volume's label info.
  
    @param  Volume                - FAT file system volume.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing the new volume label info.
  
    @retval EFI_SUCCESS           - Set the volume label info successfully.
    @retval EFI_WRITE_PROTECTED   - The disk is write protected.
    @retval EFI_BAD_BUFFER_SIZE   - The buffer size is error.
    @return other                 - An error occurred when operation the disk.
  
  **/
  EFI_STATUS
  FatSetVolumeLabelInfo (
    IN FAT_VOLUME  *Volume,
    IN UINTN       BufferSize,
    IN VOID        *Buffer
    )
  {
    EFI_FILE_SYSTEM_VOLUME_LABEL  *Info;
  
    Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
  
    if (BufferSize < SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL + 2) {
      return EFI_BAD_BUFFER_SIZE;
    }
  
    return FatSetVolumeEntry (Volume, Info->VolumeLabel);
  }
  
  /**
  
    Set the file info.
  
    @param  Volume                - FAT file system volume.
    @param  IFile                 - The instance of the open file.
    @param  OFile                 - The open file.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing the new file info.
  
    @retval EFI_SUCCESS           - Set the file info successfully.
    @retval EFI_ACCESS_DENIED     - It is the root directory
                            or the directory attribute bit can not change
                            or try to change a directory size
                            or something else.
    @retval EFI_UNSUPPORTED       - The new file size is larger than 4GB.
    @retval EFI_WRITE_PROTECTED   - The disk is write protected.
    @retval EFI_BAD_BUFFER_SIZE   - The buffer size is error.
    @retval EFI_INVALID_PARAMETER - The time info or attributes info is error.
    @retval EFI_OUT_OF_RESOURCES  - Can not allocate new memory.
    @retval EFI_VOLUME_CORRUPTED  - The volume is corrupted.
    @return other                 - An error occurred when operation the disk.
  
  **/
  EFI_STATUS
  FatSetFileInfo (
    IN FAT_VOLUME  *Volume,
    IN FAT_IFILE   *IFile,
    IN FAT_OFILE   *OFile,
    IN UINTN       BufferSize,
    IN VOID        *Buffer
    )
  {
    EFI_STATUS     Status;
    EFI_FILE_INFO  *NewInfo;
    FAT_OFILE      *DotOFile;
    FAT_OFILE      *Parent;
    CHAR16         NewFileName[EFI_PATH_STRING_LENGTH];
    EFI_TIME       ZeroTime;
    FAT_DIRENT     *DirEnt;
    FAT_DIRENT     *TempDirEnt;
    UINT8          NewAttribute;
    BOOLEAN        ReadOnly;
  
    ZeroMem (&ZeroTime, sizeof (EFI_TIME));
    Parent = OFile->Parent;
    DirEnt = OFile->DirEnt;
    //
    // If this is the root directory, we can't make any updates
    //
    if (Parent == NULL) {
      return EFI_ACCESS_DENIED;
    }
  
    //
    // Make sure there's a valid input buffer
    //
    NewInfo = Buffer;
    if ((BufferSize < SIZE_OF_EFI_FILE_INFO + 2) || (NewInfo->Size > BufferSize)) {
      return EFI_BAD_BUFFER_SIZE;
    }
  
    ReadOnly = (BOOLEAN)(IFile->ReadOnly || (DirEnt->Entry.Attributes & EFI_FILE_READ_ONLY));
    //
    // if a zero time is specified, then the original time is preserved
    //
    if (CompareMem (&ZeroTime, &NewInfo->CreateTime, sizeof (EFI_TIME)) != 0) {
      if (!FatIsValidTime (&NewInfo->CreateTime)) {
        return EFI_INVALID_PARAMETER;
      }
  
      if (!ReadOnly) {
        FatEfiTimeToFatTime (&NewInfo->CreateTime, &DirEnt->Entry.FileCreateTime);
      }
    }
  
    if (CompareMem (&ZeroTime, &NewInfo->ModificationTime, sizeof (EFI_TIME)) != 0) {
      if (!FatIsValidTime (&NewInfo->ModificationTime)) {
        return EFI_INVALID_PARAMETER;
      }
  
      if (!ReadOnly) {
        FatEfiTimeToFatTime (&NewInfo->ModificationTime, &DirEnt->Entry.FileModificationTime);
      }
  
      OFile->PreserveLastModification = TRUE;
    }
  
    if (NewInfo->Attribute & (~EFI_FILE_VALID_ATTR)) {
      return EFI_INVALID_PARAMETER;
    }
  
    NewAttribute = (UINT8)NewInfo->Attribute;
    //
    // Can not change the directory attribute bit
    //
    if ((NewAttribute ^ DirEnt->Entry.Attributes) & EFI_FILE_DIRECTORY) {
      return EFI_ACCESS_DENIED;
    }
  
    //
    // Set the current attributes even if the IFile->ReadOnly is TRUE
    //
    DirEnt->Entry.Attributes = (UINT8)((DirEnt->Entry.Attributes &~EFI_FILE_VALID_ATTR) | NewAttribute);
    //
    // Open the filename and see if it refers to an existing file
    //
    Status = FatLocateOFile (&Parent, NewInfo->FileName, DirEnt->Entry.Attributes, NewFileName);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    if (*NewFileName != 0) {
      //
      // File was not found.  We do not allow rename of the current directory if
      // there are open files below the current directory
      //
      if (!IsListEmpty (&OFile->ChildHead) || (Parent == OFile)) {
        return EFI_ACCESS_DENIED;
      }
  
      if (ReadOnly) {
        return EFI_ACCESS_DENIED;
      }
  
      Status = FatRemoveDirEnt (OFile->Parent, DirEnt);
      if (EFI_ERROR (Status)) {
        return Status;
      }
  
      //
      // Create new dirent
      //
      Status = FatCreateDirEnt (Parent, NewFileName, DirEnt->Entry.Attributes, &TempDirEnt);
      if (EFI_ERROR (Status)) {
        return Status;
      }
  
      FatCloneDirEnt (TempDirEnt, DirEnt);
      FatFreeDirEnt (DirEnt);
      DirEnt        = TempDirEnt;
      DirEnt->OFile = OFile;
      OFile->DirEnt = DirEnt;
      OFile->Parent = Parent;
      RemoveEntryList (&OFile->ChildLink);
      InsertHeadList (&Parent->ChildHead, &OFile->ChildLink);
      //
      // If this is a directory, synchronize its dot directory entry
      //
      if (OFile->ODir != NULL) {
        //
        // Synchronize its dot entry
        //
        FatResetODirCursor (OFile);
        ASSERT (OFile->Parent != NULL);
        for (DotOFile = OFile; DotOFile != OFile->Parent->Parent; DotOFile = DotOFile->Parent) {
          Status = FatGetNextDirEnt (OFile, &DirEnt);
          if (EFI_ERROR (Status) || (DirEnt == NULL) || !FatIsDotDirEnt (DirEnt)) {
            return EFI_VOLUME_CORRUPTED;
          }
  
          FatCloneDirEnt (DirEnt, DotOFile->DirEnt);
          Status = FatStoreDirEnt (OFile, DirEnt);
          if (EFI_ERROR (Status)) {
            return Status;
          }
        }
      }
  
      //
      // If the file is renamed, we should append the ARCHIVE attribute
      //
      OFile->Archive = TRUE;
    } else if (Parent != OFile) {
      //
      // filename is to a different filename that already exists
      //
      return EFI_ACCESS_DENIED;
    }
  
    //
    // If the file size has changed, apply it
    //
    if (NewInfo->FileSize != OFile->FileSize) {
      if ((OFile->ODir != NULL) || ReadOnly) {
        //
        // If this is a directory or the file is read only, we can't change the file size
        //
        return EFI_ACCESS_DENIED;
      }
  
      if (NewInfo->FileSize > OFile->FileSize) {
        Status = FatExpandOFile (OFile, NewInfo->FileSize);
      } else {
        Status = FatTruncateOFile (OFile, (UINTN)NewInfo->FileSize);
      }
  
      if (EFI_ERROR (Status)) {
        return Status;
      }
  
      FatUpdateDirEntClusterSizeInfo (OFile);
    }
  
    OFile->Dirty = TRUE;
    return FatOFileFlush (OFile);
  }
  
  /**
  
    Set or Get the some types info of the file into Buffer.
  
    @param  IsSet      - TRUE:The access is set, else is get
    @param  FHand      - The handle of file
    @param  Type       - The type of the info
    @param  BufferSize - Size of Buffer
    @param  Buffer     - Buffer containing volume info
  
    @retval EFI_SUCCESS       - Get the info successfully
    @retval EFI_DEVICE_ERROR  - Can not find the OFile for the file
  
  **/
  EFI_STATUS
  FatSetOrGetInfo (
    IN     BOOLEAN            IsSet,
    IN     EFI_FILE_PROTOCOL  *FHand,
    IN     EFI_GUID           *Type,
    IN OUT UINTN              *BufferSize,
    IN OUT VOID               *Buffer
    )
  {
    FAT_IFILE   *IFile;
    FAT_OFILE   *OFile;
    FAT_VOLUME  *Volume;
    EFI_STATUS  Status;
  
    IFile  = IFILE_FROM_FHAND (FHand);
    OFile  = IFile->OFile;
    Volume = OFile->Volume;
  
    Status = OFile->Error;
    if (Status == EFI_NOT_FOUND) {
      return EFI_DEVICE_ERROR;
    }
  
    FatWaitNonblockingTask (IFile);
  
    FatAcquireLock ();
  
    //
    // Verify the file handle isn't in an error state
    //
    if (!EFI_ERROR (Status)) {
      //
      // Get the proper information based on the request
      //
      Status = EFI_UNSUPPORTED;
      if (IsSet) {
        if (CompareGuid (Type, &gEfiFileInfoGuid)) {
          Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetFileInfo (Volume, IFile, OFile, *BufferSize, Buffer);
        }
  
        if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) {
          Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeInfo (Volume, *BufferSize, Buffer);
        }
  
        if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
          Status = Volume->ReadOnly ? EFI_WRITE_PROTECTED : FatSetVolumeLabelInfo (Volume, *BufferSize, Buffer);
        }
      } else {
        if (CompareGuid (Type, &gEfiFileInfoGuid)) {
          Status = FatGetFileInfo (OFile, BufferSize, Buffer);
        }
  
        if (CompareGuid (Type, &gEfiFileSystemInfoGuid)) {
          Status = FatGetVolumeInfo (Volume, BufferSize, Buffer);
        }
  
        if (CompareGuid (Type, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
          Status = FatGetVolumeLabelInfo (Volume, BufferSize, Buffer);
        }
      }
    }
  
    Status = FatCleanupVolume (Volume, NULL, Status, NULL);
  
    FatReleaseLock ();
    return Status;
  }
  
  /**
  
    Get the some types info of the file into Buffer.
  
    @param  FHand                 - The handle of file.
    @param  Type                  - The type of the info.
    @param  BufferSize            - Size of Buffer.
    @param  Buffer                - Buffer containing volume info.
  
    @retval EFI_SUCCESS           - Get the info successfully.
    @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
  
  **/
  EFI_STATUS
  EFIAPI
  FatGetInfo (
    IN     EFI_FILE_PROTOCOL  *FHand,
    IN     EFI_GUID           *Type,
    IN OUT UINTN              *BufferSize,
    OUT VOID                  *Buffer
    )
  {
    return FatSetOrGetInfo (FALSE, FHand, Type, BufferSize, Buffer);
  }
  
  /**
  
    Set the some types info of the file into Buffer.
  
    @param  FHand                 - The handle of file.
    @param  Type                  - The type of the info.
    @param  BufferSize            - Size of Buffer
    @param  Buffer                - Buffer containing volume info.
  
    @retval EFI_SUCCESS           - Set the info successfully.
    @retval EFI_DEVICE_ERROR      - Can not find the OFile for the file.
  
  **/
  EFI_STATUS
  EFIAPI
  FatSetInfo (
    IN EFI_FILE_PROTOCOL  *FHand,
    IN EFI_GUID           *Type,
    IN UINTN              BufferSize,
    IN VOID               *Buffer
    )
  {
    return FatSetOrGetInfo (TRUE, FHand, Type, &BufferSize, Buffer);
  }
  