From 22be792bfd8e4a4da7495ec60084c23dd8a43677 Mon Sep 17 00:00:00 2001
From: huangkaibin <huangkaibin@huawei.com>
Date: Fri, 25 Jan 2019 19:46:52 +0000
Subject: [PATCH] add-support-of-gpt_sync_mbr

Hybrid MBR support is support by adding a new type of 'gpt_sync_mbr' for mklabel command.
This feature is migrated from the path of "parted-gpt-mbr-sync.patch" of SUSE 12 SP1.

Compilation Error:
gpt.c:1345:50: error: 'GPTPartitionData' {aka 'struct _GPTPartitionData'}
has no member named 'boot'
This code has been refactored.'boot' is removed.
So we should call ped_partition_get_flag to instead.
Signed-off-by: Hongtao Zhang <zhanghongtao22@huawei.com>
---
 libparted/labels/gpt.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 242 insertions(+), 4 deletions(-)

diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c
index 4f922b2..b2dad71 100644
--- a/libparted/labels/gpt.c
+++ b/libparted/labels/gpt.c
@@ -306,6 +306,7 @@ typedef struct _GPTPartitionData
 } GPTPartitionData;
 
 static PedDiskType gpt_disk_type;
+static PedDiskType gpt_sync_mbr_disk_type;
 
 static inline uint32_t
 pth_get_size (const PedDevice *dev)
@@ -474,8 +475,54 @@ _pmbr_is_valid (const LegacyMBR_t *mbr)
   return 0;
 }
 
+/* checks if device has a hybrid protective MBR partition table
+ *
+ * If the 1st partition has type 0xEE and starts at sector 1,
+ * we consider it 'normal' GPT.
+ * Otherwise it is synced GPT with hybridized pMBR.
+ *
+ * Note: Testing whether it spans entire disk causes an
+ * issue when fixing a GPT after disk resize (see bnc#946641)
+ * - hence this check was removed ~ condition relaxed.
+ *
+ * Ultimate solution: get rid of the gpt_sync_mbr hack ASAP
+ */
+static inline int
+_has_hybrid_pmbr (const PedDevice *dev)
+{
+  int has_hybrid_pmbr = 1;
+
+  PED_ASSERT (dev != NULL);
+
+  void *label;
+  if (!ptt_read_sector (dev, 0, &label))
+    return 0;
+
+  LegacyMBR_t mbr;
+  memcpy(&mbr, label, sizeof(mbr));
+
+  if (mbr.Signature != PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE)) {
+    free(label);
+    return 0;
+  }
+
+  uint32_t efi_gpt_expected_size;
+  if ((dev->length - 1ULL) > 0xFFFFFFFFULL)
+    efi_gpt_expected_size = 0xFFFFFFFF;
+  else
+    efi_gpt_expected_size = dev->length - 1UL;
+
+  if ((mbr.PartitionRecord[0].OSType == EFI_PMBR_OSTYPE_EFI) &&
+      (mbr.PartitionRecord[0].StartingLBA == PED_CPU_TO_LE32(1)))
+        /* pMBR is not hybrid */
+        has_hybrid_pmbr = 0;
+
+  free(label);
+  return has_hybrid_pmbr;
+}
+
 static int
-gpt_probe (const PedDevice *dev)
+_gpt_probe_generic(const PedDevice *dev)
 {
   int gpt_sig_found = 0;
 
@@ -509,6 +556,19 @@ gpt_probe (const PedDevice *dev)
   return gpt_sig_found;
 }
 
+static int
+gpt_probe (const PedDevice *dev)
+{
+  return _gpt_probe_generic(dev) && !_has_hybrid_pmbr(dev);
+}
+
+
+static int
+gpt_sync_mbr_probe (const PedDevice *dev)
+{
+  return _gpt_probe_generic(dev) && _has_hybrid_pmbr(dev);
+}
+
 static PedDisk *
 gpt_alloc (const PedDevice *dev)
 {
@@ -554,6 +614,36 @@ error:
 }
 
 static PedDisk *
+gpt_sync_mbr_alloc (const PedDevice *dev)
+{
+  PedDisk *disk;
+  GPTDiskData *gpt_disk_data;
+  PedSector data_start, data_end;
+
+  disk = _ped_disk_alloc ((PedDevice *) dev, &gpt_sync_mbr_disk_type);
+  if (!disk)
+    goto error;
+  disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData));
+  if (!disk->disk_specific)
+    goto error_free_disk;
+
+  data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+  data_end = dev->length - 2
+    - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+  ped_geometry_init (&gpt_disk_data->data_area, dev, data_start,
+                     data_end - data_start + 1);
+  gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES;
+  uuid_generate ((unsigned char *) &gpt_disk_data->uuid);
+  swap_uuid_and_efi_guid ((unsigned char *) (&gpt_disk_data->uuid));
+  return disk;
+
+error_free_disk:
+  free (disk);
+error:
+  return NULL;
+}
+
+static PedDisk *
 gpt_duplicate (const PedDisk *disk)
 {
   PedDisk *new_disk;
@@ -967,7 +1057,7 @@ gpt_read (PedDisk *disk)
 
   /* motivation: let the user decide about the pmbr... during
      ped_disk_probe(), they probably didn't get a choice... */
-  if (!gpt_probe (disk->dev))
+  if (!gpt_probe (disk->dev) && !gpt_sync_mbr_probe(disk->dev))
     goto error;
 
   GuidPartitionTableHeader_t *gpt = NULL;
@@ -1124,11 +1214,61 @@ error:
   return 0;
 }
 
