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