diff options
| author | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
|---|---|---|
| committer | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
| commit | 7e2ccccace24636f29ddc210b94606abd4c7e42b (patch) | |
| tree | 545d43ea12671048956cafeb12303e84d601e571 /MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c | |
| parent | 27b044605cd5f6b33a3d231576003850b3fe305b (diff) | |
| download | edk2-trunk.tar.xz | |
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c')
| -rw-r--r-- | MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c | 5972 |
1 files changed, 2986 insertions, 2986 deletions
diff --git a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c index 86020de6e0..52eb472a58 100644 --- a/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c +++ b/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c @@ -1,2986 +1,2986 @@ -/** @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",
- __func__
- ));
- 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;
-}
+/** @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", + __func__ + )); + 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; +} |