+static inline unsigned char
+_part_to_ostype (PedPartition* part)
+{
+    if (part->fs_type) {
+        if (strncmp (part->fs_type->name, "fat", 3) == 0) return 0xc;
+        if (strncmp (part->fs_type->name, "ntfs", 4) == 0) return 0x7;
+        if (strncmp (part->fs_type->name, "hfs", 3) == 0) return 0xaf;
+        if (strstr (part->fs_type->name, "swap")) return 0x82;
+    }
+#ifdef __PPC__
+    if (part->num == 1) return 0x41; /* PARTITION_PREP */
+#endif
+    return 0x83; // Everything else is Linux
+}
+
+static inline PedPartition*
+_find_first_part (const PedDisk* disk)
+{
+    PedPartition *retval = NULL, *part = NULL;
+    uint64_t lowest_end = 0xffffffffffffffff;
+    while (part = ped_disk_next_partition (disk, part)) {
+        if (part->geom.start == 0 || part->type == PED_PARTITION_METADATA
+                || part->type == PED_PARTITION_FREESPACE)
+            continue;
+        if (part->geom.end < lowest_end) {
+            retval = part;
+            lowest_end = part->geom.end;
+        }
+    }
+    return retval;
+}
+
+static inline uint32_t
+_part_32bitmax (uint64_t in)
+{
+    if (in > 0xFFFFFFFFULL)
+        return 0xFFFFFFFF;
+    else
+        return (uint32_t)in;
+}
+
+
 #ifndef DISCOVER_ONLY
 /* Write the protective MBR (to keep DOS happy) */
 static int
-_write_pmbr (PedDevice *dev, bool pmbr_boot)
+_write_pmbr (const PedDisk *disk, bool pmbr_boot)
 {
+  PedDevice * dev = disk->dev;
+
+  /* need sync GPT -> hybrid pMBR ? */
+  int sync_pmbr = !strcmp(disk->type->name, "gpt_sync_mbr") ? 1 : 0;
+
+  int i, pmbr_id, first_entry = 0, last_entry = 3;
+  PedPartition *part = NULL, *esp;
+
   /* The UEFI spec is not clear about what to do with the following
      elements of the Protective MBR (pmbr): BootCode (0-440B),
      UniqueMBRSignature (440B-444B) and Unknown (444B-446B).
@@ -1142,6 +1282,8 @@ _write_pmbr (PedDevice *dev, bool pmbr_boot)
   memset (pmbr->PartitionRecord, 0, sizeof pmbr->PartitionRecord);
 
   pmbr->Signature = PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE);
+
+  if (!sync_pmbr) {
   pmbr->PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI;
   pmbr->PartitionRecord[0].StartSector = 2;
   pmbr->PartitionRecord[0].EndHead = 0xFF;
@@ -1154,6 +1296,60 @@ _write_pmbr (PedDevice *dev, bool pmbr_boot)
     pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (dev->length - 1UL);
   if (pmbr_boot)
     pmbr->PartitionRecord[0].BootIndicator = 0x80;
+  } else {
+  /* Search for an EFI System Partition */
+  esp = _find_first_part(disk);
+  if (!esp || !esp->fs_type || strncmp (esp->fs_type->name, "fat", 3) != 0)
+      esp = NULL;
+
+  pmbr_id = 3;
+  if (esp) {
+      pmbr_id = 0;
+      first_entry = 1;
+      last_entry = 4;
+  }
+
+  /* Write a pseudo-PMBR so Linux is happy */
+  pmbr->PartitionRecord[pmbr_id].OSType      = EFI_PMBR_OSTYPE_EFI;
+  pmbr->PartitionRecord[pmbr_id].StartSector = 1;
+  pmbr->PartitionRecord[pmbr_id].EndHead     = 0xFF;
+  pmbr->PartitionRecord[pmbr_id].EndSector   = 0xFF;
+  pmbr->PartitionRecord[pmbr_id].EndTrack    = 0xFF;
+  pmbr->PartitionRecord[pmbr_id].StartingLBA = PED_CPU_TO_LE32(1);
+  pmbr->PartitionRecord[pmbr_id].SizeInLBA = PED_CPU_TO_LE32 (1);
+  if (esp)
+      pmbr->PartitionRecord[pmbr_id].SizeInLBA = PED_CPU_TO_LE32 (esp->geom.end - 1);
+
+  /* sync the first 3 GPT entries to MBR primary partitions */
+  for (i=first_entry; i < last_entry; i++) {
+      part = ped_disk_next_partition (disk, part);
+      if (part == NULL)
+          break;
+      /* we might get a starting garbage partition */
+      if (part->geom.start == 0 || part->type == PED_PARTITION_METADATA || part->type == PED_PARTITION_FREESPACE || part == esp) {
+          i--;
+          continue;
+      }
+
+      /* partition can not be represented by dos label - don't sync it */
+      if (part->geom.start > 0xFFFFFFFF ||
+          (part->geom.end - part->geom.start + 1) > 0xFFFFFFFF) {
+          continue;
+      }
+
+      pmbr->PartitionRecord[i].OSType      = _part_to_ostype(part);
+      pmbr->PartitionRecord[i].StartHead     = 0xFE;
+      pmbr->PartitionRecord[i].StartSector   = 0xFF;
+      pmbr->PartitionRecord[i].StartTrack    = 0xFF;
+      pmbr->PartitionRecord[i].EndHead     = 0xFE;
+      pmbr->PartitionRecord[i].EndSector   = 0xFF;
+      pmbr->PartitionRecord[i].EndTrack    = 0xFF;
+      pmbr->PartitionRecord[i].StartingLBA = PED_CPU_TO_LE32 (_part_32bitmax(part->geom.start));
+      if(ped_partition_get_flag(part, PED_PARTITION_BOOT))
+          pmbr->PartitionRecord[i].BootIndicator = 0x80;
+      pmbr->PartitionRecord[i].SizeInLBA = PED_CPU_TO_LE32 (_part_32bitmax(part->geom.end - part->geom.start + 1));
+  }
+  }
 
   int write_ok = ped_device_write (dev, pmbr, GPT_PMBR_LBA,
                                    GPT_PMBR_SECTORS);
