CloverBootloader/MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c

2925 lines
86 KiB
C
Raw Normal View History

/** @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;
}