MdeModulePkg/UDF: Fix creation of UDF logical partition
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / PartitionDxe / Udf.c
index 609f56cef69315a4edd016f7c954d510f8695860..8aee30c759e3b4cea9cb17534b9f8a86f5fe84f5 100644 (file)
@@ -64,11 +64,12 @@ FindAnchorVolumeDescriptorPointer (
   OUT  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  UINT32      BlockSize;\r
-  EFI_LBA     EndLBA;\r
-  EFI_LBA     DescriptorLBAs[4];\r
-  UINTN       Index;\r
+  EFI_STATUS          Status;\r
+  UINT32              BlockSize;\r
+  EFI_LBA             EndLBA;\r
+  EFI_LBA             DescriptorLBAs[4];\r
+  UINTN               Index;\r
+  UDF_DESCRIPTOR_TAG  *DescriptorTag;\r
 \r
   BlockSize = BlockIo->Media->BlockSize;\r
   EndLBA = BlockIo->Media->LastBlock;\r
@@ -88,10 +89,13 @@ FindAnchorVolumeDescriptorPointer (
     if (EFI_ERROR (Status)) {\r
       return Status;\r
     }\r
+\r
+    DescriptorTag = &AnchorPoint->DescriptorTag;\r
+\r
     //\r
     // Check if read LBA has a valid AVDP descriptor.\r
     //\r
-    if (IS_AVDP (AnchorPoint)) {\r
+    if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {\r
       return EFI_SUCCESS;\r
     }\r
   }\r
@@ -102,23 +106,18 @@ FindAnchorVolumeDescriptorPointer (
 }\r
 \r
 /**\r
-  Check if block device supports a valid UDF file system as specified by OSTA\r
-  Universal Disk Format Specification 2.60.\r
+  Find UDF volume identifiers in a Volume Recognition Sequence.\r
 \r
-  @param[in]   BlockIo  BlockIo interface.\r
-  @param[in]   DiskIo   DiskIo interface.\r
+  @param[in]  BlockIo             BlockIo interface.\r
+  @param[in]  DiskIo              DiskIo interface.\r
 \r
-  @retval EFI_SUCCESS          UDF file system found.\r
-  @retval EFI_UNSUPPORTED      UDF file system not found.\r
-  @retval EFI_NO_MEDIA         The device has no media.\r
-  @retval EFI_DEVICE_ERROR     The device reported an error.\r
-  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
-  @retval EFI_OUT_OF_RESOURCES The scan was not successful due to lack of\r
-                               resources.\r
+  @retval EFI_SUCCESS             UDF volume identifiers were found.\r
+  @retval EFI_NOT_FOUND           UDF volume identifiers were not found.\r
+  @retval other                   Failed to perform disk I/O.\r
 \r
 **/\r
 EFI_STATUS\r
-SupportUdfFileSystem (\r
+FindUdfVolumeIdentifiers (\r
   IN EFI_BLOCK_IO_PROTOCOL  *BlockIo,\r
   IN EFI_DISK_IO_PROTOCOL   *DiskIo\r
   )\r
@@ -128,7 +127,6 @@ SupportUdfFileSystem (
   UINT64                                EndDiskOffset;\r
   CDROM_VOLUME_DESCRIPTOR               VolDescriptor;\r
   CDROM_VOLUME_DESCRIPTOR               TerminatingVolDescriptor;\r
-  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  AnchorPoint;\r
 \r
   ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR));\r
 \r
@@ -167,7 +165,7 @@ SupportUdfFileSystem (
         (CompareMem ((VOID *)&VolDescriptor,\r
                      (VOID *)&TerminatingVolDescriptor,\r
                      sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) {\r
-      return EFI_UNSUPPORTED;\r
+      return EFI_NOT_FOUND;\r
     }\r
   }\r
 \r
@@ -176,7 +174,7 @@ SupportUdfFileSystem (
   //\r
   Offset += UDF_LOGICAL_SECTOR_SIZE;\r
   if (Offset >= EndDiskOffset) {\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_NOT_FOUND;\r
   }\r
 \r
   Status = DiskIo->ReadDisk (\r
@@ -196,7 +194,7 @@ SupportUdfFileSystem (
       (CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
                    (VOID *)UDF_NSR3_IDENTIFIER,\r
                    sizeof (VolDescriptor.Unknown.Id)) != 0)) {\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_NOT_FOUND;\r
   }\r
 \r
   //\r
@@ -204,7 +202,7 @@ SupportUdfFileSystem (
   //\r
   Offset += UDF_LOGICAL_SECTOR_SIZE;\r
   if (Offset >= EndDiskOffset) {\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_NOT_FOUND;\r
   }\r
 \r
   Status = DiskIo->ReadDisk (\r
@@ -221,15 +219,291 @@ SupportUdfFileSystem (
   if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
                   (VOID *)UDF_TEA_IDENTIFIER,\r
                   sizeof (VolDescriptor.Unknown.Id)) != 0) {\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check if Logical Volume Descriptor is supported by current EDK2 UDF file\r
+  system implementation.\r
+\r
+  @param[in]  LogicalVolDesc  Logical Volume Descriptor pointer.\r
+\r
+  @retval TRUE                Logical Volume Descriptor is supported.\r
+  @retval FALSE               Logical Volume Descriptor is not supported.\r
+\r
+**/\r
+BOOLEAN\r
+IsLogicalVolumeDescriptorSupported (\r
+  UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc\r
+  )\r
+{\r
+  //\r
+  // Check for a valid UDF revision range\r
+  //\r
+  switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) {\r
+  case 0x0102:\r
+  case 0x0150:\r
+  case 0x0200:\r
+  case 0x0201:\r
+  case 0x0250:\r
+  case 0x0260:\r
+    break;\r
+  default:\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check for a single Partition Map\r
+  //\r
+  if (LogicalVolDesc->NumberOfPartitionMaps > 1) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // UDF 1.02 revision supports only Type 1 (Physical) partitions, but\r
+  // let's check it any way.\r
+  //\r
+  // PartitionMap[0] -> type\r
+  // PartitionMap[1] -> length (in bytes)\r
+  //\r
+  if (LogicalVolDesc->PartitionMaps[0] != 1 ||\r
+      LogicalVolDesc->PartitionMaps[1] != 6) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Find UDF logical volume location and whether it is supported by current EDK2\r
+  UDF file system implementation.\r
+\r
+  @param[in]  BlockIo             BlockIo interface.\r
+  @param[in]  DiskIo              DiskIo interface.\r
+  @param[in]  AnchorPoint         Anchor volume descriptor pointer.\r
+  @param[out] MainVdsStartBlock   Main VDS starting block number.\r
+  @param[out] MainVdsEndBlock     Main VDS ending block number.\r
+\r
+  @retval EFI_SUCCESS             UDF logical volume was found.\r
+  @retval EFI_VOLUME_CORRUPTED    UDF file system structures are corrupted.\r
+  @retval EFI_UNSUPPORTED         UDF logical volume is not supported.\r
+  @retval other                   Failed to perform disk I/O.\r
+\r
+**/\r
+EFI_STATUS\r
+FindLogicalVolumeLocation (\r
+  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,\r
+  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,\r
+  IN   UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint,\r
+  OUT  UINT64                                *MainVdsStartBlock,\r
+  OUT  UINT64                                *MainVdsEndBlock\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  UINT32                         BlockSize;\r
+  EFI_LBA                        LastBlock;\r
+  UDF_EXTENT_AD                  *ExtentAd;\r
+  UINT64                         SeqBlocksNum;\r
+  UINT64                         SeqStartBlock;\r
+  UINT64                         GuardMainVdsStartBlock;\r
+  VOID                           *Buffer;\r
+  UINT64                         SeqEndBlock;\r
+  BOOLEAN                        StopSequence;\r
+  UINTN                          LvdsCount;\r
+  UDF_LOGICAL_VOLUME_DESCRIPTOR  *LogicalVolDesc;\r
+  UDF_DESCRIPTOR_TAG             *DescriptorTag;\r
+\r
+  BlockSize = BlockIo->Media->BlockSize;\r
+  LastBlock = BlockIo->Media->LastBlock;\r
+  ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent;\r
+\r
+  //\r
+  // UDF 2.60, 2.2.3.1 struct MainVolumeDescriptorSequenceExtent\r
+  //\r
+  // The Main Volume Descriptor Sequence Extent shall have a minimum length of\r
+  // 16 logical sectors.\r
+  //\r
+  // Also make sure it does not exceed maximum number of blocks in the disk.\r
+  //\r
+  SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize);\r
+  if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastBlock + 1) {\r
+    return EFI_VOLUME_CORRUPTED;\r
+  }\r
+\r
+  //\r
+  // Check for valid Volume Descriptor Sequence starting block number\r
+  //\r
+  SeqStartBlock = (UINT64)ExtentAd->ExtentLocation;\r
+  if (SeqStartBlock > LastBlock ||\r
+      SeqStartBlock + SeqBlocksNum - 1 > LastBlock) {\r
+    return EFI_VOLUME_CORRUPTED;\r
+  }\r
+\r
+  GuardMainVdsStartBlock = SeqStartBlock;\r
+\r
+  //\r
+  // Allocate buffer for reading disk blocks\r
+  //\r
+  Buffer = AllocateZeroPool ((UINTN)BlockSize);\r
+  if (Buffer == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  SeqEndBlock = SeqStartBlock + SeqBlocksNum;\r
+  StopSequence = FALSE;\r
+  LvdsCount = 0;\r
+  Status = EFI_VOLUME_CORRUPTED;\r
+  //\r
+  // Start Main Volume Descriptor Sequence\r
+  //\r
+  for (; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) {\r
+    //\r
+    // Read disk block\r
+    //\r
+    Status = BlockIo->ReadBlocks (\r
+      BlockIo,\r
+      BlockIo->Media->MediaId,\r
+      SeqStartBlock,\r
+      BlockSize,\r
+      Buffer\r
+      );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Out_Free;\r
+    }\r
+\r
+    DescriptorTag = Buffer;\r
+\r
+    //\r
+    // ECMA 167, 8.4.1 Contents of a Volume Descriptor Sequence\r
+    //\r
+    // - A Volume Descriptor Sequence shall contain one or more Primary Volume\r
+    //   Descriptors.\r
+    // - A Volume Descriptor Sequence shall contain zero or more Implementation\r
+    //   Use Volume Descriptors.\r
+    // - A Volume Descriptor Sequence shall contain zero or more Partition\r
+    //   Descriptors.\r
+    // - A Volume Descriptor Sequence shall contain zero or more Logical Volume\r
+    //   Descriptors.\r
+    // - A Volume Descriptor Sequence shall contain zero or more Unallocated\r
+    //   Space Descriptors.\r
+    //\r
+    switch (DescriptorTag->TagIdentifier) {\r
+    case UdfPrimaryVolumeDescriptor:\r
+    case UdfImplemenationUseVolumeDescriptor:\r
+    case UdfPartitionDescriptor:\r
+    case UdfUnallocatedSpaceDescriptor:\r
+      break;\r
+\r
+    case UdfLogicalVolumeDescriptor:\r
+      LogicalVolDesc = Buffer;\r
+\r
+      //\r
+      // Check for existence of a single LVD and whether it is supported by\r
+      // current EDK2 UDF file system implementation.\r
+      //\r
+      if (++LvdsCount > 1 ||\r
+          !IsLogicalVolumeDescriptorSupported (LogicalVolDesc)) {\r
+        Status = EFI_UNSUPPORTED;\r
+        StopSequence = TRUE;\r
+      }\r
+\r
+      break;\r
+\r
+    case UdfTerminatingDescriptor:\r
+      //\r
+      // Stop the sequence when we find a Terminating Descriptor\r
+      // (aka Unallocated Sector), se we don't have to walk all the unallocated\r
+      // area unnecessarily.\r
+      //\r
+      StopSequence = TRUE;\r
+      break;\r
+\r
+    default:\r
+      //\r
+      // An invalid Volume Descriptor has been found in the sequece. Volume is\r
+      // corrupted.\r
+      //\r
+      Status = EFI_VOLUME_CORRUPTED;\r
+      goto Out_Free;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check if LVD was found\r
+  //\r
+  if (!EFI_ERROR (Status) && LvdsCount == 1) {\r
+    *MainVdsStartBlock = GuardMainVdsStartBlock;\r
+    //\r
+    // We do not need to read either LVD or PD descriptors to know the last\r
+    // valid block in the found UDF file system. It's already LastBlock.\r
+    //\r
+    *MainVdsEndBlock = LastBlock;\r
+\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+Out_Free:\r
+  //\r
+  // Free block read buffer\r
+  //\r
+  FreePool (Buffer);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Find a supported UDF file system in block device.\r
+\r
+  @param[in]  BlockIo             BlockIo interface.\r
+  @param[in]  DiskIo              DiskIo interface.\r
+  @param[out] StartingLBA         UDF file system starting LBA.\r
+  @param[out] EndingLBA           UDF file system starting LBA.\r
+\r
+  @retval EFI_SUCCESS             UDF file system was found.\r
+  @retval other                   UDF file system was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+FindUdfFileSystem (\r
+  IN EFI_BLOCK_IO_PROTOCOL  *BlockIo,\r
+  IN EFI_DISK_IO_PROTOCOL   *DiskIo,\r
+  OUT EFI_LBA               *StartingLBA,\r
+  OUT EFI_LBA               *EndingLBA\r
+  )\r
+{\r
+  EFI_STATUS Status;\r
+  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  AnchorPoint;\r
+\r
+  //\r
+  // Find UDF volume identifiers\r
+  //\r
+  Status = FindUdfVolumeIdentifiers (BlockIo, DiskIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
+  //\r
+  // Find Anchor Volume Descriptor Pointer\r
+  //\r
   Status = FindAnchorVolumeDescriptorPointer (BlockIo, DiskIo, &AnchorPoint);\r
   if (EFI_ERROR (Status)) {\r
-    return EFI_UNSUPPORTED;\r
+    return Status;\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  //\r
+  // Find Logical Volume location\r
+  //\r
+  Status = FindLogicalVolumeLocation (\r
+    BlockIo,\r
+    DiskIo,\r
+    &AnchorPoint,\r
+    (UINT64 *)StartingLBA,\r
+    (UINT64 *)EndingLBA\r
+    );\r
+\r
+  return Status;\r
 }\r
 \r
 /**\r
@@ -263,9 +537,9 @@ PartitionInstallUdfChildHandles (
   UINT32                       RemainderByMediaBlockSize;\r
   EFI_STATUS                   Status;\r
   EFI_BLOCK_IO_MEDIA           *Media;\r
-  EFI_DEVICE_PATH_PROTOCOL     *DevicePathNode;\r
-  EFI_GUID                     *VendorDefinedGuid;\r
   EFI_PARTITION_INFO_PROTOCOL  PartitionInfo;\r
+  EFI_LBA                      StartingLBA;\r
+  EFI_LBA                      EndingLBA;\r
 \r
   Media = BlockIo->Media;\r
 \r
@@ -281,35 +555,10 @@ PartitionInstallUdfChildHandles (
     return EFI_NOT_FOUND;\r
   }\r
 \r
-  DevicePathNode = DevicePath;\r
-  while (!IsDevicePathEnd (DevicePathNode)) {\r
-    //\r
-    // Do not allow checking for UDF file systems in CDROM "El Torito"\r
-    // partitions, and skip duplicate installation of UDF file system child\r
-    // nodes.\r
-    //\r
-    if (DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH) {\r
-      if (DevicePathSubType (DevicePathNode) == MEDIA_CDROM_DP) {\r
-        return EFI_NOT_FOUND;\r
-      }\r
-      if (DevicePathSubType (DevicePathNode) == MEDIA_VENDOR_DP) {\r
-        VendorDefinedGuid = (EFI_GUID *)((UINTN)DevicePathNode +\r
-                                         OFFSET_OF (VENDOR_DEVICE_PATH, Guid));\r
-        if (CompareGuid (VendorDefinedGuid, &gUdfDevPathGuid)) {\r
-          return EFI_NOT_FOUND;\r
-        }\r
-      }\r
-    }\r
-    //\r
-    // Try next device path node\r
-    //\r
-    DevicePathNode = NextDevicePathNode (DevicePathNode);\r
-  }\r
-\r
   //\r
-  // Check if block device supports an UDF file system\r
+  // Search for an UDF file system on block device\r
   //\r
-  Status = SupportUdfFileSystem (BlockIo, DiskIo);\r
+  Status = FindUdfFileSystem (BlockIo, DiskIo, &StartingLBA, &EndingLBA);\r
   if (EFI_ERROR (Status)) {\r
     return EFI_NOT_FOUND;\r
   }\r
@@ -334,13 +583,10 @@ PartitionInstallUdfChildHandles (
     DevicePath,\r
     (EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath,\r
     &PartitionInfo,\r
-    0,\r
-    Media->LastBlock,\r
+    StartingLBA,\r
+    EndingLBA,\r
     Media->BlockSize\r
     );\r
-  if (!EFI_ERROR (Status)) {\r
-    Status = EFI_NOT_FOUND;\r
-  }\r
 \r
   return Status;\r
 }\r