mirror of https://github.com/acidanthera/audk.git
789 lines
22 KiB
C
789 lines
22 KiB
C
/** @file
|
|
Scan for an UDF file system on a formatted media.
|
|
|
|
Caution: This file requires additional review when modified.
|
|
This driver will have external input - CD/DVD media.
|
|
This external input must be validated carefully to avoid security issue like
|
|
buffer overflow, integer overflow.
|
|
|
|
FindUdfFileSystem() routine will consume the media properties and do basic
|
|
validation.
|
|
|
|
Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc.
|
|
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 "Partition.h"
|
|
|
|
#define MAX_CORRECTION_BLOCKS_NUM 512u
|
|
|
|
//
|
|
// C5BD4D42-1A76-4996-8956-73CDA326CD0A
|
|
//
|
|
#define EFI_UDF_DEVICE_PATH_GUID \
|
|
{ 0xC5BD4D42, 0x1A76, 0x4996, \
|
|
{ 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A } \
|
|
}
|
|
|
|
typedef struct {
|
|
VENDOR_DEVICE_PATH DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL End;
|
|
} UDF_DEVICE_PATH;
|
|
|
|
//
|
|
// Vendor-Defined Device Path GUID for UDF file system
|
|
//
|
|
EFI_GUID gUdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID;
|
|
|
|
//
|
|
// Vendor-Defined Media Device Path for UDF file system
|
|
//
|
|
UDF_DEVICE_PATH gUdfDevicePath = {
|
|
{ { MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
|
|
{ sizeof (VENDOR_DEVICE_PATH), 0 } },
|
|
EFI_UDF_DEVICE_PATH_GUID
|
|
},
|
|
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{ sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
|
|
}
|
|
};
|
|
|
|
/**
|
|
Find the anchor volume descriptor pointer.
|
|
|
|
@param[in] BlockIo BlockIo interface.
|
|
@param[in] DiskIo DiskIo interface.
|
|
@param[out] AnchorPoint Anchor volume descriptor pointer.
|
|
@param[out] LastRecordedBlock Last recorded block.
|
|
|
|
@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,
|
|
OUT EFI_LBA *LastRecordedBlock
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 BlockSize;
|
|
EFI_LBA EndLBA;
|
|
UDF_DESCRIPTOR_TAG *DescriptorTag;
|
|
UINTN AvdpsCount;
|
|
UINTN Size;
|
|
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoints;
|
|
INTN Index;
|
|
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPointPtr;
|
|
EFI_LBA LastAvdpBlockNum;
|
|
|
|
//
|
|
// UDF 2.60, 2.2.3 Anchor Volume Descriptor Pointer
|
|
//
|
|
// An Anchor Volume Descriptor Pointer structure shall be recorded in at
|
|
// least 2 of the following 3 locations on the media: Logical Sector 256,
|
|
// N - 256 or N, where N is the last *addressable* sector of a volume.
|
|
//
|
|
// To figure out what logical sector N is, the SCSI commands READ CAPACITY and
|
|
// READ TRACK INFORMATION are used, however many drives or medias report their
|
|
// "last recorded block" wrongly. Although, READ CAPACITY returns the last
|
|
// readable data block but there might be unwritten blocks, which are located
|
|
// outside any track and therefore AVDP will not be found at block N.
|
|
//
|
|
// That said, we define a magic number of 512 blocks to be used as correction
|
|
// when attempting to find AVDP and define last block number.
|
|
//
|
|
BlockSize = BlockIo->Media->BlockSize;
|
|
EndLBA = BlockIo->Media->LastBlock;
|
|
*LastRecordedBlock = EndLBA;
|
|
AvdpsCount = 0;
|
|
|
|
//
|
|
// Check if the block size of the underlying media can hold the data of an
|
|
// Anchor Volume Descriptor Pointer
|
|
//
|
|
if (BlockSize < sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"%a: Media block size 0x%x unable to hold an AVDP.\n",
|
|
__FUNCTION__,
|
|
BlockSize
|
|
));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Find AVDP at block 256
|
|
//
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
MultU64x32 (256, BlockSize),
|
|
sizeof (*AnchorPoint),
|
|
AnchorPoint
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
DescriptorTag = &AnchorPoint->DescriptorTag;
|
|
|
|
//
|
|
// Check if read block is a valid AVDP descriptor
|
|
//
|
|
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
|
|
DEBUG ((DEBUG_INFO, "%a: found AVDP at block %d\n", __FUNCTION__, 256));
|
|
AvdpsCount++;
|
|
}
|
|
|
|
//
|
|
// Find AVDP at block N - 256
|
|
//
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
MultU64x32 ((UINT64)EndLBA - 256, BlockSize),
|
|
sizeof (*AnchorPoint),
|
|
AnchorPoint
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check if read block is a valid AVDP descriptor
|
|
//
|
|
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer &&
|
|
++AvdpsCount == 2) {
|
|
DEBUG ((DEBUG_INFO, "%a: found AVDP at block %Ld\n", __FUNCTION__,
|
|
EndLBA - 256));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Check if at least one AVDP was found in previous locations
|
|
//
|
|
if (AvdpsCount == 0) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
//
|
|
// Find AVDP at block N
|
|
//
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
MultU64x32 ((UINT64)EndLBA, BlockSize),
|
|
sizeof (*AnchorPoint),
|
|
AnchorPoint
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check if read block is a valid AVDP descriptor
|
|
//
|
|
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// No AVDP found at block N. Possibly drive/media returned bad last recorded
|
|
// block, or it is part of unwritten data blocks and outside any track.
|
|
//
|
|
// Search backwards for an AVDP from block N-1 through
|
|
// N-MAX_CORRECTION_BLOCKS_NUM. If any AVDP is found, then correct last block
|
|
// number for the new UDF partition child handle.
|
|
//
|
|
Size = MAX_CORRECTION_BLOCKS_NUM * BlockSize;
|
|
|
|
AnchorPoints = AllocateZeroPool (Size);
|
|
if (AnchorPoints == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Read consecutive MAX_CORRECTION_BLOCKS_NUM disk blocks
|
|
//
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
MultU64x32 ((UINT64)EndLBA - MAX_CORRECTION_BLOCKS_NUM, BlockSize),
|
|
Size,
|
|
AnchorPoints
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Out_Free;
|
|
}
|
|
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
|
|
//
|
|
// Search for AVDP from blocks N-1 through N-MAX_CORRECTION_BLOCKS_NUM
|
|
//
|
|
for (Index = MAX_CORRECTION_BLOCKS_NUM - 2; Index >= 0; Index--) {
|
|
AnchorPointPtr = (VOID *)((UINTN)AnchorPoints + Index * BlockSize);
|
|
|
|
DescriptorTag = &AnchorPointPtr->DescriptorTag;
|
|
|
|
//
|
|
// Check if read block is a valid AVDP descriptor
|
|
//
|
|
if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
|
|
//
|
|
// Calculate last recorded block number
|
|
//
|
|
LastAvdpBlockNum = EndLBA - (MAX_CORRECTION_BLOCKS_NUM - Index);
|
|
DEBUG ((DEBUG_WARN, "%a: found AVDP at block %Ld\n", __FUNCTION__,
|
|
LastAvdpBlockNum));
|
|
DEBUG ((DEBUG_WARN, "%a: correcting last block from %Ld to %Ld\n",
|
|
__FUNCTION__, EndLBA, LastAvdpBlockNum));
|
|
//
|
|
// Save read AVDP from last block
|
|
//
|
|
CopyMem (AnchorPoint, AnchorPointPtr, sizeof (*AnchorPointPtr));
|
|
//
|
|
// Set last recorded block number
|
|
//
|
|
*LastRecordedBlock = LastAvdpBlockNum;
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Out_Free:
|
|
FreePool (AnchorPoints);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Find UDF volume identifiers in a Volume Recognition Sequence.
|
|
|
|
@param[in] BlockIo BlockIo interface.
|
|
@param[in] DiskIo DiskIo interface.
|
|
|
|
@retval EFI_SUCCESS UDF volume identifiers were found.
|
|
@retval EFI_NOT_FOUND UDF volume identifiers were not found.
|
|
@retval other Failed to perform disk I/O.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FindUdfVolumeIdentifiers (
|
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
|
IN EFI_DISK_IO_PROTOCOL *DiskIo
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 Offset;
|
|
UINT64 EndDiskOffset;
|
|
CDROM_VOLUME_DESCRIPTOR VolDescriptor;
|
|
CDROM_VOLUME_DESCRIPTOR TerminatingVolDescriptor;
|
|
|
|
ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR));
|
|
|
|
//
|
|
// Start Volume Recognition Sequence
|
|
//
|
|
EndDiskOffset = MultU64x32 (BlockIo->Media->LastBlock,
|
|
BlockIo->Media->BlockSize);
|
|
|
|
for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset;
|
|
Offset += UDF_LOGICAL_SECTOR_SIZE) {
|
|
//
|
|
// Check if block device has a Volume Structure Descriptor and an Extended
|
|
// Area.
|
|
//
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
Offset,
|
|
sizeof (CDROM_VOLUME_DESCRIPTOR),
|
|
(VOID *)&VolDescriptor
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
|
|
(VOID *)UDF_BEA_IDENTIFIER,
|
|
sizeof (VolDescriptor.Unknown.Id)) == 0) {
|
|
break;
|
|
}
|
|
|
|
if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
|
|
(VOID *)CDVOL_ID,
|
|
sizeof (VolDescriptor.Unknown.Id)) != 0) ||
|
|
(CompareMem ((VOID *)&VolDescriptor,
|
|
(VOID *)&TerminatingVolDescriptor,
|
|
sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look for "NSR0{2,3}" identifiers in the Extended Area.
|
|
//
|
|
Offset += UDF_LOGICAL_SECTOR_SIZE;
|
|
if (Offset >= EndDiskOffset) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
Offset,
|
|
sizeof (CDROM_VOLUME_DESCRIPTOR),
|
|
(VOID *)&VolDescriptor
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
|
|
(VOID *)UDF_NSR2_IDENTIFIER,
|
|
sizeof (VolDescriptor.Unknown.Id)) != 0) &&
|
|
(CompareMem ((VOID *)VolDescriptor.Unknown.Id,
|
|
(VOID *)UDF_NSR3_IDENTIFIER,
|
|
sizeof (VolDescriptor.Unknown.Id)) != 0)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Look for "TEA01" identifier in the Extended Area
|
|
//
|
|
Offset += UDF_LOGICAL_SECTOR_SIZE;
|
|
if (Offset >= EndDiskOffset) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = DiskIo->ReadDisk (
|
|
DiskIo,
|
|
BlockIo->Media->MediaId,
|
|
Offset,
|
|
sizeof (CDROM_VOLUME_DESCRIPTOR),
|
|
(VOID *)&VolDescriptor
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
|
|
(VOID *)UDF_TEA_IDENTIFIER,
|
|
sizeof (VolDescriptor.Unknown.Id)) != 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Check if Logical Volume Descriptor is supported by current EDK2 UDF file
|
|
system implementation.
|
|
|
|
@param[in] LogicalVolDesc Logical Volume Descriptor pointer.
|
|
|
|
@retval TRUE Logical Volume Descriptor is supported.
|
|
@retval FALSE Logical Volume Descriptor is not supported.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsLogicalVolumeDescriptorSupported (
|
|
UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc
|
|
)
|
|
{
|
|
//
|
|
// Check for a valid UDF revision range
|
|
//
|
|
switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) {
|
|
case 0x0102:
|
|
case 0x0150:
|
|
case 0x0200:
|
|
case 0x0201:
|
|
case 0x0250:
|
|
case 0x0260:
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check for a single Partition Map
|
|
//
|
|
if (LogicalVolDesc->NumberOfPartitionMaps > 1) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// UDF 1.02 revision supports only Type 1 (Physical) partitions, but
|
|
// let's check it any way.
|
|
//
|
|
// PartitionMap[0] -> type
|
|
// PartitionMap[1] -> length (in bytes)
|
|
//
|
|
if (LogicalVolDesc->PartitionMaps[0] != 1 ||
|
|
LogicalVolDesc->PartitionMaps[1] != 6) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Find UDF logical volume location and whether it is supported by current EDK2
|
|
UDF file system implementation.
|
|
|
|
@param[in] BlockIo BlockIo interface.
|
|
@param[in] DiskIo DiskIo interface.
|
|
@param[in] AnchorPoint Anchor volume descriptor pointer.
|
|
@param[in] LastRecordedBlock Last recorded block in media.
|
|
@param[out] MainVdsStartBlock Main VDS starting block number.
|
|
@param[out] MainVdsEndBlock Main VDS ending block number.
|
|
|
|
@retval EFI_SUCCESS UDF logical volume was found.
|
|
@retval EFI_VOLUME_CORRUPTED UDF file system structures are corrupted.
|
|
@retval EFI_UNSUPPORTED UDF logical volume is not supported.
|
|
@retval other Failed to perform disk I/O.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FindLogicalVolumeLocation (
|
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
|
IN EFI_DISK_IO_PROTOCOL *DiskIo,
|
|
IN UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER *AnchorPoint,
|
|
IN EFI_LBA LastRecordedBlock,
|
|
OUT UINT64 *MainVdsStartBlock,
|
|
OUT UINT64 *MainVdsEndBlock
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 BlockSize;
|
|
UDF_EXTENT_AD *ExtentAd;
|
|
UINT64 SeqBlocksNum;
|
|
UINT64 SeqStartBlock;
|
|
UINT64 GuardMainVdsStartBlock;
|
|
VOID *Buffer;
|
|
UINT64 SeqEndBlock;
|
|
BOOLEAN StopSequence;
|
|
UINTN LvdsCount;
|
|
UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc;
|
|
UDF_DESCRIPTOR_TAG *DescriptorTag;
|
|
|
|
BlockSize = BlockIo->Media->BlockSize;
|
|
ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent;
|
|
|
|
//
|
|
// UDF 2.60, 2.2.3.1 struct MainVolumeDescriptorSequenceExtent
|
|
//
|
|
// The Main Volume Descriptor Sequence Extent shall have a minimum length of
|
|
// 16 logical sectors.
|
|
//
|
|
// Also make sure it does not exceed maximum number of blocks in the disk.
|
|
//
|
|
SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize);
|
|
if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastRecordedBlock + 1) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
//
|
|
// Check for valid Volume Descriptor Sequence starting block number
|
|
//
|
|
SeqStartBlock = (UINT64)ExtentAd->ExtentLocation;
|
|
if (SeqStartBlock > LastRecordedBlock ||
|
|
SeqStartBlock + SeqBlocksNum - 1 > LastRecordedBlock) {
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
GuardMainVdsStartBlock = SeqStartBlock;
|
|
|
|
//
|
|
// Allocate buffer for reading disk blocks
|
|
//
|
|
Buffer = AllocateZeroPool ((UINTN)BlockSize);
|
|
if (Buffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
SeqEndBlock = SeqStartBlock + SeqBlocksNum;
|
|
StopSequence = FALSE;
|
|
LvdsCount = 0;
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
//
|
|
// Start Main Volume Descriptor Sequence
|
|
//
|
|
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;
|
|
|
|
//
|
|
// ECMA 167, 8.4.1 Contents of a Volume Descriptor Sequence
|
|
//
|
|
// - A Volume Descriptor Sequence shall contain one or more Primary Volume
|
|
// Descriptors.
|
|
// - A Volume Descriptor Sequence shall contain zero or more Implementation
|
|
// Use Volume Descriptors.
|
|
// - A Volume Descriptor Sequence shall contain zero or more Partition
|
|
// Descriptors.
|
|
// - A Volume Descriptor Sequence shall contain zero or more Logical Volume
|
|
// Descriptors.
|
|
// - A Volume Descriptor Sequence shall contain zero or more Unallocated
|
|
// Space Descriptors.
|
|
//
|
|
switch (DescriptorTag->TagIdentifier) {
|
|
case UdfPrimaryVolumeDescriptor:
|
|
case UdfImplemenationUseVolumeDescriptor:
|
|
case UdfPartitionDescriptor:
|
|
case UdfUnallocatedSpaceDescriptor:
|
|
break;
|
|
|
|
case UdfLogicalVolumeDescriptor:
|
|
LogicalVolDesc = Buffer;
|
|
|
|
//
|
|
// Check for existence of a single LVD and whether it is supported by
|
|
// current EDK2 UDF file system implementation.
|
|
//
|
|
if (++LvdsCount > 1 ||
|
|
!IsLogicalVolumeDescriptorSupported (LogicalVolDesc)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
StopSequence = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
case UdfTerminatingDescriptor:
|
|
//
|
|
// Stop the sequence when we find a Terminating Descriptor
|
|
// (aka Unallocated Sector), se we don't have to walk all the unallocated
|
|
// area unnecessarily.
|
|
//
|
|
StopSequence = TRUE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// An invalid Volume Descriptor has been found in the sequece. Volume is
|
|
// corrupted.
|
|
//
|
|
Status = EFI_VOLUME_CORRUPTED;
|
|
goto Out_Free;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if LVD was found
|
|
//
|
|
if (!EFI_ERROR (Status) && LvdsCount == 1) {
|
|
*MainVdsStartBlock = GuardMainVdsStartBlock;
|
|
//
|
|
// We do not need to read either LVD or PD descriptors to know the last
|
|
// valid block in the found UDF file system. It's already
|
|
// LastRecordedBlock.
|
|
//
|
|
*MainVdsEndBlock = LastRecordedBlock;
|
|
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
Out_Free:
|
|
//
|
|
// Free block read buffer
|
|
//
|
|
FreePool (Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Find a supported UDF file system in block device.
|
|
|
|
@attention This is boundary function that may receive untrusted input.
|
|
@attention The input is from Partition.
|
|
|
|
The CD/DVD media is the external input, so this routine will do basic
|
|
validation for the media.
|
|
|
|
@param[in] BlockIo BlockIo interface.
|
|
@param[in] DiskIo DiskIo interface.
|
|
@param[out] StartingLBA UDF file system starting LBA.
|
|
@param[out] EndingLBA UDF file system starting LBA.
|
|
|
|
@retval EFI_SUCCESS UDF file system was found.
|
|
@retval other UDF file system was not found.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FindUdfFileSystem (
|
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
|
IN EFI_DISK_IO_PROTOCOL *DiskIo,
|
|
OUT EFI_LBA *StartingLBA,
|
|
OUT EFI_LBA *EndingLBA
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER AnchorPoint;
|
|
EFI_LBA LastRecordedBlock;
|
|
|
|
//
|
|
// Find UDF volume identifiers
|
|
//
|
|
Status = FindUdfVolumeIdentifiers (BlockIo, DiskIo);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Find Anchor Volume Descriptor Pointer
|
|
//
|
|
Status = FindAnchorVolumeDescriptorPointer (
|
|
BlockIo,
|
|
DiskIo,
|
|
&AnchorPoint,
|
|
&LastRecordedBlock
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Find Logical Volume location
|
|
//
|
|
Status = FindLogicalVolumeLocation (
|
|
BlockIo,
|
|
DiskIo,
|
|
&AnchorPoint,
|
|
LastRecordedBlock,
|
|
(UINT64 *)StartingLBA,
|
|
(UINT64 *)EndingLBA
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Install child handles if the Handle supports UDF/ECMA-167 volume format.
|
|
|
|
@param[in] This Calling context.
|
|
@param[in] Handle Parent Handle.
|
|
@param[in] DiskIo Parent DiskIo interface.
|
|
@param[in] DiskIo2 Parent DiskIo2 interface.
|
|
@param[in] BlockIo Parent BlockIo interface.
|
|
@param[in] BlockIo2 Parent BlockIo2 interface.
|
|
@param[in] DevicePath Parent Device Path
|
|
|
|
|
|
@retval EFI_SUCCESS Child handle(s) was added.
|
|
@retval EFI_MEDIA_CHANGED Media changed Detected.
|
|
@retval other no child handle was added.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PartitionInstallUdfChildHandles (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DISK_IO_PROTOCOL *DiskIo,
|
|
IN EFI_DISK_IO2_PROTOCOL *DiskIo2,
|
|
IN EFI_BLOCK_IO_PROTOCOL *BlockIo,
|
|
IN EFI_BLOCK_IO2_PROTOCOL *BlockIo2,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
UINT32 RemainderByMediaBlockSize;
|
|
EFI_STATUS Status;
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
EFI_PARTITION_INFO_PROTOCOL PartitionInfo;
|
|
EFI_LBA StartingLBA;
|
|
EFI_LBA EndingLBA;
|
|
BOOLEAN ChildCreated;
|
|
|
|
Media = BlockIo->Media;
|
|
ChildCreated = FALSE;
|
|
|
|
//
|
|
// Check if UDF logical block size is multiple of underlying device block size
|
|
//
|
|
DivU64x32Remainder (
|
|
UDF_LOGICAL_SECTOR_SIZE, // Dividend
|
|
Media->BlockSize, // Divisor
|
|
&RemainderByMediaBlockSize // Remainder
|
|
);
|
|
if (RemainderByMediaBlockSize != 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Detect El Torito feature first.
|
|
// And always continue to search for UDF.
|
|
//
|
|
Status = PartitionInstallElToritoChildHandles (
|
|
This,
|
|
Handle,
|
|
DiskIo,
|
|
DiskIo2,
|
|
BlockIo,
|
|
BlockIo2,
|
|
DevicePath
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "PartitionDxe: El Torito standard found on handle 0x%p.\n", Handle));
|
|
ChildCreated = TRUE;
|
|
}
|
|
|
|
//
|
|
// Search for an UDF file system on block device
|
|
//
|
|
Status = FindUdfFileSystem (BlockIo, DiskIo, &StartingLBA, &EndingLBA);
|
|
if (EFI_ERROR (Status)) {
|
|
return (ChildCreated ? EFI_SUCCESS : EFI_NOT_FOUND);
|
|
}
|
|
|
|
//
|
|
// Create Partition Info protocol for UDF file system
|
|
//
|
|
ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL));
|
|
PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION;
|
|
PartitionInfo.Type = PARTITION_TYPE_OTHER;
|
|
|
|
//
|
|
// Install partition child handle for UDF file system
|
|
//
|
|
Status = PartitionInstallChildHandle (
|
|
This,
|
|
Handle,
|
|
DiskIo,
|
|
DiskIo2,
|
|
BlockIo,
|
|
BlockIo2,
|
|
DevicePath,
|
|
(EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath,
|
|
&PartitionInfo,
|
|
StartingLBA,
|
|
EndingLBA,
|
|
Media->BlockSize,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return (ChildCreated ? EFI_SUCCESS : Status);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|