<commit_before>/** @file
  Handle on-disk format and volume structures in UDF/ECMA-167 file systems.

  Copyright (C) 2014-2017 <NAME> <<EMAIL>>

  This program and the accompanying materials are licensed and made available
  under the terms and conditions of the BSD License which accompanies this
  distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/

#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, so we don't have to double check here.
    //
    // Partition reference number can also be retrieved from
    // LongAd->ExtentLocation.PartitionReferenceNumber, however the spec says
    // it may be 0, so let's not rely on it.
    //
    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.

  @return The logical sector number of a given Long Allocation Descriptor.

**/
UINT64
GetLongAdLsn (
  IN UDF_VOLUME_INFO                 *Volume,
  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd
  )
{
  UDF_PARTITION_DESCRIPTOR *PartitionDesc;

  PartitionDesc = GetPdFromLongAd (Volume, LongAd);
  ASSERT (PartitionDesc != NULL);

  return (UINT64)PartitionDesc->PartitionStartingLocation -
    Volume->MainVdsStartLocation +
    LongAd->ExtentLocation.LogicalBlockNumber;
}

/**
  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;
  Lsn = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse);

  //
  // 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);

  ASSERT (*NewFileIdentifierDesc != NULL);
}

/**
  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);

  ASSERT (*NewFileEntry != NULL);
}

/**
  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.

  @param[in]  FileEntryData       (Extended) File Entry pointer.
  @param[out] Data                Buffer contains the raw data of a given
                                  (Extended) File Entry.
  @param[out] Length              Length of the data in Buffer.

**/
VOID
GetFileEntryData (
  IN   VOID    *FileEntryData,
  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);
  }
}

/**
  Get Allocation Descriptors' data information from a given FE/EFE.

  @param[in]  FileEntryData       (Extended) File Entry pointer.
  @param[out] AdsData             Buffer contains the Allocation Descriptors'
                                  data from a given FE/EFE.
  @param[out] Length              Length of the data in AdsData.

**/
VOID
GetAdsInformation (
  IN   VOID    *FileEntryData,
  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);
  }
}

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

  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.

  @return The logical sector number of the given Allocation Descriptor.

**/
UINT64
GetAllocationDescriptorLsn (
  IN UDF_FE_RECORDING_FLAGS          RecordingFlags,
  IN UDF_VOLUME_INFO                 *Volume,
  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
  IN VOID                            *Ad
  )
{
  if (RecordingFlags == LongAdsSequence) {
    return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad);
  } else if (RecordingFlags == ShortAdsSequence) {
    return GetShortAdLsn (
      Volume,
      GetPdFromLongAd (Volume, ParentIcb),
      (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad
      );
  }

  return 0;
}

/**
  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.<commit_msg>MdeModulePkg/UdfDxe: Resolve potential NULL pointer dereference<commit_after>/** @file
  Handle on-disk format and volume structures in UDF/ECMA-167 file systems.

  Copyright (C) 2014-2017 <NAME> <<EMAIL>>

  This program and the accompanying materials are licensed and made available
  under the terms and conditions of the BSD License which accompanies this
  distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/

#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, so we don't have to double check here.
    //
    // Partition reference number can also be retrieved from
    // LongAd->ExtentLocation.PartitionReferenceNumber, however the spec says
    // it may be 0, so let's not rely on it.
    //
    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.

  @return The logical sector number of a given Long Allocation Descriptor.

**/
UINT64
GetLongAdLsn (
  IN UDF_VOLUME_INFO                 *Volume,
  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd
  )
{
  UDF_PARTITION_DESCRIPTOR *PartitionDesc;

  PartitionDesc = GetPdFromLongAd (Volume, LongAd);
  ASSERT (PartitionDesc != NULL);

  return (UINT64)PartitionDesc->PartitionStartingLocation -
    Volume->MainVdsStartLocation +
    LongAd->ExtentLocation.LogicalBlockNumber;
}

/**
  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;
  Lsn = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse);

  //
  // 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);

  ASSERT (*NewFileIdentifierDesc != NULL);
}

/**
  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);

  ASSERT (*NewFileEntry != NULL);
}

/**
  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.

  @param[in]  FileEntryData       (Extended) File Entry pointer.
  @param[out] Data                Buffer contains the raw data of a given
                                  (Extended) File Entry.
  @param[out] Length              Length of the data in Buffer.

**/
VOID
GetFileEntryData (
  IN   VOID    *FileEntryData,
  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);
  }
}

/**
  Get Allocation Descriptors' data information from a given FE/EFE.

  @param[in]  FileEntryData       (Extended) File Entry pointer.
  @param[out] AdsData             Buffer contains the Allocation Descriptors'
                                  data from a given FE/EFE.
  @param[out] Length              Length of the data in AdsData.

**/
VOID
GetAdsInformation (
  IN   VOID    *FileEntryData,
  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);
  }
}

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

  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.

  @return The logical sector number of the given Allocation Descriptor.

**/
UINT64
GetAllocationDescriptorLsn (
  IN UDF_FE_RECORDING_FLAGS          RecordingFlags,
  IN UDF_VOLUME_INFO                 *Volume,
  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,
  IN VOID                            *Ad
  )
{
  UDF_PARTITION_DESCRIPTOR *PartitionDesc;

  if (RecordingFlags == LongAdsSequence) {
    return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad);
  } else if (RecordingFlags == ShortAdsSequence) {
    PartitionDesc = GetPdFromLongAd (Volume, ParentIcb);
    ASSERT (PartitionDesc != NULL);

    return GetShortAdLsn (
      Volume,
      PartitionDesc,
      (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad
      );
  }

  return 0;
}

/**
  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.
{'fraction_non_alphanumeric': 0.07738655198674013, 'fraction_numerical': 0.00654033956009497, 'mean_word_length': 2.4196537990196076, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 153, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1368712', 'n_tokens_mistral': 14001, 'n_tokens_neox': 13346, 'n_words': 3703}