https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
		
			
				
	
	
		
			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;
 | 
						|
}
 |