]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c
MdeModulePkg/PartitionDxe: Add UDF file system support
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / PartitionDxe / Udf.c
diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c b/MdeModulePkg/Universal/Disk/PartitionDxe/Udf.c
new file mode 100644 (file)
index 0000000..c1d4480
--- /dev/null
@@ -0,0 +1,318 @@
+/** @file\r
+  Scan for an UDF file system on a formatted media.\r
+\r
+  Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include "Partition.h"\r
+\r
+//\r
+// C5BD4D42-1A76-4996-8956-73CDA326CD0A\r
+//\r
+#define EFI_UDF_DEVICE_PATH_GUID                        \\r
+  { 0xC5BD4D42, 0x1A76, 0x4996,                         \\r
+    { 0x89, 0x56, 0x73, 0xCD, 0xA3, 0x26, 0xCD, 0x0A }  \\r
+  }\r
+\r
+typedef struct {\r
+  VENDOR_DEVICE_PATH        DevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL  End;\r
+} UDF_DEVICE_PATH;\r
+\r
+//\r
+// Vendor-Defined Media Device Path for UDF file system\r
+//\r
+UDF_DEVICE_PATH gUdfDevicePath = {\r
+  { { MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,\r
+      { sizeof (VENDOR_DEVICE_PATH), 0 } },\r
+    EFI_UDF_DEVICE_PATH_GUID\r
+  },\r
+  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }\r
+  }\r
+};\r
+\r
+EFI_STATUS\r
+FindAnchorVolumeDescriptorPointer (\r
+  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,\r
+  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,\r
+  OUT  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      BlockSize = BlockIo->Media->BlockSize;\r
+  EFI_LBA     EndLBA = BlockIo->Media->LastBlock;\r
+  EFI_LBA     DescriptorLBAs[] = { 256, EndLBA - 256, EndLBA, 512 };\r
+  UINTN       Index;\r
+\r
+  for (Index = 0; Index < ARRAY_SIZE (DescriptorLBAs); Index++) {\r
+    Status = DiskIo->ReadDisk (\r
+      DiskIo,\r
+      BlockIo->Media->MediaId,\r
+      MultU64x32 (DescriptorLBAs[Index], BlockSize),\r
+      sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER),\r
+      (VOID *)AnchorPoint\r
+      );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    //\r
+    // Check if read LBA has a valid AVDP descriptor.\r
+    //\r
+    if (IS_AVDP (AnchorPoint)) {\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+  //\r
+  // No AVDP found.\r
+  //\r
+  return EFI_VOLUME_CORRUPTED;\r
+}\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
+\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
+\r
+**/\r
+EFI_STATUS\r
+SupportUdfFileSystem (\r
+  IN EFI_BLOCK_IO_PROTOCOL  *BlockIo,\r
+  IN EFI_DISK_IO_PROTOCOL   *DiskIo\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  UINT64                                Offset;\r
+  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
+  //\r
+  // Start Volume Recognition Sequence\r
+  //\r
+  EndDiskOffset = MultU64x32 (BlockIo->Media->LastBlock,\r
+                              BlockIo->Media->BlockSize);\r
+\r
+  for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset;\r
+       Offset += UDF_LOGICAL_SECTOR_SIZE) {\r
+    //\r
+    // Check if block device has a Volume Structure Descriptor and an Extended\r
+    // Area.\r
+    //\r
+    Status = DiskIo->ReadDisk (\r
+      DiskIo,\r
+      BlockIo->Media->MediaId,\r
+      Offset,\r
+      sizeof (CDROM_VOLUME_DESCRIPTOR),\r
+      (VOID *)&VolDescriptor\r
+      );\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
+                    (VOID *)UDF_BEA_IDENTIFIER,\r
+                    sizeof (VolDescriptor.Unknown.Id)) == 0) {\r
+      break;\r
+    }\r
+\r
+    if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
+                     (VOID *)CDVOL_ID,\r
+                     sizeof (VolDescriptor.Unknown.Id)) != 0) ||\r
+        (CompareMem ((VOID *)&VolDescriptor,\r
+                     (VOID *)&TerminatingVolDescriptor,\r
+                     sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Look for "NSR0{2,3}" identifiers in the Extended Area.\r
+  //\r
+  Offset += UDF_LOGICAL_SECTOR_SIZE;\r
+  if (Offset >= EndDiskOffset) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = DiskIo->ReadDisk (\r
+    DiskIo,\r
+    BlockIo->Media->MediaId,\r
+    Offset,\r
+    sizeof (CDROM_VOLUME_DESCRIPTOR),\r
+    (VOID *)&VolDescriptor\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
+                   (VOID *)UDF_NSR2_IDENTIFIER,\r
+                   sizeof (VolDescriptor.Unknown.Id)) != 0) &&\r
+      (CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
+                   (VOID *)UDF_NSR3_IDENTIFIER,\r
+                   sizeof (VolDescriptor.Unknown.Id)) != 0)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Look for "TEA01" identifier in the Extended Area\r
+  //\r
+  Offset += UDF_LOGICAL_SECTOR_SIZE;\r
+  if (Offset >= EndDiskOffset) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = DiskIo->ReadDisk (\r
+    DiskIo,\r
+    BlockIo->Media->MediaId,\r
+    Offset,\r
+    sizeof (CDROM_VOLUME_DESCRIPTOR),\r
+    (VOID *)&VolDescriptor\r
+    );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,\r
+                  (VOID *)UDF_TEA_IDENTIFIER,\r
+                  sizeof (VolDescriptor.Unknown.Id)) != 0) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = FindAnchorVolumeDescriptorPointer (BlockIo, DiskIo, &AnchorPoint);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Install child handles if the Handle supports UDF/ECMA-167 volume format.\r
+\r
+  @param[in]  This        Calling context.\r
+  @param[in]  Handle      Parent Handle.\r
+  @param[in]  DiskIo      Parent DiskIo interface.\r
+  @param[in]  DiskIo2     Parent DiskIo2 interface.\r
+  @param[in]  BlockIo     Parent BlockIo interface.\r
+  @param[in]  BlockIo2    Parent BlockIo2 interface.\r
+  @param[in]  DevicePath  Parent Device Path\r
+\r
+\r
+  @retval EFI_SUCCESS         Child handle(s) was added.\r
+  @retval EFI_MEDIA_CHANGED   Media changed Detected.\r
+  @retval other               no child handle was added.\r
+\r
+**/\r
+EFI_STATUS\r
+PartitionInstallUdfChildHandles (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN  EFI_HANDLE                   Handle,\r
+  IN  EFI_DISK_IO_PROTOCOL         *DiskIo,\r
+  IN  EFI_DISK_IO2_PROTOCOL        *DiskIo2,\r
+  IN  EFI_BLOCK_IO_PROTOCOL        *BlockIo,\r
+  IN  EFI_BLOCK_IO2_PROTOCOL       *BlockIo2,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_BLOCK_IO_MEDIA           *Media;\r
+  EFI_DEVICE_PATH_PROTOCOL     *DevicePathNode;\r
+  EFI_GUID                     *VendorDefinedGuid;\r
+  EFI_GUID                     UdfDevPathGuid = EFI_UDF_DEVICE_PATH_GUID;\r
+  EFI_PARTITION_INFO_PROTOCOL  PartitionInfo;\r
+\r
+  Media = BlockIo->Media;\r
+\r
+  //\r
+  // Check if UDF logical block size is multiple of underlying device block size\r
+  //\r
+  if ((UDF_LOGICAL_SECTOR_SIZE % Media->BlockSize) != 0 ||\r
+      Media->BlockSize > UDF_LOGICAL_SECTOR_SIZE) {\r
+    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, &UdfDevPathGuid)) {\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
+  //\r
+  Status = SupportUdfFileSystem (BlockIo, DiskIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Create Partition Info protocol for UDF file system\r
+  //\r
+  ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL));\r
+  PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION;\r
+  PartitionInfo.Type = PARTITION_TYPE_OTHER;\r
+\r
+  //\r
+  // Install partition child handle for UDF file system\r
+  //\r
+  Status = PartitionInstallChildHandle (\r
+    This,\r
+    Handle,\r
+    DiskIo,\r
+    DiskIo2,\r
+    BlockIo,\r
+    BlockIo2,\r
+    DevicePath,\r
+    (EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath,\r
+    &PartitionInfo,\r
+    0,\r
+    Media->LastBlock,\r
+    Media->BlockSize\r
+    );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = EFI_NOT_FOUND;\r
+  }\r
+\r
+  return Status;\r
+}\r