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