diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c')
-rw-r--r-- | roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c | 2924 |
1 files changed, 2924 insertions, 0 deletions
diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c new file mode 100644 index 000000000..e9e55cb2b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c @@ -0,0 +1,2924 @@ +/** @file
+ Handle on-disk format and volume structures in UDF/ECMA-167 file systems.
+
+ Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
+ Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Udf.h"
+
+//
+// Vendor-Defined Device Path GUID for UDF file system
+//
+EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID;
+
+/**
+ Find the anchor volume descriptor pointer.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[out] AnchorPoint Anchor volume descriptor pointer.
+
+ @retval EFI_SUCCESS Anchor volume descriptor pointer found.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval other Anchor volume descriptor pointer not found.
+
+**/
+EFI_STATUS
+FindAnchorVolumeDescriptorPointer (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ OUT UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ EFI_LBA EndLBA;
+ EFI_LBA DescriptorLBAs[4];
+ UINTN Index;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+
+ BlockSize = BlockIo->Media->BlockSize;
+ EndLBA = BlockIo->Media->LastBlock;
+ DescriptorLBAs[0] = 256;
+ DescriptorLBAs[1] = EndLBA - 256;
+ DescriptorLBAs[2] = EndLBA;
+ DescriptorLBAs[3] = 512;
+
+ for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) {
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (DescriptorLBAs[Index], BlockSize),
+ sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER),
+ (VOID *)AnchorPoint
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DescriptorTag = &AnchorPoint->DescriptorTag;
+
+ //
+ // Check if read LBA has a valid AVDP descriptor.
+ //
+ if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
+ return EFI_SUCCESS;
+ }
+ }
+ //
+ // No AVDP found.
+ //
+ return EFI_VOLUME_CORRUPTED;
+}
+
+/**
+ Save the content of Logical Volume Descriptors and Partitions Descriptors in
+ memory.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] AnchorPoint Anchor volume descriptor pointer.
+ @param[out] Volume UDF volume information structure.
+
+ @retval EFI_SUCCESS The descriptors were saved.
+ @retval EFI_OUT_OF_RESOURCES The descriptors were not saved due to lack of
+ resources.
+ @retval other The descriptors were not saved due to
+ ReadDisk error.
+
+**/
+EFI_STATUS
+StartMainVolumeDescriptorSequence (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint,
+ OUT UDF_VOLUME_INFO *Volume
+ )
+{
+ EFI_STATUS Status;
+ UINT32 BlockSize;
+ UDF_EXTENT_AD *ExtentAd;
+ EFI_LBA SeqStartBlock;
+ EFI_LBA SeqEndBlock;
+ BOOLEAN StopSequence;
+ VOID *Buffer;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+ UINT32 LogicalBlockSize;
+
+ BlockSize = BlockIo->Media->BlockSize;
+ ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent;
+
+ //
+ // Allocate buffer for reading disk blocks
+ //
+ Buffer = AllocateZeroPool ((UINTN)BlockSize);
+ if (Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // The logical partition created by Partition driver is relative to the main
+ // VDS extent location, so we start the Main Volume Descriptor Sequence at
+ // LBA 0.
+ //
+ // We don't need to check again if we have valid Volume Descriptors here since
+ // Partition driver already did.
+ //
+ SeqStartBlock = 0;
+ SeqEndBlock = SeqStartBlock + DivU64x32 ((UINT64)ExtentAd->ExtentLength,
+ BlockSize);
+ StopSequence = FALSE;
+ for (; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) {
+ //
+ // Read disk block
+ //
+ Status = BlockIo->ReadBlocks (
+ BlockIo,
+ BlockIo->Media->MediaId,
+ SeqStartBlock,
+ BlockSize,
+ Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto Out_Free;
+ }
+
+ DescriptorTag = Buffer;
+
+ switch (DescriptorTag->TagIdentifier) {
+ case UdfPartitionDescriptor:
+ //
+ // Save Partition Descriptor
+ //
+ CopyMem (&Volume->PartitionDesc, Buffer, sizeof (Volume->PartitionDesc));
+ break;
+
+ case UdfLogicalVolumeDescriptor:
+ //
+ // Save Logical Volume Descriptor
+ //
+ CopyMem (&Volume->LogicalVolDesc, Buffer, sizeof (Volume->LogicalVolDesc));
+ break;
+
+ case UdfTerminatingDescriptor:
+ StopSequence = TRUE;
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ //
+ // Determine FE (File Entry) size
+ //
+ LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize;
+ if (LogicalBlockSize >= UDF_LOGICAL_SECTOR_SIZE) {
+ Volume->FileEntrySize = (UINTN)LogicalBlockSize;
+ } else {
+ Volume->FileEntrySize = UDF_LOGICAL_SECTOR_SIZE;
+ }
+
+ Status = EFI_SUCCESS;
+
+Out_Free:
+ //
+ // Free block read buffer
+ //
+ FreePool (Buffer);
+
+ return Status;
+}
+
+/**
+ Return a Partition Descriptor given a Long Allocation Descriptor. This is
+ necessary to calculate the right extent (LongAd) offset which is added up
+ with partition's starting location.
+
+ @param[in] Volume Volume information pointer.
+ @param[in] LongAd Long Allocation Descriptor pointer.
+
+ @return A pointer to a Partition Descriptor.
+
+**/
+UDF_PARTITION_DESCRIPTOR *
+GetPdFromLongAd (
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd
+ )
+{
+ UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc;
+ UINT16 PartitionNum;
+
+ LogicalVolDesc = &Volume->LogicalVolDesc;
+
+ switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) {
+ case 0x0102:
+ case 0x0150:
+ case 0x0200:
+ case 0x0201:
+ case 0x0250:
+ case 0x0260:
+ //
+ // UDF 1.02 specification:
+ //
+ // There shall be exactly one prevailing Logical Volume Descriptor recorded
+ // per Volume Set. The Partition Maps field shall contain only Type 1
+ // Partition Maps.
+ //
+ // UDF 1.50 through 2.60 specs say:
+ //
+ // For the purpose of interchange partition maps shall be limited to
+ // Partition Map type 1, except type 2 maps as described in the document.
+ //
+ // NOTE: Only one Type 1 (Physical) Partition is supported. It has been
+ // checked already in Partition driver for existence of a single Type 1
+ // Partition map. Hence, the 'PartitionReferenceNumber' field (the index
+ // used to access Partition Maps data within the Logical Volume Descriptor)
+ // in the Long Allocation Descriptor should be 0 to indicate there is only
+ // one partition.
+ //
+ if (LongAd->ExtentLocation.PartitionReferenceNumber != 0) {
+ return NULL;
+ }
+ //
+ // Since only one partition, get the first one directly.
+ //
+ PartitionNum = *(UINT16 *)((UINTN)&LogicalVolDesc->PartitionMaps[4]);
+ break;
+
+ default:
+ //
+ // Unsupported UDF revision
+ //
+ return NULL;
+ }
+
+ //
+ // Check if partition number matches Partition Descriptor found in Main Volume
+ // Descriptor Sequence.
+ //
+ if (Volume->PartitionDesc.PartitionNumber == PartitionNum) {
+ return &Volume->PartitionDesc;
+ }
+
+ return NULL;
+}
+
+/**
+ Return logical sector number of a given Long Allocation Descriptor.
+
+ @param[in] Volume Volume information pointer.
+ @param[in] LongAd Long Allocation Descriptor pointer.
+ @param[out] Lsn Logical sector number pointer.
+
+ @retval EFI_SUCCESS Logical sector number successfully returned.
+ @retval EFI_UNSUPPORTED Logical sector number is not returned due to
+ unrecognized format.
+
+**/
+EFI_STATUS
+GetLongAdLsn (
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd,
+ OUT UINT64 *Lsn
+ )
+{
+ UDF_PARTITION_DESCRIPTOR *PartitionDesc;
+
+ PartitionDesc = GetPdFromLongAd (Volume, LongAd);
+ if (PartitionDesc == NULL) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Fail to get the Partition Descriptor from the given Long Allocation Descriptor.\n",
+ __FUNCTION__
+ ));
+ return EFI_UNSUPPORTED;
+ }
+
+ *Lsn = (UINT64)PartitionDesc->PartitionStartingLocation -
+ Volume->MainVdsStartLocation +
+ LongAd->ExtentLocation.LogicalBlockNumber;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return logical sector number of a given Short Allocation Descriptor.
+
+ @param[in] Volume Volume pointer.
+ @param[in] PartitionDesc Partition Descriptor pointer.
+ @param[in] ShortAd Short Allocation Descriptor pointer.
+
+ @return The logical sector number of a given Short Allocation Descriptor.
+
+**/
+UINT64
+GetShortAdLsn (
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_PARTITION_DESCRIPTOR *PartitionDesc,
+ IN UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd
+ )
+{
+ return (UINT64)PartitionDesc->PartitionStartingLocation -
+ Volume->MainVdsStartLocation + ShortAd->ExtentPosition;
+}
+
+/**
+ Find File Set Descriptor of a given Logical Volume Descriptor.
+
+ The found FSD will contain the extent (LogicalVolumeContentsUse) where our
+ root directory is.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume Volume information pointer.
+
+ @retval EFI_SUCCESS File Set Descriptor pointer found.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval other File Set Descriptor pointer not found.
+
+**/
+EFI_STATUS
+FindFileSetDescriptor (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Lsn;
+ UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+
+ LogicalVolDesc = &Volume->LogicalVolDesc;
+ Status = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse, &Lsn);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // As per UDF 2.60 specification:
+ //
+ // There shall be exactly one File Set Descriptor recorded per Logical
+ // Volume.
+ //
+ // Read disk block
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (Lsn, LogicalVolDesc->LogicalBlockSize),
+ sizeof (Volume->FileSetDesc),
+ &Volume->FileSetDesc
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DescriptorTag = &Volume->FileSetDesc.DescriptorTag;
+
+ //
+ // Check if read block is a File Set Descriptor
+ //
+ if (DescriptorTag->TagIdentifier != UdfFileSetDescriptor) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read Volume and File Structure on an UDF file system.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[out] Volume Volume information pointer.
+
+ @retval EFI_SUCCESS Volume and File Structure were read.
+ @retval other Volume and File Structure were not read.
+
+**/
+EFI_STATUS
+ReadVolumeFileStructure (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ OUT UDF_VOLUME_INFO *Volume
+ )
+{
+ EFI_STATUS Status;
+ UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint;
+ UDF_EXTENT_AD *ExtentAd;
+
+ //
+ // Find Anchor Volume Descriptor Pointer
+ //
+ Status = FindAnchorVolumeDescriptorPointer (
+ BlockIo,
+ DiskIo,
+ &AnchorPoint
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Save Main VDS start block number
+ //
+ ExtentAd = &AnchorPoint.MainVolumeDescriptorSequenceExtent;
+
+ Volume->MainVdsStartLocation = (UINT64)ExtentAd->ExtentLocation;
+
+ //
+ // Start Main Volume Descriptor Sequence.
+ //
+ Status = StartMainVolumeDescriptorSequence (
+ BlockIo,
+ DiskIo,
+ &AnchorPoint,
+ Volume
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+/**
+ Calculate length of a given File Identifier Descriptor.
+
+ @param[in] FileIdentifierDesc File Identifier Descriptor pointer.
+
+ @return The length of a given File Identifier Descriptor.
+
+**/
+UINT64
+GetFidDescriptorLength (
+ IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc
+ )
+{
+ return (UINT64)(
+ (INTN)((OFFSET_OF (UDF_FILE_IDENTIFIER_DESCRIPTOR, Data[0]) + 3 +
+ FileIdentifierDesc->LengthOfFileIdentifier +
+ FileIdentifierDesc->LengthOfImplementationUse) >> 2) << 2
+ );
+}
+
+/**
+ Duplicate a given File Identifier Descriptor.
+
+ @param[in] FileIdentifierDesc File Identifier Descriptor pointer.
+ @param[out] NewFileIdentifierDesc The duplicated File Identifier Descriptor.
+
+**/
+VOID
+DuplicateFid (
+ IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc,
+ OUT UDF_FILE_IDENTIFIER_DESCRIPTOR **NewFileIdentifierDesc
+ )
+{
+ *NewFileIdentifierDesc =
+ (UDF_FILE_IDENTIFIER_DESCRIPTOR *)AllocateCopyPool (
+ (UINTN) GetFidDescriptorLength (FileIdentifierDesc), FileIdentifierDesc);
+}
+
+/**
+ Duplicate either a given File Entry or a given Extended File Entry.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] Volume Volume information pointer.
+ @param[in] FileEntry (Extended) File Entry pointer.
+ @param[out] NewFileEntry The duplicated (Extended) File Entry.
+
+**/
+VOID
+DuplicateFe (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN VOID *FileEntry,
+ OUT VOID **NewFileEntry
+ )
+{
+ *NewFileEntry = AllocateCopyPool (Volume->FileEntrySize, FileEntry);
+}
+
+/**
+ Get raw data + length of a given File Entry or Extended File Entry.
+
+ The file's recorded data can contain either real file content (inline) or
+ a sequence of extents (or Allocation Descriptors) which tells where file's
+ content is stored in.
+
+ NOTE: The FE/EFE can be thought it was an inode.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The (Extended) File Entry is external input, so this routine will do basic
+ validation for (Extended) File Entry and report status.
+
+ @param[in] FileEntryData (Extended) File Entry pointer.
+ @param[in] FileEntrySize Size of the (Extended) File Entry specified
+ by FileEntryData.
+ @param[out] Data Buffer contains the raw data of a given
+ (Extended) File Entry.
+ @param[out] Length Length of the data in Buffer.
+
+ @retval EFI_SUCCESS Raw data and size of the FE/EFE was read.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+
+**/
+EFI_STATUS
+GetFileEntryData (
+ IN VOID *FileEntryData,
+ IN UINTN FileEntrySize,
+ OUT VOID **Data,
+ OUT UINT64 *Length
+ )
+{
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+ UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry;
+ UDF_FILE_ENTRY *FileEntry;
+
+ DescriptorTag = FileEntryData;
+
+ if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) {
+ ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData;
+
+ *Length = ExtendedFileEntry->InformationLength;
+ *Data = (VOID *)((UINT8 *)ExtendedFileEntry->Data +
+ ExtendedFileEntry->LengthOfExtendedAttributes);
+ } else if (DescriptorTag->TagIdentifier == UdfFileEntry) {
+ FileEntry = (UDF_FILE_ENTRY *)FileEntryData;
+
+ *Length = FileEntry->InformationLength;
+ *Data = (VOID *)((UINT8 *)FileEntry->Data +
+ FileEntry->LengthOfExtendedAttributes);
+ }
+
+ if ((*Length > FileEntrySize) ||
+ ((UINTN)FileEntryData > (UINTN)(*Data)) ||
+ ((UINTN)(*Data) - (UINTN)FileEntryData > FileEntrySize - *Length)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Get Allocation Descriptors' data information from a given FE/EFE.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The (Extended) File Entry is external input, so this routine will do basic
+ validation for (Extended) File Entry and report status.
+
+ @param[in] FileEntryData (Extended) File Entry pointer.
+ @param[in] FileEntrySize Size of the (Extended) File Entry specified
+ by FileEntryData.
+ @param[out] AdsData Buffer contains the Allocation Descriptors'
+ data from a given FE/EFE.
+ @param[out] Length Length of the data in AdsData.
+
+ @retval EFI_SUCCESS The data and size of Allocation Descriptors
+ were read from the FE/EFE.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+
+**/
+EFI_STATUS
+GetAdsInformation (
+ IN VOID *FileEntryData,
+ IN UINTN FileEntrySize,
+ OUT VOID **AdsData,
+ OUT UINT64 *Length
+ )
+{
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+ UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry;
+ UDF_FILE_ENTRY *FileEntry;
+
+ DescriptorTag = FileEntryData;
+
+ if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) {
+ ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)FileEntryData;
+
+ *Length = ExtendedFileEntry->LengthOfAllocationDescriptors;
+ *AdsData = (VOID *)((UINT8 *)ExtendedFileEntry->Data +
+ ExtendedFileEntry->LengthOfExtendedAttributes);
+ } else if (DescriptorTag->TagIdentifier == UdfFileEntry) {
+ FileEntry = (UDF_FILE_ENTRY *)FileEntryData;
+
+ *Length = FileEntry->LengthOfAllocationDescriptors;
+ *AdsData = (VOID *)((UINT8 *)FileEntry->Data +
+ FileEntry->LengthOfExtendedAttributes);
+ }
+
+ if ((*Length > FileEntrySize) ||
+ ((UINTN)FileEntryData > (UINTN)(*AdsData)) ||
+ ((UINTN)(*AdsData) - (UINTN)FileEntryData > FileEntrySize - *Length)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Read next Long Allocation Descriptor from a given file's data.
+
+ @param[in] Data File's data pointer.
+ @param[in,out] Offset Starting offset of the File's data to read.
+ @param[in] Length Length of the data to read.
+ @param[out] FoundLongAd Long Allocation Descriptor pointer.
+
+ @retval EFI_SUCCESS A Long Allocation Descriptor was found.
+ @retval EFI_DEVICE_ERROR No more Long Allocation Descriptors.
+
+**/
+EFI_STATUS
+GetLongAdFromAds (
+ IN VOID *Data,
+ IN OUT UINT64 *Offset,
+ IN UINT64 Length,
+ OUT UDF_LONG_ALLOCATION_DESCRIPTOR **FoundLongAd
+ )
+{
+ UDF_LONG_ALLOCATION_DESCRIPTOR *LongAd;
+ UDF_EXTENT_FLAGS ExtentFlags;
+
+ for (;;) {
+ if (*Offset >= Length) {
+ //
+ // No more Long Allocation Descriptors.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ LongAd =
+ (UDF_LONG_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset);
+
+ //
+ // If it's either an indirect AD (Extended Alllocation Descriptor) or an
+ // allocated AD, then return it.
+ //
+ ExtentFlags = GET_EXTENT_FLAGS (LongAdsSequence, LongAd);
+ if (ExtentFlags == ExtentIsNextExtent ||
+ ExtentFlags == ExtentRecordedAndAllocated) {
+ break;
+ }
+
+ //
+ // This AD is either not recorded but allocated, or not recorded and not
+ // allocated. Skip it.
+ //
+ *Offset += AD_LENGTH (LongAdsSequence);
+ }
+
+ *FoundLongAd = LongAd;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read next Short Allocation Descriptor from a given file's data.
+
+ @param[in] Data File's data pointer.
+ @param[in,out] Offset Starting offset of the File's data to read.
+ @param[in] Length Length of the data to read.
+ @param[out] FoundShortAd Short Allocation Descriptor pointer.
+
+ @retval EFI_SUCCESS A Short Allocation Descriptor was found.
+ @retval EFI_DEVICE_ERROR No more Short Allocation Descriptors.
+
+**/
+EFI_STATUS
+GetShortAdFromAds (
+ IN VOID *Data,
+ IN OUT UINT64 *Offset,
+ IN UINT64 Length,
+ OUT UDF_SHORT_ALLOCATION_DESCRIPTOR **FoundShortAd
+ )
+{
+ UDF_SHORT_ALLOCATION_DESCRIPTOR *ShortAd;
+ UDF_EXTENT_FLAGS ExtentFlags;
+
+ for (;;) {
+ if (*Offset >= Length) {
+ //
+ // No more Short Allocation Descriptors.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ ShortAd =
+ (UDF_SHORT_ALLOCATION_DESCRIPTOR *)((UINT8 *)Data + *Offset);
+
+ //
+ // If it's either an indirect AD (Extended Alllocation Descriptor) or an
+ // allocated AD, then return it.
+ //
+ ExtentFlags = GET_EXTENT_FLAGS (ShortAdsSequence, ShortAd);
+ if (ExtentFlags == ExtentIsNextExtent ||
+ ExtentFlags == ExtentRecordedAndAllocated) {
+ break;
+ }
+
+ //
+ // This AD is either not recorded but allocated, or not recorded and not
+ // allocated. Skip it.
+ //
+ *Offset += AD_LENGTH (ShortAdsSequence);
+ }
+
+ *FoundShortAd = ShortAd;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get either a Short Allocation Descriptor or a Long Allocation Descriptor from
+ file's data.
+
+ @param[in] RecordingFlags Flag to indicate the type of descriptor.
+ @param[in] Data File's data pointer.
+ @param[in,out] Offset Starting offset of the File's data to read.
+ @param[in] Length Length of the data to read.
+ @param[out] FoundAd Allocation Descriptor pointer.
+
+ @retval EFI_SUCCESS A Short Allocation Descriptor was found.
+ @retval EFI_DEVICE_ERROR No more Allocation Descriptors.
+ Invalid type of descriptor was given.
+
+**/
+EFI_STATUS
+GetAllocationDescriptor (
+ IN UDF_FE_RECORDING_FLAGS RecordingFlags,
+ IN VOID *Data,
+ IN OUT UINT64 *Offset,
+ IN UINT64 Length,
+ OUT VOID **FoundAd
+ )
+{
+ if (RecordingFlags == LongAdsSequence) {
+ return GetLongAdFromAds (
+ Data,
+ Offset,
+ Length,
+ (UDF_LONG_ALLOCATION_DESCRIPTOR **)FoundAd
+ );
+ } else if (RecordingFlags == ShortAdsSequence) {
+ return GetShortAdFromAds (
+ Data,
+ Offset,
+ Length,
+ (UDF_SHORT_ALLOCATION_DESCRIPTOR **)FoundAd
+ );
+ }
+
+ //
+ // Code should never reach here.
+ //
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Return logical sector number of either Short or Long Allocation Descriptor.
+
+ @param[in] RecordingFlags Flag to indicate the type of descriptor.
+ @param[in] Volume Volume information pointer.
+ @param[in] ParentIcb Long Allocation Descriptor pointer.
+ @param[in] Ad Allocation Descriptor pointer.
+ @param[out] Lsn Logical sector number pointer.
+
+ @retval EFI_SUCCESS Logical sector number of the given Allocation
+ Descriptor successfully returned.
+ @retval EFI_UNSUPPORTED Logical sector number of the given Allocation
+ Descriptor is not returned due to unrecognized
+ format.
+
+**/
+EFI_STATUS
+GetAllocationDescriptorLsn (
+ IN UDF_FE_RECORDING_FLAGS RecordingFlags,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb,
+ IN VOID *Ad,
+ OUT UINT64 *Lsn
+ )
+{
+ UDF_PARTITION_DESCRIPTOR *PartitionDesc;
+
+ if (RecordingFlags == LongAdsSequence) {
+ return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad, Lsn);
+ } else if (RecordingFlags == ShortAdsSequence) {
+ PartitionDesc = GetPdFromLongAd (Volume, ParentIcb);
+ if (PartitionDesc == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ *Lsn = GetShortAdLsn (
+ Volume,
+ PartitionDesc,
+ (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad
+ );
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Code should never reach here.
+ //
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Return offset + length of a given indirect Allocation Descriptor (AED).
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume Volume information pointer.
+ @param[in] ParentIcb Long Allocation Descriptor pointer.
+ @param[in] RecordingFlags Flag to indicate the type of descriptor.
+ @param[in] Ad Allocation Descriptor pointer.
+ @param[out] Offset Offset of a given indirect Allocation
+ Descriptor.
+ @param[out] Length Length of a given indirect Allocation
+ Descriptor.
+
+ @retval EFI_SUCCESS The offset and length were returned.
+ @retval EFI_OUT_OF_RESOURCES The offset and length were not returned due
+ to lack of resources.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval other The offset and length were not returned.
+
+**/
+EFI_STATUS
+GetAedAdsOffset (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb,
+ IN UDF_FE_RECORDING_FLAGS RecordingFlags,
+ IN VOID *Ad,
+ OUT UINT64 *Offset,
+ OUT UINT64 *Length
+ )
+{
+ EFI_STATUS Status;
+ UINT32 ExtentLength;
+ UINT64 Lsn;
+ VOID *Data;
+ UINT32 LogicalBlockSize;
+ UDF_ALLOCATION_EXTENT_DESCRIPTOR *AllocExtDesc;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+
+ ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+ Status = GetAllocationDescriptorLsn (RecordingFlags,
+ Volume,
+ ParentIcb,
+ Ad,
+ &Lsn);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Data = AllocatePool (ExtentLength);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize;
+
+ //
+ // Read extent.
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (Lsn, LogicalBlockSize),
+ ExtentLength,
+ Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ AllocExtDesc = (UDF_ALLOCATION_EXTENT_DESCRIPTOR *)Data;
+
+ DescriptorTag = &AllocExtDesc->DescriptorTag;
+
+ //
+ // Check if read extent contains a valid tag identifier for AED.
+ //
+ if (DescriptorTag->TagIdentifier != UdfAllocationExtentDescriptor) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Exit;
+ }
+
+ //
+ // Get AED's block offset and its length.
+ //
+ *Offset = MultU64x32 (Lsn, LogicalBlockSize) +
+ sizeof (UDF_ALLOCATION_EXTENT_DESCRIPTOR);
+ *Length = AllocExtDesc->LengthOfAllocationDescriptors;
+
+Exit:
+ FreePool (Data);
+
+ return Status;
+}
+
+/**
+ Read Allocation Extent Descriptor into memory.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume Volume information pointer.
+ @param[in] ParentIcb Long Allocation Descriptor pointer.
+ @param[in] RecordingFlags Flag to indicate the type of descriptor.
+ @param[in] Ad Allocation Descriptor pointer.
+ @param[out] Data Buffer that contains the Allocation Extent
+ Descriptor.
+ @param[out] Length Length of Data.
+
+ @retval EFI_SUCCESS The Allocation Extent Descriptor was read.
+ @retval EFI_OUT_OF_RESOURCES The Allocation Extent Descriptor was not read
+ due to lack of resources.
+ @retval other Fail to read the disk.
+
+**/
+EFI_STATUS
+GetAedAdsData (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb,
+ IN UDF_FE_RECORDING_FLAGS RecordingFlags,
+ IN VOID *Ad,
+ OUT VOID **Data,
+ OUT UINT64 *Length
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Offset;
+
+ //
+ // Get AED's offset + length.
+ //
+ Status = GetAedAdsOffset (
+ BlockIo,
+ DiskIo,
+ Volume,
+ ParentIcb,
+ RecordingFlags,
+ Ad,
+ &Offset,
+ Length
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Allocate buffer to read in AED's data.
+ //
+ *Data = AllocatePool ((UINTN) (*Length));
+ if (*Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ Offset,
+ (UINTN) (*Length),
+ *Data
+ );
+}
+
+/**
+ Function used to serialise reads of Allocation Descriptors.
+
+ @param[in] RecordingFlags Flag to indicate the type of descriptor.
+ @param[in] Ad Allocation Descriptor pointer.
+ @param[in, out] Buffer Buffer to hold the next Allocation Descriptor.
+ @param[in] Length Length of Buffer.
+
+ @retval EFI_SUCCESS Buffer was grown to hold the next Allocation
+ Descriptor.
+ @retval EFI_OUT_OF_RESOURCES Buffer was not grown due to lack of resources.
+
+**/
+EFI_STATUS
+GrowUpBufferToNextAd (
+ IN UDF_FE_RECORDING_FLAGS RecordingFlags,
+ IN VOID *Ad,
+ IN OUT VOID **Buffer,
+ IN UINT64 Length
+ )
+{
+ UINT32 ExtentLength;
+
+ ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+
+ if (*Buffer == NULL) {
+ *Buffer = AllocatePool (ExtentLength);
+ if (*Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ *Buffer = ReallocatePool ((UINTN) Length, (UINTN) (Length + ExtentLength), *Buffer);
+ if (*Buffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read data or size of either a File Entry or an Extended File Entry.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume Volume information pointer.
+ @param[in] ParentIcb Long Allocation Descriptor pointer.
+ @param[in] FileEntryData FE/EFE structure pointer.
+ @param[in, out] ReadFileInfo Read file information pointer.
+
+ @retval EFI_SUCCESS Data or size of a FE/EFE was read.
+ @retval EFI_OUT_OF_RESOURCES Data or size of a FE/EFE was not read due to
+ lack of resources.
+ @retval EFI_INVALID_PARAMETER The read file flag given in ReadFileInfo is
+ invalid.
+ @retval EFI_UNSUPPORTED The FE recording flag given in FileEntryData
+ is not supported.
+ @retval other Data or size of a FE/EFE was not read.
+
+**/
+EFI_STATUS
+ReadFile (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb,
+ IN VOID *FileEntryData,
+ IN OUT UDF_READ_FILE_INFO *ReadFileInfo
+ )
+{
+ EFI_STATUS Status;
+ UINT32 LogicalBlockSize;
+ VOID *Data;
+ VOID *DataBak;
+ UINT64 Length;
+ VOID *Ad;
+ UINT64 AdOffset;
+ UINT64 Lsn;
+ BOOLEAN DoFreeAed;
+ UINT64 FilePosition;
+ UINT64 Offset;
+ UINT64 DataOffset;
+ UINT64 BytesLeft;
+ UINT64 DataLength;
+ BOOLEAN FinishedSeeking;
+ UINT32 ExtentLength;
+ UDF_FE_RECORDING_FLAGS RecordingFlags;
+
+ LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize;
+ DoFreeAed = FALSE;
+
+ //
+ // set BytesLeft to suppress incorrect compiler/analyzer warnings
+ //
+ BytesLeft = 0;
+ DataOffset = 0;
+ FilePosition = 0;
+ FinishedSeeking = FALSE;
+ Data = NULL;
+
+ switch (ReadFileInfo->Flags) {
+ case ReadFileGetFileSize:
+ case ReadFileAllocateAndRead:
+ //
+ // Initialise ReadFileInfo structure for either getting file size, or
+ // reading file's recorded data.
+ //
+ ReadFileInfo->ReadLength = 0;
+ ReadFileInfo->FileData = NULL;
+ break;
+ case ReadFileSeekAndRead:
+ //
+ // About to seek a file and/or read its data.
+ //
+ Length = ReadFileInfo->FileSize - ReadFileInfo->FilePosition;
+ if (ReadFileInfo->FileDataSize > Length) {
+ //
+ // About to read beyond the EOF -- truncate it.
+ //
+ ReadFileInfo->FileDataSize = Length;
+ }
+
+ //
+ // Initialise data to start seeking and/or reading a file.
+ //
+ BytesLeft = ReadFileInfo->FileDataSize;
+ DataOffset = 0;
+ FilePosition = 0;
+ FinishedSeeking = FALSE;
+
+ break;
+ }
+
+ RecordingFlags = GET_FE_RECORDING_FLAGS (FileEntryData);
+ switch (RecordingFlags) {
+ case InlineData:
+ //
+ // There are no extents for this FE/EFE. All data is inline.
+ //
+ Status = GetFileEntryData (FileEntryData, Volume->FileEntrySize, &Data, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (ReadFileInfo->Flags == ReadFileGetFileSize) {
+ ReadFileInfo->ReadLength = Length;
+ } else if (ReadFileInfo->Flags == ReadFileAllocateAndRead) {
+ //
+ // Allocate buffer for starting read data.
+ //
+ ReadFileInfo->FileData = AllocatePool ((UINTN) Length);
+ if (ReadFileInfo->FileData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Read all inline data into ReadFileInfo->FileData
+ //
+ CopyMem (ReadFileInfo->FileData, Data, (UINTN) Length);
+ ReadFileInfo->ReadLength = Length;
+ } else if (ReadFileInfo->Flags == ReadFileSeekAndRead) {
+ //
+ // If FilePosition is non-zero, seek file to FilePosition, read
+ // FileDataSize bytes and then updates FilePosition.
+ //
+ CopyMem (
+ ReadFileInfo->FileData,
+ (VOID *)((UINT8 *)Data + ReadFileInfo->FilePosition),
+ (UINTN) ReadFileInfo->FileDataSize
+ );
+
+ ReadFileInfo->FilePosition += ReadFileInfo->FileDataSize;
+ } else {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ break;
+
+ case LongAdsSequence:
+ case ShortAdsSequence:
+ //
+ // This FE/EFE contains a run of Allocation Descriptors. Get data + size
+ // for start reading them out.
+ //
+ Status = GetAdsInformation (FileEntryData, Volume->FileEntrySize, &Data, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ AdOffset = 0;
+
+ for (;;) {
+ //
+ // Read AD.
+ //
+ Status = GetAllocationDescriptor (
+ RecordingFlags,
+ Data,
+ &AdOffset,
+ Length,
+ &Ad
+ );
+ if (Status == EFI_DEVICE_ERROR) {
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ //
+ // Check if AD is an indirect AD. If so, read Allocation Extent
+ // Descriptor and its extents (ADs).
+ //
+ if (GET_EXTENT_FLAGS (RecordingFlags, Ad) == ExtentIsNextExtent) {
+ DataBak = Data;
+ Status = GetAedAdsData (
+ BlockIo,
+ DiskIo,
+ Volume,
+ ParentIcb,
+ RecordingFlags,
+ Ad,
+ &Data,
+ &Length
+ );
+
+ if (!DoFreeAed) {
+ DoFreeAed = TRUE;
+ } else {
+ FreePool (DataBak);
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto Error_Get_Aed;
+ }
+ ASSERT (Data != NULL);
+
+ AdOffset = 0;
+ continue;
+ }
+
+ ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);
+
+ Status = GetAllocationDescriptorLsn (RecordingFlags,
+ Volume,
+ ParentIcb,
+ Ad,
+ &Lsn);
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ switch (ReadFileInfo->Flags) {
+ case ReadFileGetFileSize:
+ ReadFileInfo->ReadLength += ExtentLength;
+ break;
+ case ReadFileAllocateAndRead:
+ //
+ // Increase FileData (if necessary) to read next extent.
+ //
+ Status = GrowUpBufferToNextAd (
+ RecordingFlags,
+ Ad,
+ &ReadFileInfo->FileData,
+ ReadFileInfo->ReadLength
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Alloc_Buffer_To_Next_Ad;
+ }
+
+ //
+ // Read extent's data into FileData.
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (Lsn, LogicalBlockSize),
+ ExtentLength,
+ (VOID *)((UINT8 *)ReadFileInfo->FileData +
+ ReadFileInfo->ReadLength)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Read_Disk_Blk;
+ }
+
+ ReadFileInfo->ReadLength += ExtentLength;
+ break;
+ case ReadFileSeekAndRead:
+ //
+ // Seek file first before reading in its data.
+ //
+ if (FinishedSeeking) {
+ Offset = 0;
+ goto Skip_File_Seek;
+ }
+
+ if (FilePosition + ExtentLength < ReadFileInfo->FilePosition) {
+ FilePosition += ExtentLength;
+ goto Skip_Ad;
+ }
+
+ if (FilePosition + ExtentLength > ReadFileInfo->FilePosition) {
+ Offset = ReadFileInfo->FilePosition - FilePosition;
+ } else {
+ Offset = 0;
+ }
+
+ //
+ // Done with seeking file. Start reading its data.
+ //
+ FinishedSeeking = TRUE;
+
+ Skip_File_Seek:
+ //
+ // Make sure we don't read more data than really wanted.
+ //
+ if (ExtentLength - Offset > BytesLeft) {
+ DataLength = BytesLeft;
+ } else {
+ DataLength = ExtentLength - Offset;
+ }
+
+ //
+ // Read extent's data into FileData.
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ Offset + MultU64x32 (Lsn, LogicalBlockSize),
+ (UINTN) DataLength,
+ (VOID *)((UINT8 *)ReadFileInfo->FileData +
+ DataOffset)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Read_Disk_Blk;
+ }
+
+ //
+ // Update current file's position.
+ //
+ DataOffset += DataLength;
+ ReadFileInfo->FilePosition += DataLength;
+
+ BytesLeft -= DataLength;
+ if (BytesLeft == 0) {
+ //
+ // There is no more file data to read.
+ //
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ break;
+ }
+
+ Skip_Ad:
+ //
+ // Point to the next AD (extent).
+ //
+ AdOffset += AD_LENGTH (RecordingFlags);
+ }
+
+ break;
+ case ExtendedAdsSequence:
+ // FIXME: Not supported. Got no volume with it, yet.
+ ASSERT (FALSE);
+ Status = EFI_UNSUPPORTED;
+ break;
+
+ default:
+ //
+ // A flag value reserved by the ECMA-167 standard (3rd Edition - June
+ // 1997); 14.6 ICB Tag; 14.6.8 Flags (RBP 18); was found.
+ //
+ Status = EFI_UNSUPPORTED;
+ break;
+ }
+
+Done:
+ if (DoFreeAed) {
+ FreePool (Data);
+ }
+
+ return Status;
+
+Error_Read_Disk_Blk:
+Error_Alloc_Buffer_To_Next_Ad:
+ if (ReadFileInfo->Flags != ReadFileSeekAndRead) {
+ FreePool (ReadFileInfo->FileData);
+ }
+
+ if (DoFreeAed) {
+ FreePool (Data);
+ }
+
+Error_Get_Aed:
+ return Status;
+}
+
+/**
+ Find a file by its filename from a given Parent file.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume Volume information pointer.
+ @param[in] FileName File name string.
+ @param[in] Parent Parent directory file.
+ @param[in] Icb Long Allocation Descriptor pointer.
+ @param[out] File Found file.
+
+ @retval EFI_SUCCESS The file was found.
+ @retval EFI_INVALID_PARAMETER One or more input parameters are invalid.
+ @retval EFI_NOT_FOUND The file was not found.
+
+**/
+EFI_STATUS
+InternalFindFile (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN CHAR16 *FileName,
+ IN UDF_FILE_INFO *Parent,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb,
+ OUT UDF_FILE_INFO *File
+ )
+{
+ EFI_STATUS Status;
+ UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc;
+ UDF_READ_DIRECTORY_INFO ReadDirInfo;
+ BOOLEAN Found;
+ CHAR16 FoundFileName[UDF_FILENAME_LENGTH];
+ VOID *CompareFileEntry;
+
+ //
+ // Check if both Parent->FileIdentifierDesc and Icb are NULL.
+ //
+ if ((Parent->FileIdentifierDesc == NULL) && (Icb == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check if parent file is really directory.
+ //
+ if (FE_ICB_FILE_TYPE (Parent->FileEntry) != UdfFileEntryDirectory) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // If FileName is current file or working directory, just duplicate Parent's
+ // FE/EFE and FID descriptors.
+ //
+ if (StrCmp (FileName, L".") == 0) {
+ if (Parent->FileIdentifierDesc == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DuplicateFe (BlockIo, Volume, Parent->FileEntry, &File->FileEntry);
+ if (File->FileEntry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DuplicateFid (Parent->FileIdentifierDesc, &File->FileIdentifierDesc);
+ if (File->FileIdentifierDesc == NULL) {
+ FreePool (File->FileEntry);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Start directory listing.
+ //
+ ZeroMem ((VOID *)&ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO));
+ Found = FALSE;
+
+ for (;;) {
+ Status = ReadDirectoryEntry (
+ BlockIo,
+ DiskIo,
+ Volume,
+ (Parent->FileIdentifierDesc != NULL) ?
+ &Parent->FileIdentifierDesc->Icb :
+ Icb,
+ Parent->FileEntry,
+ &ReadDirInfo,
+ &FileIdentifierDesc
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_DEVICE_ERROR) {
+ Status = EFI_NOT_FOUND;
+ }
+
+ break;
+ }
+ //
+ // After calling function ReadDirectoryEntry(), if 'FileIdentifierDesc' is
+ // NULL, then the 'Status' must be EFI_OUT_OF_RESOURCES. Hence, if the code
+ // reaches here, 'FileIdentifierDesc' must be not NULL.
+ //
+ // The ASSERT here is for addressing a false positive NULL pointer
+ // dereference issue raised from static analysis.
+ //
+ ASSERT (FileIdentifierDesc != NULL);
+
+ if (FileIdentifierDesc->FileCharacteristics & PARENT_FILE) {
+ //
+ // This FID contains the location (FE/EFE) of the parent directory of this
+ // directory (Parent), and if FileName is either ".." or "\\", then it's
+ // the expected FID.
+ //
+ if (StrCmp (FileName, L"..") == 0 || StrCmp (FileName, L"\\") == 0) {
+ Found = TRUE;
+ break;
+ }
+ } else {
+ Status = GetFileNameFromFid (FileIdentifierDesc, ARRAY_SIZE (FoundFileName), FoundFileName);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (StrCmp (FileName, FoundFileName) == 0) {
+ //
+ // FID has been found. Prepare to find its respective FE/EFE.
+ //
+ Found = TRUE;
+ break;
+ }
+ }
+
+ FreePool ((VOID *)FileIdentifierDesc);
+ }
+
+ if (ReadDirInfo.DirectoryData != NULL) {
+ //
+ // Free all allocated resources for the directory listing.
+ //
+ FreePool (ReadDirInfo.DirectoryData);
+ }
+
+ if (Found) {
+ Status = EFI_SUCCESS;
+
+ File->FileIdentifierDesc = FileIdentifierDesc;
+
+ //
+ // If the requested file is root directory, then the FE/EFE was already
+ // retrieved in UdfOpenVolume() function, thus no need to find it again.
+ //
+ // Otherwise, find FE/EFE from the respective FID.
+ //
+ if (StrCmp (FileName, L"\\") != 0) {
+ Status = FindFileEntry (
+ BlockIo,
+ DiskIo,
+ Volume,
+ &FileIdentifierDesc->Icb,
+ &CompareFileEntry
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Find_Fe;
+ }
+
+ //
+ // Make sure that both Parent's FE/EFE and found FE/EFE are not equal.
+ //
+ if (CompareMem ((VOID *)Parent->FileEntry, (VOID *)CompareFileEntry,
+ Volume->FileEntrySize) != 0) {
+ File->FileEntry = CompareFileEntry;
+ } else {
+ FreePool ((VOID *)FileIdentifierDesc);
+ FreePool ((VOID *)CompareFileEntry);
+ Status = EFI_NOT_FOUND;
+ }
+ }
+ }
+
+ return Status;
+
+Error_Find_Fe:
+ FreePool ((VOID *)FileIdentifierDesc);
+
+ return Status;
+}
+
+/**
+ Read volume information on a medium which contains a valid UDF file system.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[out] Volume UDF volume information structure.
+
+ @retval EFI_SUCCESS Volume information read.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The volume was not read due to lack of resources.
+
+**/
+EFI_STATUS
+ReadUdfVolumeInformation (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ OUT UDF_VOLUME_INFO *Volume
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Read all necessary UDF volume information and keep it private to the driver
+ //
+ Status = ReadVolumeFileStructure (
+ BlockIo,
+ DiskIo,
+ Volume
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Find File Set Descriptor
+ //
+ Status = FindFileSetDescriptor (BlockIo, DiskIo, Volume);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+/**
+ Find the root directory on an UDF volume.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[out] File Root directory file.
+
+ @retval EFI_SUCCESS Root directory found.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The root directory was not found due to lack of
+ resources.
+
+**/
+EFI_STATUS
+FindRootDirectory (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ OUT UDF_FILE_INFO *File
+ )
+{
+ EFI_STATUS Status;
+ UDF_FILE_INFO Parent;
+
+ Status = FindFileEntry (
+ BlockIo,
+ DiskIo,
+ Volume,
+ &Volume->FileSetDesc.RootDirectoryIcb,
+ &File->FileEntry
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Parent.FileEntry = File->FileEntry;
+ Parent.FileIdentifierDesc = NULL;
+
+ Status = FindFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ L"\\",
+ NULL,
+ &Parent,
+ &Volume->FileSetDesc.RootDirectoryIcb,
+ File
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (File->FileEntry);
+ }
+
+ return Status;
+}
+
+/**
+ Find either a File Entry or a Extended File Entry from a given ICB.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] Icb ICB of the FID.
+ @param[out] FileEntry File Entry or Extended File Entry.
+
+ @retval EFI_SUCCESS File Entry or Extended File Entry found.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The FE/EFE entry was not found due to lack of
+ resources.
+
+**/
+EFI_STATUS
+FindFileEntry (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb,
+ OUT VOID **FileEntry
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Lsn;
+ UINT32 LogicalBlockSize;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+ VOID *ReadBuffer;
+
+ Status = GetLongAdLsn (Volume, Icb, &Lsn);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ LogicalBlockSize = Volume->LogicalVolDesc.LogicalBlockSize;
+
+ ReadBuffer = AllocateZeroPool (Volume->FileEntrySize);
+ if (ReadBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Read extent.
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (Lsn, LogicalBlockSize),
+ Volume->FileEntrySize,
+ ReadBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Read_Disk_Blk;
+ }
+
+ DescriptorTag = ReadBuffer;
+
+ //
+ // Check if the read extent contains a valid Tag Identifier for the expected
+ // FE/EFE.
+ //
+ if (DescriptorTag->TagIdentifier != UdfFileEntry &&
+ DescriptorTag->TagIdentifier != UdfExtendedFileEntry) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Error_Invalid_Fe;
+ }
+
+ *FileEntry = ReadBuffer;
+ return EFI_SUCCESS;
+
+Error_Invalid_Fe:
+Error_Read_Disk_Blk:
+ FreePool (ReadBuffer);
+
+ return Status;
+}
+
+/**
+ Find a file given its absolute path on an UDF volume.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] FilePath File's absolute path.
+ @param[in] Root Root directory file.
+ @param[in] Parent Parent directory file.
+ @param[in] Icb ICB of Parent.
+ @param[out] File Found file.
+
+ @retval EFI_SUCCESS FilePath was found.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The FilePath file was not found due to lack of
+ resources.
+
+**/
+EFI_STATUS
+FindFile (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN CHAR16 *FilePath,
+ IN UDF_FILE_INFO *Root,
+ IN UDF_FILE_INFO *Parent,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *Icb,
+ OUT UDF_FILE_INFO *File
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 FileName[UDF_FILENAME_LENGTH];
+ CHAR16 *FileNamePointer;
+ UDF_FILE_INFO PreviousFile;
+ VOID *FileEntry;
+
+ Status = EFI_NOT_FOUND;
+
+ CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO));
+ while (*FilePath != L'\0') {
+ FileNamePointer = FileName;
+ while (*FilePath != L'\0' && *FilePath != L'\\') {
+ if ((((UINTN)FileNamePointer - (UINTN)FileName) / sizeof (CHAR16)) >=
+ (ARRAY_SIZE (FileName) - 1)) {
+ return EFI_NOT_FOUND;
+ }
+
+ *FileNamePointer++ = *FilePath++;
+ }
+
+ *FileNamePointer = L'\0';
+ if (FileName[0] == L'\0') {
+ //
+ // Open root directory.
+ //
+ if (Root == NULL) {
+ //
+ // There is no file found for the root directory yet. So, find only its
+ // FID by now.
+ //
+ // See UdfOpenVolume() function.
+ //
+ Status = InternalFindFile (BlockIo,
+ DiskIo,
+ Volume,
+ L"\\",
+ &PreviousFile,
+ Icb,
+ File);
+ } else {
+ //
+ // We've already a file pointer (Root) for the root directory. Duplicate
+ // its FE/EFE and FID descriptors.
+ //
+ Status = EFI_SUCCESS;
+ DuplicateFe (BlockIo, Volume, Root->FileEntry, &File->FileEntry);
+ if (File->FileEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ //
+ // File->FileEntry is not NULL.
+ //
+ DuplicateFid (Root->FileIdentifierDesc, &File->FileIdentifierDesc);
+ if (File->FileIdentifierDesc == NULL) {
+ FreePool (File->FileEntry);
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+ }
+ } else {
+ //
+ // No root directory. Find filename from the current directory.
+ //
+ Status = InternalFindFile (BlockIo,
+ DiskIo,
+ Volume,
+ FileName,
+ &PreviousFile,
+ Icb,
+ File);
+ }
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // If the found file is a symlink, then find its respective FE/EFE and
+ // FID descriptors.
+ //
+ if (FE_ICB_FILE_TYPE (File->FileEntry) == UdfFileEntrySymlink) {
+ FreePool ((VOID *)File->FileIdentifierDesc);
+
+ FileEntry = File->FileEntry;
+
+ Status = ResolveSymlink (BlockIo,
+ DiskIo,
+ Volume,
+ &PreviousFile,
+ FileEntry,
+ File);
+
+ FreePool (FileEntry);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+ sizeof (UDF_FILE_INFO)) != 0) {
+ CleanupFileInformation (&PreviousFile);
+ }
+
+ CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO));
+ if (*FilePath != L'\0' && *FilePath == L'\\') {
+ FilePath++;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read a directory entry at a time on an UDF volume.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] ParentIcb ICB of the parent file.
+ @param[in] FileEntryData FE/EFE of the parent file.
+ @param[in, out] ReadDirInfo Next read directory listing structure
+ information.
+ @param[out] FoundFid File Identifier Descriptor pointer.
+
+ @retval EFI_SUCCESS Directory entry read.
+ @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The directory entry was not read due to lack of
+ resources.
+
+**/
+EFI_STATUS
+ReadDirectoryEntry (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_LONG_ALLOCATION_DESCRIPTOR *ParentIcb,
+ IN VOID *FileEntryData,
+ IN OUT UDF_READ_DIRECTORY_INFO *ReadDirInfo,
+ OUT UDF_FILE_IDENTIFIER_DESCRIPTOR **FoundFid
+ )
+{
+ EFI_STATUS Status;
+ UDF_READ_FILE_INFO ReadFileInfo;
+ UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc;
+
+ if (ReadDirInfo->DirectoryData == NULL) {
+ //
+ // The directory's recorded data has not been read yet. So let's cache it
+ // into memory and the next calls won't need to read it again.
+ //
+ ReadFileInfo.Flags = ReadFileAllocateAndRead;
+
+ Status = ReadFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ ParentIcb,
+ FileEntryData,
+ &ReadFileInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Fill in ReadDirInfo structure with the read directory's data information.
+ //
+ ReadDirInfo->DirectoryData = ReadFileInfo.FileData;
+ ReadDirInfo->DirectoryLength = ReadFileInfo.ReadLength;
+ }
+
+ do {
+ if (ReadDirInfo->FidOffset >= ReadDirInfo->DirectoryLength) {
+ //
+ // There are no longer FIDs for this directory. By returning
+ // EFI_DEVICE_ERROR to the callee will indicate end of directory
+ // listening.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Get FID for this entry.
+ //
+ FileIdentifierDesc = GET_FID_FROM_ADS (ReadDirInfo->DirectoryData,
+ ReadDirInfo->FidOffset);
+ //
+ // Update FidOffset to point to next FID.
+ //
+ ReadDirInfo->FidOffset += GetFidDescriptorLength (FileIdentifierDesc);
+ } while (FileIdentifierDesc->FileCharacteristics & DELETED_FILE);
+
+ DuplicateFid (FileIdentifierDesc, FoundFid);
+ if (*FoundFid == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get a filename (encoded in OSTA-compressed format) from a File Identifier
+ Descriptor on an UDF volume.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The File Identifier Descriptor is external input, so this routine will do
+ basic validation for File Identifier Descriptor and report status.
+
+ @param[in] FileIdentifierDesc File Identifier Descriptor pointer.
+ @param[in] CharMax The maximum number of FileName Unicode char,
+ including terminating null char.
+ @param[out] FileName Decoded filename.
+
+ @retval EFI_SUCCESS Filename decoded and read.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_BUFFER_TOO_SMALL The string buffer FileName cannot hold the
+ decoded filename.
+**/
+EFI_STATUS
+GetFileNameFromFid (
+ IN UDF_FILE_IDENTIFIER_DESCRIPTOR *FileIdentifierDesc,
+ IN UINTN CharMax,
+ OUT CHAR16 *FileName
+ )
+{
+ UINT8 *OstaCompressed;
+ UINT8 CompressionId;
+ UINT8 Length;
+ UINTN Index;
+ CHAR16 *FileNameBak;
+
+ if (CharMax == 0) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ OstaCompressed =
+ (UINT8 *)(
+ (UINT8 *)FileIdentifierDesc->Data +
+ FileIdentifierDesc->LengthOfImplementationUse
+ );
+
+ CompressionId = OstaCompressed[0];
+ if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ FileNameBak = FileName;
+
+ //
+ // Decode filename.
+ //
+ Length = FileIdentifierDesc->LengthOfFileIdentifier;
+ if (CompressionId == 16) {
+ if (((UINTN)Length >> 1) > CharMax) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ } else {
+ if ((Length != 0) && ((UINTN)Length - 1 > CharMax)) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ for (Index = 1; Index < Length; Index++) {
+ if (CompressionId == 16) {
+ *FileName = OstaCompressed[Index++] << 8;
+ } else {
+ *FileName = 0;
+ }
+
+ if (Index < Length) {
+ *FileName |= (CHAR16)(OstaCompressed[Index]);
+ }
+
+ FileName++;
+ }
+
+ Index = ((UINTN)FileName - (UINTN)FileNameBak) / sizeof (CHAR16);
+ if (Index > CharMax - 1) {
+ Index = CharMax - 1;
+ }
+ FileNameBak[Index] = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Resolve a symlink file on an UDF volume.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The Path Component is external input, so this routine will do basic
+ validation for Path Component and report status.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] Parent Parent file.
+ @param[in] FileEntryData FE/EFE structure pointer.
+ @param[out] File Resolved file.
+
+ @retval EFI_SUCCESS Symlink file resolved.
+ @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The symlink file was not resolved due to lack of
+ resources.
+
+**/
+EFI_STATUS
+ResolveSymlink (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_FILE_INFO *Parent,
+ IN VOID *FileEntryData,
+ OUT UDF_FILE_INFO *File
+ )
+{
+ EFI_STATUS Status;
+ UDF_READ_FILE_INFO ReadFileInfo;
+ UINT8 *Data;
+ UINT64 Length;
+ UINT8 *EndData;
+ UDF_PATH_COMPONENT *PathComp;
+ UINT8 PathCompLength;
+ CHAR16 FileName[UDF_FILENAME_LENGTH];
+ CHAR16 *Char;
+ UINTN Index;
+ UINT8 CompressionId;
+ UDF_FILE_INFO PreviousFile;
+ BOOLEAN NotParent;
+ BOOLEAN NotFile;
+
+ ZeroMem ((VOID *)File, sizeof (UDF_FILE_INFO));
+
+ //
+ // Symlink files on UDF volumes do not contain so much data other than
+ // Path Components which resolves to real filenames, so it's OK to read in
+ // all its data here -- usually the data will be inline with the FE/EFE for
+ // lower filenames.
+ //
+ ReadFileInfo.Flags = ReadFileAllocateAndRead;
+
+ Status = ReadFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ &Parent->FileIdentifierDesc->Icb,
+ FileEntryData,
+ &ReadFileInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Length = ReadFileInfo.ReadLength;
+
+ Data = (UINT8 *)ReadFileInfo.FileData;
+ EndData = Data + Length;
+
+ CopyMem ((VOID *)&PreviousFile, (VOID *)Parent, sizeof (UDF_FILE_INFO));
+
+ for (;;) {
+ PathComp = (UDF_PATH_COMPONENT *)Data;
+
+ PathCompLength = PathComp->LengthOfComponentIdentifier;
+
+ switch (PathComp->ComponentType) {
+ case 1:
+ //
+ // This Path Component specifies the root directory hierarchy subject to
+ // agreement between the originator and recipient of the medium. Skip it.
+ //
+ // Fall through.
+ //
+ case 2:
+ //
+ // "\\." of the current directory. Read next Path Component.
+ //
+ goto Next_Path_Component;
+ case 3:
+ //
+ // ".." (parent directory). Go to it.
+ //
+ CopyMem ((VOID *)FileName, L"..", 6);
+ break;
+ case 4:
+ //
+ // "." (current file). Duplicate both FE/EFE and FID of this file.
+ //
+ DuplicateFe (BlockIo, Volume, PreviousFile.FileEntry, &File->FileEntry);
+ if (File->FileEntry == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error_Find_File;
+ }
+
+ DuplicateFid (PreviousFile.FileIdentifierDesc,
+ &File->FileIdentifierDesc);
+ if (File->FileIdentifierDesc == NULL) {
+ FreePool (File->FileEntry);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error_Find_File;
+ }
+ goto Next_Path_Component;
+ case 5:
+ //
+ // This Path Component identifies an object, either a file or a
+ // directory or an alias.
+ //
+ // Decode it from the compressed data in ComponentIdentifier and find
+ // respective path.
+ //
+ CompressionId = PathComp->ComponentIdentifier[0];
+ if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ if ((UINTN)PathComp->ComponentIdentifier + PathCompLength > (UINTN)EndData) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ Char = FileName;
+ for (Index = 1; Index < PathCompLength; Index++) {
+ if (CompressionId == 16) {
+ *Char = *(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier +
+ Index) << 8;
+ Index++;
+ } else {
+ if (Index > ARRAY_SIZE (FileName)) {
+ return EFI_UNSUPPORTED;
+ }
+ *Char = 0;
+ }
+
+ if (Index < Length) {
+ *Char |= (CHAR16)(*(UINT8 *)((UINT8 *)PathComp->ComponentIdentifier + Index));
+ }
+
+ Char++;
+ }
+
+ Index = ((UINTN)Char - (UINTN)FileName) / sizeof (CHAR16);
+ if (Index > ARRAY_SIZE (FileName) - 1) {
+ Index = ARRAY_SIZE (FileName) - 1;
+ }
+ FileName[Index] = L'\0';
+ break;
+ default:
+ //
+ // According to the ECMA-167 standard (3rd Edition - June 1997), Section
+ // 14.16.1.1, all other values are reserved.
+ //
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Error_Find_File;
+ }
+
+ //
+ // Find file from the read filename in symlink's file data.
+ //
+ Status = InternalFindFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ FileName,
+ &PreviousFile,
+ NULL,
+ File
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error_Find_File;
+ }
+
+ Next_Path_Component:
+ Data += sizeof (UDF_PATH_COMPONENT) + PathCompLength;
+ if (Data >= EndData) {
+ break;
+ }
+
+ //
+ // Check the content in the file info pointed by File.
+ //
+ if ((File->FileEntry == NULL) || (File->FileIdentifierDesc == NULL)) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Error_Find_File;
+ }
+
+ NotParent = (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+ sizeof (UDF_FILE_INFO)) != 0);
+ NotFile = (CompareMem ((VOID *)&PreviousFile, (VOID *)File,
+ sizeof (UDF_FILE_INFO)) != 0);
+
+ if (NotParent && NotFile) {
+ CleanupFileInformation (&PreviousFile);
+ }
+
+ if (NotFile) {
+ CopyMem ((VOID *)&PreviousFile, (VOID *)File, sizeof (UDF_FILE_INFO));
+ }
+ }
+
+ //
+ // Unmap the symlink file.
+ //
+ FreePool (ReadFileInfo.FileData);
+
+ //
+ // Check the content in the resolved file info.
+ //
+ if ((File->FileEntry == NULL) || (File->FileIdentifierDesc == NULL)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ return EFI_SUCCESS;
+
+Error_Find_File:
+ if (CompareMem ((VOID *)&PreviousFile, (VOID *)Parent,
+ sizeof (UDF_FILE_INFO)) != 0) {
+ CleanupFileInformation (&PreviousFile);
+ }
+
+ FreePool (ReadFileInfo.FileData);
+
+ return Status;
+}
+
+/**
+ Clean up in-memory UDF file information.
+
+ @param[in] File File information pointer.
+
+**/
+VOID
+CleanupFileInformation (
+ IN UDF_FILE_INFO *File
+ )
+{
+ if (File->FileEntry != NULL) {
+ FreePool (File->FileEntry);
+ }
+ if (File->FileIdentifierDesc != NULL) {
+ FreePool ((VOID *)File->FileIdentifierDesc);
+ }
+
+ ZeroMem ((VOID *)File, sizeof (UDF_FILE_INFO));
+}
+
+/**
+ Find a file from its absolute path on an UDF volume.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] File File information structure.
+ @param[out] Size Size of the file.
+
+ @retval EFI_SUCCESS File size calculated and set in Size.
+ @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The file size was not calculated due to lack of
+ resources.
+
+**/
+EFI_STATUS
+GetFileSize (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_FILE_INFO *File,
+ OUT UINT64 *Size
+ )
+{
+ EFI_STATUS Status;
+ UDF_READ_FILE_INFO ReadFileInfo;
+
+ ReadFileInfo.Flags = ReadFileGetFileSize;
+
+ Status = ReadFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ &File->FileIdentifierDesc->Icb,
+ File->FileEntry,
+ &ReadFileInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Size = ReadFileInfo.ReadLength;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set information about a file on an UDF volume.
+
+ @param[in] File File pointer.
+ @param[in] FileSize Size of the file.
+ @param[in] FileName Filename of the file.
+ @param[in, out] BufferSize Size of the returned file infomation.
+ @param[out] Buffer Data of the returned file information.
+
+ @retval EFI_SUCCESS File information set.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The file information was not set due to lack of
+ resources.
+
+**/
+EFI_STATUS
+SetFileInfo (
+ IN UDF_FILE_INFO *File,
+ IN UINT64 FileSize,
+ IN CHAR16 *FileName,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ UINTN FileInfoLength;
+ EFI_FILE_INFO *FileInfo;
+ UDF_FILE_ENTRY *FileEntry;
+ UDF_EXTENDED_FILE_ENTRY *ExtendedFileEntry;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+
+ //
+ // Calculate the needed size for the EFI_FILE_INFO structure.
+ //
+ FileInfoLength = sizeof (EFI_FILE_INFO) + ((FileName != NULL) ?
+ StrSize (FileName) :
+ sizeof (CHAR16));
+ if (*BufferSize < FileInfoLength) {
+ //
+ // The given Buffer has no size enough for EFI_FILE_INFO structure.
+ //
+ *BufferSize = FileInfoLength;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Buffer now contains room enough to store EFI_FILE_INFO structure.
+ // Now, fill it in with all necessary information about the file.
+ //
+ FileInfo = (EFI_FILE_INFO *)Buffer;
+ FileInfo->Size = FileInfoLength;
+ FileInfo->Attribute &= ~EFI_FILE_VALID_ATTR;
+ FileInfo->Attribute |= EFI_FILE_READ_ONLY;
+
+ if (IS_FID_DIRECTORY_FILE (File->FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+ } else if (IS_FID_NORMAL_FILE (File->FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_ARCHIVE;
+ }
+
+ if (IS_FID_HIDDEN_FILE (File->FileIdentifierDesc)) {
+ FileInfo->Attribute |= EFI_FILE_HIDDEN;
+ }
+
+ DescriptorTag = File->FileEntry;
+
+ if (DescriptorTag->TagIdentifier == UdfFileEntry) {
+ FileEntry = (UDF_FILE_ENTRY *)File->FileEntry;
+
+ //
+ // Check if FE has the system attribute set.
+ //
+ if (FileEntry->IcbTag.Flags & (1 << 10)) {
+ FileInfo->Attribute |= EFI_FILE_SYSTEM;
+ }
+
+ FileInfo->FileSize = FileSize;
+ FileInfo->PhysicalSize = FileSize;
+
+ FileInfo->CreateTime.Year = FileEntry->AccessTime.Year;
+ FileInfo->CreateTime.Month = FileEntry->AccessTime.Month;
+ FileInfo->CreateTime.Day = FileEntry->AccessTime.Day;
+ FileInfo->CreateTime.Hour = FileEntry->AccessTime.Hour;
+ FileInfo->CreateTime.Minute = FileEntry->AccessTime.Minute;
+ FileInfo->CreateTime.Second = FileEntry->AccessTime.Second;
+ FileInfo->CreateTime.Nanosecond =
+ FileEntry->AccessTime.HundredsOfMicroseconds;
+
+ FileInfo->LastAccessTime.Year =
+ FileEntry->AccessTime.Year;
+ FileInfo->LastAccessTime.Month =
+ FileEntry->AccessTime.Month;
+ FileInfo->LastAccessTime.Day =
+ FileEntry->AccessTime.Day;
+ FileInfo->LastAccessTime.Hour =
+ FileEntry->AccessTime.Hour;
+ FileInfo->LastAccessTime.Minute =
+ FileEntry->AccessTime.Minute;
+ FileInfo->LastAccessTime.Second =
+ FileEntry->AccessTime.Second;
+ FileInfo->LastAccessTime.Nanosecond =
+ FileEntry->AccessTime.HundredsOfMicroseconds;
+ } else if (DescriptorTag->TagIdentifier == UdfExtendedFileEntry) {
+ ExtendedFileEntry = (UDF_EXTENDED_FILE_ENTRY *)File->FileEntry;
+
+ //
+ // Check if EFE has the system attribute set.
+ //
+ if (ExtendedFileEntry->IcbTag.Flags & (1 << 10)) {
+ FileInfo->Attribute |= EFI_FILE_SYSTEM;
+ }
+
+ FileInfo->FileSize = FileSize;
+ FileInfo->PhysicalSize = FileSize;
+
+ FileInfo->CreateTime.Year = ExtendedFileEntry->CreationTime.Year;
+ FileInfo->CreateTime.Month = ExtendedFileEntry->CreationTime.Month;
+ FileInfo->CreateTime.Day = ExtendedFileEntry->CreationTime.Day;
+ FileInfo->CreateTime.Hour = ExtendedFileEntry->CreationTime.Hour;
+ FileInfo->CreateTime.Minute = ExtendedFileEntry->CreationTime.Second;
+ FileInfo->CreateTime.Second = ExtendedFileEntry->CreationTime.Second;
+ FileInfo->CreateTime.Nanosecond =
+ ExtendedFileEntry->AccessTime.HundredsOfMicroseconds;
+
+ FileInfo->LastAccessTime.Year =
+ ExtendedFileEntry->AccessTime.Year;
+ FileInfo->LastAccessTime.Month =
+ ExtendedFileEntry->AccessTime.Month;
+ FileInfo->LastAccessTime.Day =
+ ExtendedFileEntry->AccessTime.Day;
+ FileInfo->LastAccessTime.Hour =
+ ExtendedFileEntry->AccessTime.Hour;
+ FileInfo->LastAccessTime.Minute =
+ ExtendedFileEntry->AccessTime.Minute;
+ FileInfo->LastAccessTime.Second =
+ ExtendedFileEntry->AccessTime.Second;
+ FileInfo->LastAccessTime.Nanosecond =
+ ExtendedFileEntry->AccessTime.HundredsOfMicroseconds;
+ }
+
+ FileInfo->CreateTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
+ FileInfo->CreateTime.Daylight = EFI_TIME_ADJUST_DAYLIGHT;
+ FileInfo->LastAccessTime.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
+ FileInfo->LastAccessTime.Daylight = EFI_TIME_ADJUST_DAYLIGHT;
+
+ CopyMem ((VOID *)&FileInfo->ModificationTime,
+ (VOID *)&FileInfo->LastAccessTime,
+ sizeof (EFI_TIME));
+
+ if (FileName != NULL) {
+ StrCpyS (FileInfo->FileName, StrLen (FileName) + 1, FileName);
+ } else {
+ FileInfo->FileName[0] = '\0';
+ }
+
+ *BufferSize = FileInfoLength;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get volume label of an UDF volume.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The File Set Descriptor is external input, so this routine will do basic
+ validation for File Set Descriptor and report status.
+
+ @param[in] Volume Volume information pointer.
+ @param[in] CharMax The maximum number of Unicode char in String,
+ including terminating null char.
+ @param[out] String String buffer pointer to store the volume label.
+
+ @retval EFI_SUCCESS Volume label is returned.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_BUFFER_TOO_SMALL The string buffer String cannot hold the
+ volume label.
+
+**/
+EFI_STATUS
+GetVolumeLabel (
+ IN UDF_VOLUME_INFO *Volume,
+ IN UINTN CharMax,
+ OUT CHAR16 *String
+ )
+{
+ UDF_FILE_SET_DESCRIPTOR *FileSetDesc;
+ UINTN Index;
+ UINT8 *OstaCompressed;
+ UINT8 CompressionId;
+ CHAR16 *StringBak;
+
+ FileSetDesc = &Volume->FileSetDesc;
+
+ OstaCompressed = &FileSetDesc->LogicalVolumeIdentifier[0];
+
+ CompressionId = OstaCompressed[0];
+ if (!IS_VALID_COMPRESSION_ID (CompressionId)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ StringBak = String;
+ for (Index = 1; Index < 128; Index++) {
+ if (CompressionId == 16) {
+ if ((Index >> 1) > CharMax) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *String = *(UINT8 *)(OstaCompressed + Index) << 8;
+ Index++;
+ } else {
+ if (Index > CharMax) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *String = 0;
+ }
+
+ if (Index < 128) {
+ *String |= (CHAR16)(*(UINT8 *)(OstaCompressed + Index));
+ }
+
+ //
+ // Unlike FID Identifiers, Logical Volume Identifier is stored in a
+ // NULL-terminated OSTA compressed format, so we must check for the NULL
+ // character.
+ //
+ if (*String == L'\0') {
+ break;
+ }
+
+ String++;
+ }
+
+ Index = ((UINTN)String - (UINTN)StringBak) / sizeof (CHAR16);
+ if (Index > CharMax - 1) {
+ Index = CharMax - 1;
+ }
+ StringBak[Index] = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get volume and free space size information of an UDF volume.
+
+ @attention This is boundary function that may receive untrusted input.
+ @attention The input is from FileSystem.
+
+ The Logical Volume Descriptor and the Logical Volume Integrity Descriptor are
+ external inputs, so this routine will do basic validation for both descriptors
+ and report status.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[out] VolumeSize Volume size.
+ @param[out] FreeSpaceSize Free space size.
+
+ @retval EFI_SUCCESS Volume and free space size calculated.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The volume and free space size were not
+ calculated due to lack of resources.
+
+**/
+EFI_STATUS
+GetVolumeSize (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ OUT UINT64 *VolumeSize,
+ OUT UINT64 *FreeSpaceSize
+ )
+{
+ EFI_STATUS Status;
+ UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc;
+ UDF_EXTENT_AD *ExtentAd;
+ UINT64 Lsn;
+ UINT32 LogicalBlockSize;
+ UDF_LOGICAL_VOLUME_INTEGRITY *LogicalVolInt;
+ UDF_DESCRIPTOR_TAG *DescriptorTag;
+ UINTN Index;
+ UINTN Length;
+ UINT32 LsnsNo;
+
+ LogicalVolDesc = &Volume->LogicalVolDesc;
+
+ ExtentAd = &LogicalVolDesc->IntegritySequenceExtent;
+
+ if ((ExtentAd->ExtentLength == 0) ||
+ (ExtentAd->ExtentLength < sizeof (UDF_LOGICAL_VOLUME_INTEGRITY))) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ LogicalVolInt = AllocatePool (ExtentAd->ExtentLength);
+ if (LogicalVolInt == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get location of Logical Volume Integrity Descriptor
+ //
+ Lsn = (UINT64)ExtentAd->ExtentLocation - Volume->MainVdsStartLocation;
+
+ LogicalBlockSize = LogicalVolDesc->LogicalBlockSize;
+
+ //
+ // Read disk block
+ //
+ Status = DiskIo->ReadDisk (
+ DiskIo,
+ BlockIo->Media->MediaId,
+ MultU64x32 (Lsn, LogicalBlockSize),
+ ExtentAd->ExtentLength,
+ LogicalVolInt
+ );
+ if (EFI_ERROR (Status)) {
+ goto Out_Free;
+ }
+
+ DescriptorTag = &LogicalVolInt->DescriptorTag;
+
+ //
+ // Check if read block is a Logical Volume Integrity Descriptor
+ //
+ if (DescriptorTag->TagIdentifier != UdfLogicalVolumeIntegrityDescriptor) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Out_Free;
+ }
+
+ if ((LogicalVolInt->NumberOfPartitions > MAX_UINT32 / sizeof (UINT32) / 2) ||
+ (LogicalVolInt->NumberOfPartitions * sizeof (UINT32) * 2 >
+ ExtentAd->ExtentLength - sizeof (UDF_LOGICAL_VOLUME_INTEGRITY))) {
+ Status = EFI_VOLUME_CORRUPTED;
+ goto Out_Free;
+ }
+
+ *VolumeSize = 0;
+ *FreeSpaceSize = 0;
+
+ Length = LogicalVolInt->NumberOfPartitions;
+ for (Index = 0; Index < Length; Index += sizeof (UINT32)) {
+ LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index);
+ //
+ // Check if size is not specified
+ //
+ if (LsnsNo == 0xFFFFFFFFUL) {
+ continue;
+ }
+ //
+ // Accumulate free space size
+ //
+ *FreeSpaceSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize);
+ }
+
+ Length = LogicalVolInt->NumberOfPartitions * sizeof (UINT32) * 2;
+ for (; Index < Length; Index += sizeof (UINT32)) {
+ LsnsNo = *(UINT32 *)((UINT8 *)LogicalVolInt->Data + Index);
+ //
+ // Check if size is not specified
+ //
+ if (LsnsNo == 0xFFFFFFFFUL) {
+ continue;
+ }
+ //
+ // Accumulate used volume space
+ //
+ *VolumeSize += MultU64x32 ((UINT64)LsnsNo, LogicalBlockSize);
+ }
+
+ Status = EFI_SUCCESS;
+
+Out_Free:
+ //
+ // Free Logical Volume Integrity Descriptor
+ //
+ FreePool (LogicalVolInt);
+
+ return Status;
+}
+
+/**
+ Seek a file and read its data into memory on an UDF volume.
+
+ @param[in] BlockIo BlockIo interface.
+ @param[in] DiskIo DiskIo interface.
+ @param[in] Volume UDF volume information structure.
+ @param[in] File File information structure.
+ @param[in] FileSize Size of the file.
+ @param[in, out] FilePosition File position.
+ @param[in, out] Buffer File data.
+ @param[in, out] BufferSize Read size.
+
+ @retval EFI_SUCCESS File seeked and read.
+ @retval EFI_UNSUPPORTED Extended Allocation Descriptors not supported.
+ @retval EFI_NO_MEDIA The device has no media.
+ @retval EFI_DEVICE_ERROR The device reported an error.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_OUT_OF_RESOURCES The file's recorded data was not read due to lack
+ of resources.
+
+**/
+EFI_STATUS
+ReadFileData (
+ IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
+ IN EFI_DISK_IO_PROTOCOL *DiskIo,
+ IN UDF_VOLUME_INFO *Volume,
+ IN UDF_FILE_INFO *File,
+ IN UINT64 FileSize,
+ IN OUT UINT64 *FilePosition,
+ IN OUT VOID *Buffer,
+ IN OUT UINT64 *BufferSize
+ )
+{
+ EFI_STATUS Status;
+ UDF_READ_FILE_INFO ReadFileInfo;
+
+ ReadFileInfo.Flags = ReadFileSeekAndRead;
+ ReadFileInfo.FilePosition = *FilePosition;
+ ReadFileInfo.FileData = Buffer;
+ ReadFileInfo.FileDataSize = *BufferSize;
+ ReadFileInfo.FileSize = FileSize;
+
+ Status = ReadFile (
+ BlockIo,
+ DiskIo,
+ Volume,
+ &File->FileIdentifierDesc->Icb,
+ File->FileEntry,
+ &ReadFileInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *BufferSize = ReadFileInfo.FileDataSize;
+ *FilePosition = ReadFileInfo.FilePosition;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if ControllerHandle supports an UDF file system.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] ControllerHandle Handle of device to test.
+
+ @retval EFI_SUCCESS UDF file system found.
+ @retval EFI_UNSUPPORTED UDF file system not found.
+
+**/
+EFI_STATUS
+SupportUdfFileSystem (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
+ EFI_DEVICE_PATH_PROTOCOL *LastDevicePathNode;
+ EFI_GUID *VendorDefinedGuid;
+
+ //
+ // Open Device Path protocol on ControllerHandle
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = EFI_UNSUPPORTED;
+
+ //
+ // Get last Device Path node
+ //
+ LastDevicePathNode = NULL;
+ DevicePathNode = DevicePath;
+ while (!IsDevicePathEnd (DevicePathNode)) {
+ LastDevicePathNode = DevicePathNode;
+ DevicePathNode = NextDevicePathNode (DevicePathNode);
+ }
+ //
+ // Check if last Device Path node contains a Vendor-Defined Media Device Path
+ // of an UDF file system.
+ //
+ if (LastDevicePathNode != NULL &&
+ DevicePathType (LastDevicePathNode) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType (LastDevicePathNode) == MEDIA_VENDOR_DP) {
+ VendorDefinedGuid = (EFI_GUID *)((UINTN)LastDevicePathNode +
+ OFFSET_OF (VENDOR_DEVICE_PATH, Guid));
+ if (CompareGuid (VendorDefinedGuid, &gUdfDevPathGuid)) {
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Close Device Path protocol on ControllerHandle
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ return Status;
+}
|