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