@@ -1273,7 +1469,7 @@ gpt_write (const PedDisk *disk)
   ptes_crc = efi_crc32 (ptes, ptes_bytes);
 
   /* Write protective MBR */
-  if (!_write_pmbr (disk->dev, gpt_disk_data->pmbr_boot))
+  if (!_write_pmbr (disk, gpt_disk_data->pmbr_boot))
     goto error_free_ptes;
 
   /* Write PTH and PTEs */
@@ -2070,6 +2266,38 @@ static PedDiskOps gpt_disk_ops =
   PT_op_function_initializers (gpt)
 };
 
+static PedDiskOps gpt_sync_mbr_disk_ops =
+{
+  clobber:                      NULL,
+  write:                        NULL_IF_DISCOVER_ONLY (gpt_write),
+
+  partition_set_name:           gpt_partition_set_name,
+  partition_get_name:           gpt_partition_get_name,
+
+  /* probe function redefined */
+  probe:                             gpt_sync_mbr_probe,
+  /* alloc function redefined */
+  alloc:                             gpt_sync_mbr_alloc,
+  duplicate:                         gpt_duplicate,
+  free:                              gpt_free,
+  read:                              gpt_read,
+  partition_new:                     gpt_partition_new,
+  partition_duplicate:               gpt_partition_duplicate,
+  partition_set_flag:                gpt_partition_set_flag,
+  partition_get_flag:                gpt_partition_get_flag,
+  partition_set_system:              gpt_partition_set_system,
+  partition_is_flag_available:       gpt_partition_is_flag_available,
+  partition_align:                   gpt_partition_align,
+  partition_destroy:                 gpt_partition_destroy,
+  partition_enumerate:               gpt_partition_enumerate,
+  alloc_metadata:                    gpt_alloc_metadata,
+  get_max_primary_partition_count:   gpt_get_max_primary_partition_count,
+  get_max_supported_partition_count: gpt_get_max_supported_partition_count,
+  partition_check:                   gpt_partition_check,
+  max_length:                        gpt_partition_max_length,
+  max_start_sector:                  gpt_partition_max_start_sector
+};
+
 static PedDiskType gpt_disk_type =
 {
   next:		NULL,
@@ -2078,16 +2306,26 @@ static PedDiskType gpt_disk_type =
   features:	PED_DISK_TYPE_PARTITION_NAME
 };
 
+static PedDiskType gpt_sync_mbr_disk_type =
+{
+  next:     NULL,
+  name:     "gpt_sync_mbr",
+  ops:      &gpt_sync_mbr_disk_ops,
+  features: PED_DISK_TYPE_PARTITION_NAME
+};
+
 void
 ped_disk_gpt_init ()
 {
   ped_disk_type_register (&gpt_disk_type);
+  ped_disk_type_register (&gpt_sync_mbr_disk_type);
 }
 
 void
 ped_disk_gpt_done ()
 {
   ped_disk_type_unregister (&gpt_disk_type);
+  ped_disk_type_unregister (&gpt_sync_mbr_disk_type);
 }
 
 verify (sizeof (GuidPartitionEntryAttributes_t) == 8);
-- 
1.8.3.1

