diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c new file mode 100644 index 00000000..78b26a58 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/FatPkg/EnhancedFatDxe/FileSpace.c @@ -0,0 +1,744 @@ +/** @file + Routines dealing with disk spaces and FAT table entries. + +Copyright (c) 2005 - 2013, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + + + +**/ + +#include "Fat.h" + + +/** + + Get the FAT entry of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + + @return The buffer of the FAT entry + +**/ +STATIC +VOID * +FatLoadFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index + ) +{ + UINTN Pos; + EFI_STATUS Status; + + if (Index > (Volume->MaxCluster + 1)) { + Volume->FatEntryBuffer = (UINT32) -1; + return &Volume->FatEntryBuffer; + } + // + // Compute buffer position needed + // + switch (Volume->FatType) { + case Fat12: + Pos = FAT_POS_FAT12 (Index); + break; + + case Fat16: + Pos = FAT_POS_FAT16 (Index); + break; + + default: + Pos = FAT_POS_FAT32 (Index); + } + // + // Set the position and read the buffer + // + Volume->FatEntryPos = Volume->FatPos + Pos; + Status = FatDiskIo ( + Volume, + ReadFat, + Volume->FatEntryPos, + Volume->FatEntrySize, + &Volume->FatEntryBuffer, + NULL + ); + if (EFI_ERROR (Status)) { + Volume->FatEntryBuffer = (UINT32) -1; + } + + return &Volume->FatEntryBuffer; +} + +/** + + Get the FAT entry value of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + + @return The value of the FAT entry. + +**/ +STATIC +UINTN +FatGetFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index + ) +{ + VOID *Pos; + UINT8 *En12; + UINT16 *En16; + UINT32 *En32; + UINTN Accum; + + Pos = FatLoadFatEntry (Volume, Index); + + if (Index > (Volume->MaxCluster + 1)) { + return (UINTN) -1; + } + + switch (Volume->FatType) { + case Fat12: + En12 = Pos; + Accum = En12[0] | (En12[1] << 8); + Accum = FAT_ODD_CLUSTER_FAT12 (Index) ? (Accum >> 4) : (Accum & FAT_CLUSTER_MASK_FAT12); + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT12) ? FAT_CLUSTER_SPECIAL_EXT : 0); + break; + + case Fat16: + En16 = Pos; + Accum = *En16; + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT16) ? FAT_CLUSTER_SPECIAL_EXT : 0); + break; + + default: + En32 = Pos; + Accum = *En32 & FAT_CLUSTER_MASK_FAT32; + Accum = Accum | ((Accum >= FAT_CLUSTER_SPECIAL_FAT32) ? FAT_CLUSTER_SPECIAL_EXT : 0); + } + + return Accum; +} + +/** + + Set the FAT entry value of the volume, which is identified with the Index. + + @param Volume - FAT file system volume. + @param Index - The index of the FAT entry of the volume. + @param Value - The new value of the FAT entry. + + @retval EFI_SUCCESS - Set the new FAT entry value successfully. + @retval EFI_VOLUME_CORRUPTED - The FAT type of the volume is error. + @return other - An error occurred when operation the FAT entries. + +**/ +STATIC +EFI_STATUS +FatSetFatEntry ( + IN FAT_VOLUME *Volume, + IN UINTN Index, + IN UINTN Value + ) +{ + VOID *Pos; + UINT8 *En12; + UINT16 *En16; + UINT32 *En32; + UINTN Accum; + EFI_STATUS Status; + UINTN OriginalVal; + + if (Index < FAT_MIN_CLUSTER) { + return EFI_VOLUME_CORRUPTED; + } + + OriginalVal = FatGetFatEntry (Volume, Index); + if (Value == FAT_CLUSTER_FREE && OriginalVal != FAT_CLUSTER_FREE) { + Volume->FatInfoSector.FreeInfo.ClusterCount += 1; + if (Index < Volume->FatInfoSector.FreeInfo.NextCluster) { + Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index; + } + } else if (Value != FAT_CLUSTER_FREE && OriginalVal == FAT_CLUSTER_FREE) { + if (Volume->FatInfoSector.FreeInfo.ClusterCount != 0) { + Volume->FatInfoSector.FreeInfo.ClusterCount -= 1; + } + } + // + // Make sure the entry is in memory + // + Pos = FatLoadFatEntry (Volume, Index); + + // + // Update the value + // + switch (Volume->FatType) { + case Fat12: + En12 = Pos; + Accum = En12[0] | (En12[1] << 8); + Value = Value & FAT_CLUSTER_MASK_FAT12; + + if (FAT_ODD_CLUSTER_FAT12 (Index)) { + Accum = (Value << 4) | (Accum & 0xF); + } else { + Accum = Value | (Accum & FAT_CLUSTER_UNMASK_FAT12); + } + + En12[0] = (UINT8) (Accum & 0xFF); + En12[1] = (UINT8) (Accum >> 8); + break; + + case Fat16: + En16 = Pos; + *En16 = (UINT16) Value; + break; + + default: + En32 = Pos; + *En32 = (*En32 & FAT_CLUSTER_UNMASK_FAT32) | (UINT32) (Value & FAT_CLUSTER_MASK_FAT32); + } + // + // If the volume's dirty bit is not set, set it now + // + if (!Volume->FatDirty && Volume->FatType != Fat12) { + Volume->FatDirty = TRUE; + FatAccessVolumeDirty (Volume, WriteFat, &Volume->DirtyValue); + } + // + // Write the updated fat entry value to the volume + // The fat is the first fat, and other fat will be in sync + // when the FAT cache flush back. + // + Status = FatDiskIo ( + Volume, + WriteFat, + Volume->FatEntryPos, + Volume->FatEntrySize, + &Volume->FatEntryBuffer, + NULL + ); + return Status; +} + +/** + + Free the cluster chain. + + @param Volume - FAT file system volume. + @param Cluster - The first cluster of cluster chain. + + @retval EFI_SUCCESS - The cluster chain is freed successfully. + @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters. + +**/ +STATIC +EFI_STATUS +FatFreeClusters ( + IN FAT_VOLUME *Volume, + IN UINTN Cluster + ) +{ + UINTN LastCluster; + + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + LastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + FatSetFatEntry (Volume, LastCluster, FAT_CLUSTER_FREE); + } + + return EFI_SUCCESS; +} + +/** + + Allocate a free cluster and return the cluster index. + + @param Volume - FAT file system volume. + + @return The index of the free cluster + +**/ +STATIC +UINTN +FatAllocateCluster ( + IN FAT_VOLUME *Volume + ) +{ + UINTN Cluster; + + // + // Start looking at FatFreePos for the next unallocated cluster + // + if (Volume->DiskError) { + return (UINTN) FAT_CLUSTER_LAST; + } + + for (;;) { + // + // If the end of the list, return no available cluster + // + if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) { + if (Volume->FreeInfoValid && 0 < (INT32) (Volume->FatInfoSector.FreeInfo.ClusterCount)) { + Volume->FreeInfoValid = FALSE; + } + + FatComputeFreeInfo (Volume); + if (Volume->FatInfoSector.FreeInfo.NextCluster > (Volume->MaxCluster + 1)) { + return (UINTN) FAT_CLUSTER_LAST; + } + } + + Cluster = FatGetFatEntry (Volume, Volume->FatInfoSector.FreeInfo.NextCluster); + if (Cluster == FAT_CLUSTER_FREE) { + break; + } + // + // Try the next cluster + // + Volume->FatInfoSector.FreeInfo.NextCluster += 1; + } + + Cluster = Volume->FatInfoSector.FreeInfo.NextCluster; + Volume->FatInfoSector.FreeInfo.NextCluster += 1; + return Cluster; +} + +/** + + Count the number of clusters given a size. + + @param Volume - The file system volume. + @param Size - The size in bytes. + + @return The number of the clusters. + +**/ +STATIC +UINTN +FatSizeToClusters ( + IN FAT_VOLUME *Volume, + IN UINTN Size + ) +{ + UINTN Clusters; + + Clusters = Size >> Volume->ClusterAlignment; + if ((Size & (Volume->ClusterSize - 1)) > 0) { + Clusters += 1; + } + + return Clusters; +} + +/** + + Shrink the end of the open file base on the file size. + + @param OFile - The open file. + + @retval EFI_SUCCESS - Shrinked successfully. + @retval EFI_VOLUME_CORRUPTED - There are errors in the file's clusters. + +**/ +EFI_STATUS +FatShrinkEof ( + IN FAT_OFILE *OFile + ) +{ + FAT_VOLUME *Volume; + UINTN NewSize; + UINTN CurSize; + UINTN Cluster; + UINTN LastCluster; + + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + + NewSize = FatSizeToClusters (Volume, OFile->FileSize); + + // + // Find the address of the last cluster + // + Cluster = OFile->FileCluster; + LastCluster = FAT_CLUSTER_FREE; + + if (NewSize != 0) { + + for (CurSize = 0; CurSize < NewSize; CurSize++) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatShrinkEof: cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + LastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + } + + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + + } else { + // + // Check to see if the file is already completely truncated + // + if (Cluster == FAT_CLUSTER_FREE) { + return EFI_SUCCESS; + } + // + // The file is being completely truncated. + // + OFile->FileCluster = FAT_CLUSTER_FREE; + } + // + // Set CurrentCluster == FileCluster + // to force a recalculation of Position related stuffs + // + OFile->FileCurrentCluster = OFile->FileCluster; + OFile->FileLastCluster = LastCluster; + OFile->Dirty = TRUE; + // + // Free the remaining cluster chain + // + return FatFreeClusters (Volume, Cluster); +} + +/** + + Grow the end of the open file base on the NewSizeInBytes. + + @param OFile - The open file. + @param NewSizeInBytes - The new size in bytes of the open file. + + @retval EFI_SUCCESS - The file is grown successfully. + @retval EFI_UNSUPPORTED - The file size is larger than 4GB. + @retval EFI_VOLUME_CORRUPTED - There are errors in the files' clusters. + @retval EFI_VOLUME_FULL - The volume is full and can not grow the file. + +**/ +EFI_STATUS +FatGrowEof ( + IN FAT_OFILE *OFile, + IN UINT64 NewSizeInBytes + ) +{ + FAT_VOLUME *Volume; + EFI_STATUS Status; + UINTN Cluster; + UINTN CurSize; + UINTN NewSize; + UINTN LastCluster; + UINTN NewCluster; + UINTN ClusterCount; + + // + // For FAT file system, the max file is 4GB. + // + if (NewSizeInBytes > 0x0FFFFFFFFL) { + return EFI_UNSUPPORTED; + } + + Volume = OFile->Volume; + ASSERT_VOLUME_LOCKED (Volume); + // + // If the file is already large enough, do nothing + // + CurSize = FatSizeToClusters (Volume, OFile->FileSize); + NewSize = FatSizeToClusters (Volume, (UINTN) NewSizeInBytes); + + if (CurSize < NewSize) { + // + // If we haven't found the files last cluster do it now + // + if ((OFile->FileCluster != 0) && (OFile->FileLastCluster == 0)) { + Cluster = OFile->FileCluster; + ClusterCount = 0; + + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) { + + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FatGrowEof: cluster chain corrupt\n") + ); + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + ClusterCount++; + OFile->FileLastCluster = Cluster; + Cluster = FatGetFatEntry (Volume, Cluster); + } + + if (ClusterCount != CurSize) { + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FatGrowEof: cluster chain size does not match file size\n") + ); + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + } + // + // Loop until we've allocated enough space + // + LastCluster = OFile->FileLastCluster; + + while (CurSize < NewSize) { + NewCluster = FatAllocateCluster (Volume); + if (FAT_END_OF_FAT_CHAIN (NewCluster)) { + if (LastCluster != FAT_CLUSTER_FREE) { + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + OFile->FileLastCluster = LastCluster; + } + + Status = EFI_VOLUME_FULL; + goto Done; + } + + if (NewCluster < FAT_MIN_CLUSTER || NewCluster > Volume->MaxCluster + 1) { + Status = EFI_VOLUME_CORRUPTED; + goto Done; + } + + if (LastCluster != 0) { + FatSetFatEntry (Volume, LastCluster, NewCluster); + } else { + OFile->FileCluster = NewCluster; + OFile->FileCurrentCluster = NewCluster; + } + + LastCluster = NewCluster; + CurSize += 1; + + // + // Terminate the cluster list + // + // Note that we must do this EVERY time we allocate a cluster, because + // FatAllocateCluster scans the FAT looking for a free cluster and + // "LastCluster" is no longer free! Usually, FatAllocateCluster will + // start looking with the cluster after "LastCluster"; however, when + // there is only one free cluster left, it will find "LastCluster" + // a second time. There are other, less predictable scenarios + // where this could happen, as well. + // + FatSetFatEntry (Volume, LastCluster, (UINTN) FAT_CLUSTER_LAST); + OFile->FileLastCluster = LastCluster; + } + } + + OFile->FileSize = (UINTN) NewSizeInBytes; + OFile->Dirty = TRUE; + return EFI_SUCCESS; + +Done: + FatShrinkEof (OFile); + return Status; +} + +/** + + Seek OFile to requested position, and calculate the number of + consecutive clusters from the position in the file + + @param OFile - The open file. + @param Position - The file's position which will be accessed. + @param PosLimit - The maximum length current reading/writing may access + + @retval EFI_SUCCESS - Set the info successfully. + @retval EFI_VOLUME_CORRUPTED - Cluster chain corrupt. + +**/ +EFI_STATUS +FatOFilePosition ( + IN FAT_OFILE *OFile, + IN UINTN Position, + IN UINTN PosLimit + ) +{ + FAT_VOLUME *Volume; + UINTN ClusterSize; + UINTN Cluster; + UINTN StartPos; + UINTN Run; + + Volume = OFile->Volume; + ClusterSize = Volume->ClusterSize; + + ASSERT_VOLUME_LOCKED (Volume); + + // + // If this is the fixed root dir, then compute its position + // from its fixed info in the fat bpb + // + if (OFile->IsFixedRootDir) { + OFile->PosDisk = Volume->RootPos + Position; + Run = OFile->FileSize - Position; + } else { + // + // Run the file's cluster chain to find the current position + // If possible, run from the current cluster rather than + // start from beginning + // Assumption: OFile->Position is always consistent with + // OFile->FileCurrentCluster. + // OFile->Position is not modified outside this function; + // OFile->FileCurrentCluster is modified outside this function + // to be the same as OFile->FileCluster + // when OFile->FileCluster is updated, so make a check of this + // and invalidate the original OFile->Position in this case + // + Cluster = OFile->FileCurrentCluster; + StartPos = OFile->Position; + if (Position < StartPos || OFile->FileCluster == Cluster) { + StartPos = 0; + Cluster = OFile->FileCluster; + } + + while (StartPos + ClusterSize <= Position) { + StartPos += ClusterSize; + if (Cluster == FAT_CLUSTER_FREE || (Cluster >= FAT_CLUSTER_SPECIAL)) { + DEBUG ((EFI_D_INIT | EFI_D_ERROR, "FatOFilePosition:"" cluster chain corrupt\n")); + return EFI_VOLUME_CORRUPTED; + } + + Cluster = FatGetFatEntry (Volume, Cluster); + } + + if (Cluster < FAT_MIN_CLUSTER || Cluster > Volume->MaxCluster + 1) { + return EFI_VOLUME_CORRUPTED; + } + + OFile->PosDisk = Volume->FirstClusterPos + + LShiftU64 (Cluster - FAT_MIN_CLUSTER, Volume->ClusterAlignment) + + Position - StartPos; + OFile->FileCurrentCluster = Cluster; + OFile->Position = StartPos; + + // + // Compute the number of consecutive clusters in the file + // + Run = StartPos + ClusterSize - Position; + if (!FAT_END_OF_FAT_CHAIN (Cluster)) { + while ((FatGetFatEntry (Volume, Cluster) == Cluster + 1) && Run < PosLimit) { + Run += ClusterSize; + Cluster += 1; + } + } + } + + OFile->PosRem = Run; + return EFI_SUCCESS; +} + +/** + + Get the size of directory of the open file. + + @param Volume - The File System Volume. + @param Cluster - The Starting cluster. + + @return The physical size of the file starting at the input cluster, if there is error in the + cluster chain, the return value is 0. + +**/ +UINTN +FatPhysicalDirSize ( + IN FAT_VOLUME *Volume, + IN UINTN Cluster + ) +{ + UINTN Size; + ASSERT_VOLUME_LOCKED (Volume); + // + // Run the cluster chain for the OFile + // + Size = 0; + // + // N.B. ".." directories on some media do not contain a starting + // cluster. In the case of "." or ".." we don't need the size anyway. + // + if (Cluster != 0) { + while (!FAT_END_OF_FAT_CHAIN (Cluster)) { + if (Cluster == FAT_CLUSTER_FREE || Cluster >= FAT_CLUSTER_SPECIAL) { + DEBUG ( + (EFI_D_INIT | EFI_D_ERROR, + "FATDirSize: cluster chain corrupt\n") + ); + return 0; + } + + Size += Volume->ClusterSize; + Cluster = FatGetFatEntry (Volume, Cluster); + } + } + + return Size; +} + +/** + + Get the physical size of a file on the disk. + + @param Volume - The file system volume. + @param RealSize - The real size of a file. + + @return The physical size of a file on the disk. + +**/ +UINT64 +FatPhysicalFileSize ( + IN FAT_VOLUME *Volume, + IN UINTN RealSize + ) +{ + UINTN ClusterSizeMask; + UINT64 PhysicalSize; + ClusterSizeMask = Volume->ClusterSize - 1; + PhysicalSize = (RealSize + ClusterSizeMask) & (~((UINT64) ClusterSizeMask)); + return PhysicalSize; +} + +/** + + Update the free cluster info of FatInfoSector of the volume. + + @param Volume - FAT file system volume. + +**/ +VOID +FatComputeFreeInfo ( + IN FAT_VOLUME *Volume + ) +{ + UINTN Index; + + // + // If we don't have valid info, compute it now + // + if (!Volume->FreeInfoValid) { + + Volume->FreeInfoValid = TRUE; + Volume->FatInfoSector.FreeInfo.ClusterCount = 0; + for (Index = Volume->MaxCluster + 1; Index >= FAT_MIN_CLUSTER; Index--) { + if (Volume->DiskError) { + break; + } + + if (FatGetFatEntry (Volume, Index) == FAT_CLUSTER_FREE) { + Volume->FatInfoSector.FreeInfo.ClusterCount += 1; + Volume->FatInfoSector.FreeInfo.NextCluster = (UINT32) Index; + } + } + + Volume->FatInfoSector.Signature = FAT_INFO_SIGNATURE; + Volume->FatInfoSector.InfoBeginSignature = FAT_INFO_BEGIN_SIGNATURE; + Volume->FatInfoSector.InfoEndSignature = FAT_INFO_END_SIGNATURE; + } +} |