summaryrefslogtreecommitdiffstats
path: root/gptsync/gptsync.c
diff options
context:
space:
mode:
Diffstat (limited to 'gptsync/gptsync.c')
-rw-r--r--gptsync/gptsync.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/gptsync/gptsync.c b/gptsync/gptsync.c
new file mode 100644
index 000000000..bd224cbb4
--- /dev/null
+++ b/gptsync/gptsync.c
@@ -0,0 +1,695 @@
+/*
+ * gptsync/gptsync.c
+ * Platform-independent code for syncing GPT and MBR
+ *
+ * Copyright (c) 2006 Christoph Pfisterer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Christoph Pfisterer nor the names of the
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gptsync.h"
+
+#include "syslinux_mbr.h"
+
+// types
+
+typedef struct {
+ UINT8 flags;
+ UINT8 start_chs[3];
+ UINT8 type;
+ UINT8 end_chs[3];
+ UINT32 start_lba;
+ UINT32 size;
+} MBR_PARTITION_INFO;
+
+typedef struct {
+ UINT8 type;
+ CHARN *name;
+} MBR_PARTTYPE;
+
+typedef struct {
+ UINT64 signature;
+ UINT32 spec_revision;
+ UINT32 header_size;
+ UINT32 header_crc32;
+ UINT32 reserved;
+ UINT64 header_lba;
+ UINT64 alternate_header_lba;
+ UINT64 first_usable_lba;
+ UINT64 last_usable_lba;
+ UINT8 disk_guid[16];
+ UINT64 entry_lba;
+ UINT32 entry_count;
+ UINT32 entry_size;
+ UINT32 entry_crc32;
+} GPT_HEADER;
+
+typedef struct {
+ UINT8 type_guid[16];
+ UINT8 partition_guid[16];
+ UINT64 start_lba;
+ UINT64 end_lba;
+ UINT64 attributes;
+ CHAR16 name[36];
+} GPT_ENTRY;
+
+#define GPT_KIND_SYSTEM (0)
+#define GPT_KIND_DATA (1)
+#define GPT_KIND_BASIC_DATA (2)
+#define GPT_KIND_FATAL (3)
+
+typedef struct {
+ UINT8 guid[16];
+ UINT8 mbr_type;
+ CHARN *name;
+ UINTN kind;
+} GPT_PARTTYPE;
+
+typedef struct {
+ UINTN index;
+ UINT64 start_lba;
+ UINT64 end_lba;
+ UINTN mbr_type;
+ UINT8 gpt_type[16];
+ GPT_PARTTYPE *gpt_parttype;
+ BOOLEAN active;
+} PARTITION_INFO;
+
+// variables
+
+UINT8 empty_guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+PARTITION_INFO mbr_parts[4];
+UINTN mbr_part_count = 0;
+PARTITION_INFO gpt_parts[128];
+UINTN gpt_part_count = 0;
+
+PARTITION_INFO new_mbr_parts[4];
+UINTN new_mbr_part_count = 0;
+
+UINT8 sector[512];
+
+MBR_PARTTYPE mbr_types[] = {
+ { 0x01, STR("FAT12") },
+ { 0x04, STR("FAT16 <32M") },
+ { 0x05, STR("DOS Extended") },
+ { 0x06, STR("FAT16") },
+ { 0x07, STR("NTFS") },
+ { 0x0b, STR("FAT32") },
+ { 0x0c, STR("FAT32 (LBA)") },
+ { 0x0e, STR("FAT16 (LBA)") },
+ { 0x0f, STR("Win95 Extended (LBA)") },
+ { 0x11, STR("Hidden FAT12") },
+ { 0x14, STR("Hidden FAT16 <32M") },
+ { 0x16, STR("Hidden FAT16") },
+ { 0x17, STR("Hidden NTFS") },
+ { 0x1b, STR("Hidden FAT32") },
+ { 0x1c, STR("Hidden FAT32 (LBA)") },
+ { 0x1e, STR("Hidden FAT16 (LBA)") },
+ { 0x82, STR("Linux swap / Solaris") },
+ { 0x83, STR("Linux") },
+ { 0x85, STR("Linux Extended") },
+ { 0x86, STR("NTFS volume set") },
+ { 0x87, STR("NTFS volume set") },
+ { 0x8e, STR("Linux LVM") },
+ { 0xa5, STR("FreeBSD") },
+ { 0xa6, STR("OpenBSD") },
+ { 0xa7, STR("NeXTSTEP") },
+ { 0xa9, STR("NetBSD") },
+ { 0xaf, STR("Mac OS X HFS+") },
+ { 0xeb, STR("BeOS") },
+ { 0xee, STR("EFI Protective") },
+ { 0xef, STR("EFI System (FAT)") },
+ { 0xfd, STR("Linux RAID") },
+ { 0, NULL },
+};
+
+GPT_PARTTYPE gpt_types[] = {
+ { "\x28\x73\x2A\xC1\x1F\xF8\xD2\x11\xBA\x4B\x00\xA0\xC9\x3E\xC9\x3B", 0xef, STR("EFI System (FAT)"), GPT_KIND_SYSTEM },
+ { "\x41\xEE\x4D\x02\xE7\x33\xD3\x11\x9D\x69\x00\x08\xC7\x81\xF3\x9F", 0x00, STR("MBR partition scheme"), GPT_KIND_FATAL },
+ { "\x16\xE3\xC9\xE3\x5C\x0B\xB8\x4D\x81\x7D\xF9\x2D\xF0\x02\x15\xAE", 0x00, STR("MS Reserved"), GPT_KIND_SYSTEM },
+ { "\xA2\xA0\xD0\xEB\xE5\xB9\x33\x44\x87\xC0\x68\xB6\xB7\x26\x99\xC7", 0x00, STR("Basic Data"), GPT_KIND_BASIC_DATA },
+ { "\xAA\xC8\x08\x58\x8F\x7E\xE0\x42\x85\xD2\xE1\xE9\x04\x34\xCF\xB3", 0x00, STR("MS LDM Metadata"), GPT_KIND_FATAL },
+ { "\xA0\x60\x9B\xAF\x31\x14\x62\x4F\xBC\x68\x33\x11\x71\x4A\x69\xAD", 0x00, STR("MS LDM Data"), GPT_KIND_FATAL },
+ { "\x0F\x88\x9D\xA1\xFC\x05\x3B\x4D\xA0\x06\x74\x3F\x0F\x84\x91\x1E", 0xfd, STR("Linux RAID"), GPT_KIND_DATA },
+ { "\x6D\xFD\x57\x06\xAB\xA4\xC4\x43\x84\xE5\x09\x33\xC8\x4B\x4F\x4F", 0x82, STR("Linux Swap"), GPT_KIND_DATA },
+ { "\x79\xD3\xD6\xE6\x07\xF5\xC2\x44\xA2\x3C\x23\x8F\x2A\x3D\xF9\x28", 0x8e, STR("Linux LVM"), GPT_KIND_DATA },
+ { "\x39\x33\xA6\x8D\x07\x00\xC0\x60\xC4\x36\x08\x3A\xC8\x23\x09\x08", 0x00, STR("Linux Reserved"), GPT_KIND_SYSTEM },
+ { "\x00\x53\x46\x48\x00\x00\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0xaf, STR("Mac OS X HFS+"), GPT_KIND_DATA },
+ { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, 0, NULL, 0 },
+};
+GPT_PARTTYPE gpt_dummy_type =
+ { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, 0, STR("Unknown"), GPT_KIND_FATAL };
+
+//
+// MBR functions
+//
+
+static CHARN * mbr_parttype_name(UINT8 type)
+{
+ int i;
+
+ for (i = 0; mbr_types[i].name; i++)
+ if (mbr_types[i].type == type)
+ return mbr_types[i].name;
+ return STR("Unknown");
+}
+
+static UINTN read_mbr(VOID)
+{
+ UINTN status;
+ UINTN i;
+ BOOLEAN used;
+ MBR_PARTITION_INFO *table;
+
+ Print(L"\nCurrent MBR partition table:\n");
+
+ // read MBR data
+ status = read_sector(0, sector);
+ if (status != 0)
+ return status;
+
+ // check for validity
+ if (*((UINT16 *)(sector + 510)) != 0xaa55) {
+ Print(L" No MBR partition table present!\n");
+ return 1;
+ }
+ table = (MBR_PARTITION_INFO *)(sector + 446);
+ for (i = 0; i < 4; i++) {
+ if (table[i].flags != 0x00 && table[i].flags != 0x80) {
+ Print(L" MBR partition table is invalid!\n");
+ return 1;
+ }
+ }
+
+ // check if used
+ used = FALSE;
+ for (i = 0; i < 4; i++) {
+ if (table[i].start_lba > 0 && table[i].size > 0) {
+ used = TRUE;
+ break;
+ }
+ }
+ if (!used) {
+ Print(L" No partitions defined\n");
+ return 0;
+ }
+
+ // dump current state & fill internal structures
+ Print(L" # A Start LBA End LBA Type\n");
+ for (i = 0; i < 4; i++) {
+ if (table[i].start_lba == 0 || table[i].size == 0)
+ continue;
+
+ mbr_parts[mbr_part_count].index = i;
+ mbr_parts[mbr_part_count].start_lba = (UINT64)table[i].start_lba;
+ mbr_parts[mbr_part_count].end_lba = (UINT64)table[i].start_lba + (UINT64)table[i].size - 1;
+ mbr_parts[mbr_part_count].mbr_type = table[i].type;
+ mbr_parts[mbr_part_count].active = (table[i].flags == 0x80) ? TRUE : FALSE;
+
+ Print(L" %d %s %12lld %12lld %02x %s\n",
+ mbr_parts[mbr_part_count].index + 1,
+ mbr_parts[mbr_part_count].active ? STR("*") : STR(" "),
+ mbr_parts[mbr_part_count].start_lba,
+ mbr_parts[mbr_part_count].end_lba,
+ mbr_parts[mbr_part_count].mbr_type,
+ mbr_parttype_name(mbr_parts[mbr_part_count].mbr_type));
+
+ mbr_part_count++;
+ }
+
+ return 0;
+}
+
+static UINTN check_mbr(VOID)
+{
+ UINTN i, k;
+
+ // check each entry
+ for (i = 0; i < mbr_part_count; i++) {
+ // check for overlap
+ for (k = 0; k < mbr_part_count; k++) {
+ if (k != i && !(mbr_parts[i].start_lba > mbr_parts[k].end_lba || mbr_parts[k].start_lba > mbr_parts[i].end_lba)) {
+ Print(L"Status: MBR partition table is invalid, partitions overlap.\n");
+ return 1;
+ }
+ }
+
+ // check for extended partitions
+ if (mbr_parts[i].mbr_type == 0x05 || mbr_parts[i].mbr_type == 0x0f || mbr_parts[i].mbr_type == 0x85) {
+ Print(L"Status: Extended partition found in MBR table, will not touch this disk.\n",
+ gpt_parts[i].gpt_parttype->name);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static UINTN write_mbr(VOID)
+{
+ UINTN status;
+ UINTN i, k;
+ UINT8 active;
+ UINT64 lba;
+ MBR_PARTITION_INFO *table;
+ BOOLEAN have_bootcode;
+
+ Print(L"\nWriting new MBR...\n");
+
+ // read MBR data
+ status = read_sector(0, sector);
+ if (status != 0)
+ return status;
+
+ // write partition table
+ *((UINT16 *)(sector + 510)) = 0xaa55;
+
+ table = (MBR_PARTITION_INFO *)(sector + 446);
+ active = 0x80;
+ for (i = 0; i < 4; i++) {
+ for (k = 0; k < new_mbr_part_count; k++) {
+ if (new_mbr_parts[k].index == i)
+ break;
+ }
+ if (k >= new_mbr_part_count) {
+ // unused entry
+ table[i].flags = 0;
+ table[i].start_chs[0] = 0;
+ table[i].start_chs[1] = 0;
+ table[i].start_chs[2] = 0;
+ table[i].type = 0;
+ table[i].end_chs[0] = 0;
+ table[i].end_chs[1] = 0;
+ table[i].end_chs[2] = 0;
+ table[i].start_lba = 0;
+ table[i].size = 0;
+ } else {
+ if (new_mbr_parts[k].active) {
+ table[i].flags = active;
+ active = 0x00;
+ } else
+ table[i].flags = 0x00;
+ table[i].start_chs[0] = 0xfe;
+ table[i].start_chs[1] = 0xff;
+ table[i].start_chs[2] = 0xff;
+ table[i].type = new_mbr_parts[k].mbr_type;
+ table[i].end_chs[0] = 0xfe;
+ table[i].end_chs[1] = 0xff;
+ table[i].end_chs[2] = 0xff;
+
+ lba = new_mbr_parts[k].start_lba;
+ if (lba > 0xffffffffULL) {
+ Print(L"Warning: Partition %d starts beyond 2 TiB limit\n", i+1);
+ lba = 0xffffffffULL;
+ }
+ table[i].start_lba = (UINT32)lba;
+
+ lba = new_mbr_parts[k].end_lba + 1 - new_mbr_parts[k].start_lba;
+ if (lba > 0xffffffffULL) {
+ Print(L"Warning: Partition %d extends beyond 2 TiB limit\n", i+1);
+ lba = 0xffffffffULL;
+ }
+ table[i].size = (UINT32)lba;
+ }
+ }
+
+ // add boot code if necessary
+ have_bootcode = FALSE;
+ for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
+ if (sector[i] != 0) {
+ have_bootcode = TRUE;
+ break;
+ }
+ }
+ if (!have_bootcode) {
+ // no boot code found in the MBR, add the syslinux MBR code
+ SetMem(sector, MBR_BOOTCODE_SIZE, 0);
+ CopyMem(sector, syslinux_mbr, SYSLINUX_MBR_SIZE);
+ }
+
+ // write MBR data
+ status = write_sector(0, sector);
+ if (status != 0)
+ return status;
+
+ Print(L"MBR updated successfully!\n");
+
+ return 0;
+}
+
+//
+// GPT functions
+//
+
+static GPT_PARTTYPE * gpt_parttype(UINT8 *type_guid)
+{
+ int i;
+
+ for (i = 0; gpt_types[i].name; i++)
+ if (guids_are_equal(gpt_types[i].guid, type_guid))
+ return &(gpt_types[i]);
+ return &gpt_dummy_type;
+}
+
+static UINTN read_gpt(VOID)
+{
+ UINTN status;
+ GPT_HEADER *header;
+ GPT_ENTRY *entry;
+ UINT64 entry_lba;
+ UINTN entry_count, entry_size, i;
+
+ Print(L"\nCurrent GPT partition table:\n");
+
+ // read GPT header
+ status = read_sector(1, sector);
+ if (status != 0)
+ return status;
+
+ // check signature
+ header = (GPT_HEADER *)sector;
+ if (header->signature != 0x5452415020494645ULL) {
+ Print(L" No GPT partition table present!\n");
+ return 0;
+ }
+ if (header->spec_revision != 0x00010000UL) {
+ Print(L" Warning: Unknown GPT spec revision 0x%08x\n", header->spec_revision);
+ }
+ if ((512 % header->entry_size) > 0 || header->entry_size > 512) {
+ Print(L" Error: Invalid GPT entry size (misaligned or more than 512 bytes)\n");
+ return 0;
+ }
+
+ // read entries
+ entry_lba = header->entry_lba;
+ entry_size = header->entry_size;
+ entry_count = header->entry_count;
+
+ Print(L" # Start LBA End LBA Type\n");
+ for (i = 0; i < entry_count; i++) {
+ if (((i * entry_size) % 512) == 0) {
+ status = read_sector(entry_lba, sector);
+ if (status != 0)
+ return status;
+ entry_lba++;
+ }
+ entry = (GPT_ENTRY *)(sector + ((i * entry_size) % 512));
+
+ if (guids_are_equal(entry->type_guid, empty_guid))
+ continue;
+
+ gpt_parts[gpt_part_count].index = i;
+ gpt_parts[gpt_part_count].start_lba = entry->start_lba;
+ gpt_parts[gpt_part_count].end_lba = entry->end_lba;
+ gpt_parts[gpt_part_count].mbr_type = 0;
+ copy_guid(gpt_parts[gpt_part_count].gpt_type, entry->type_guid);
+ gpt_parts[gpt_part_count].gpt_parttype = gpt_parttype(gpt_parts[gpt_part_count].gpt_type);
+ gpt_parts[gpt_part_count].active = FALSE;
+
+ Print(L" %d %12lld %12lld %s\n",
+ gpt_parts[gpt_part_count].index + 1,
+ gpt_parts[gpt_part_count].start_lba,
+ gpt_parts[gpt_part_count].end_lba,
+ gpt_parts[gpt_part_count].gpt_parttype->name);
+
+ gpt_part_count++;
+ }
+
+ return 0;
+}
+
+static UINTN check_gpt(VOID)
+{
+ UINTN i, k;
+ BOOLEAN found_data_parts;
+
+ if (gpt_part_count == 0) {
+ Print(L"Status: No GPT partition table, no need to sync.\n");
+ return 1;
+ }
+
+ // check each entry
+ found_data_parts = FALSE;
+ for (i = 0; i < gpt_part_count; i++) {
+ // check sanity
+ if (gpt_parts[i].end_lba < gpt_parts[i].start_lba) {
+ Print(L"Status: GPT partition table is invalid.\n");
+ return 1;
+ }
+ // check for overlap
+ for (k = 0; k < gpt_part_count; k++) {
+ if (k != i && !(gpt_parts[i].start_lba > gpt_parts[k].end_lba || gpt_parts[k].start_lba > gpt_parts[i].end_lba)) {
+ Print(L"Status: GPT partition table is invalid, partitions overlap.\n");
+ return 1;
+ }
+ }
+
+ // check for partitions kind
+ if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_FATAL) {
+ Print(L"Status: GPT partition of type '%s' found, will not touch this disk.\n",
+ gpt_parts[i].gpt_parttype->name);
+ return 1;
+ }
+ if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_DATA ||
+ gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA)
+ found_data_parts = TRUE;
+ }
+
+ if (!found_data_parts) {
+ Print(L"Status: GPT partition table has no data partitions, no need to sync.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+//
+// compare GPT and MBR tables
+//
+
+#define ACTION_NONE (0)
+#define ACTION_NOP (1)
+#define ACTION_REWRITE (2)
+
+static UINTN analyze(VOID)
+{
+ UINTN action = ACTION_NONE;
+ UINTN i, k, iter;
+ UINT64 min_start_lba;
+ BOOLEAN have_active;
+
+ new_mbr_part_count = 0;
+
+ // check for common scenarios
+ if (mbr_part_count == 0) {
+ // current MBR is empty
+ action = ACTION_REWRITE;
+ } else if (mbr_part_count == 1 && mbr_parts[0].mbr_type == 0xee) {
+ // MBR has just the EFI Protective partition (i.e. untouched)
+ action = ACTION_REWRITE;
+ }
+ if (action == ACTION_NONE && mbr_part_count > 0) {
+ if (mbr_parts[0].mbr_type == 0xee &&
+ gpt_parts[0].gpt_parttype->mbr_type == 0xef &&
+ mbr_parts[0].start_lba == 1 &&
+ mbr_parts[0].end_lba == gpt_parts[0].end_lba) {
+ // The Apple Way, "EFI Protective" covering the tables and the ESP
+ action = ACTION_NOP;
+ if ((mbr_part_count != gpt_part_count && gpt_part_count <= 4) ||
+ (mbr_part_count != 4 && gpt_part_count > 4)) {
+ // number of partitions has changed
+ action = ACTION_REWRITE;
+ } else {
+ // check partition ranges and types
+ for (i = 1; i < mbr_part_count; i++) {
+ if (mbr_parts[i].start_lba != gpt_parts[i].start_lba ||
+ mbr_parts[i].end_lba != gpt_parts[i].end_lba ||
+ (gpt_parts[i].gpt_parttype->mbr_type && mbr_parts[i].mbr_type != gpt_parts[i].gpt_parttype->mbr_type))
+ // position or type has changed
+ action = ACTION_REWRITE;
+ }
+ }
+ }
+ }
+ if (action == ACTION_NONE && mbr_part_count > 0 && mbr_parts[0].mbr_type == 0xef) {
+ // The XOM Way, all partitions mirrored 1:1
+ action = ACTION_REWRITE;
+ // check partition ranges and types
+ for (i = 0; i < mbr_part_count; i++) {
+ if (mbr_parts[i].start_lba != gpt_parts[i].start_lba ||
+ mbr_parts[i].end_lba != gpt_parts[i].end_lba ||
+ (gpt_parts[i].gpt_parttype->mbr_type && mbr_parts[i].mbr_type != gpt_parts[i].gpt_parttype->mbr_type))
+ // position or type has changed -> better don't touch
+ action = ACTION_NONE;
+ }
+ }
+
+ if (action == ACTION_NOP) {
+ Print(L"Status: Tables are synchronized, no need to sync.\n");
+ return 0;
+ } else if (action == ACTION_REWRITE) {
+ Print(L"Status: MBR table must be updated.\n");
+ } else {
+ Print(L"Status: Analysis inconclusive, will not touch this disk.\n");
+ return 1;
+ }
+
+ // generate the new table
+
+ // first entry: EFI Protective
+ new_mbr_parts[0].index = 0;
+ new_mbr_parts[0].start_lba = 1;
+ new_mbr_parts[0].mbr_type = 0xee;
+ new_mbr_part_count = 1;
+
+ if (gpt_parts[0].gpt_parttype->mbr_type == 0xef) {
+ new_mbr_parts[0].end_lba = gpt_parts[0].end_lba;
+ i = 1;
+ } else {
+ min_start_lba = gpt_parts[0].start_lba;
+ for (k = 0; k < gpt_part_count; k++) {
+ if (min_start_lba > gpt_parts[k].start_lba)
+ min_start_lba = gpt_parts[k].start_lba;
+ }
+ new_mbr_parts[0].end_lba = min_start_lba - 1;
+ i = 0;
+ }
+
+ // add other GPT partitions until the table is full
+ for (; i < gpt_part_count && new_mbr_part_count < 4; i++) {
+ new_mbr_parts[new_mbr_part_count].index = new_mbr_part_count;
+ new_mbr_parts[new_mbr_part_count].start_lba = gpt_parts[i].start_lba;
+ new_mbr_parts[new_mbr_part_count].end_lba = gpt_parts[i].end_lba;
+ new_mbr_parts[new_mbr_part_count].mbr_type = gpt_parts[i].gpt_parttype->mbr_type;
+ new_mbr_parts[new_mbr_part_count].active = FALSE;
+
+ // find matching partition in the old MBR table
+ for (k = 0; k < mbr_part_count; k++) {
+ if (mbr_parts[k].start_lba == gpt_parts[i].start_lba) {
+ if (new_mbr_parts[new_mbr_part_count].mbr_type == 0)
+ new_mbr_parts[new_mbr_part_count].mbr_type = mbr_parts[k].mbr_type;
+ new_mbr_parts[new_mbr_part_count].active = mbr_parts[k].active;
+ break;
+ }
+ }
+
+ if (new_mbr_parts[new_mbr_part_count].mbr_type == 0) {
+ // TODO: detect the actual file system on the partition
+
+ // fallback: use linux native
+ //if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) {
+ new_mbr_parts[new_mbr_part_count].mbr_type = 0x83;
+ }
+
+ new_mbr_part_count++;
+ }
+
+ // if no partition is active, pick one
+ for (iter = 0; iter < 3; iter++) {
+ // check
+ have_active = FALSE;
+ for (i = 0; i < new_mbr_part_count; i++)
+ if (new_mbr_parts[i].active)
+ have_active = TRUE;
+ if (have_active)
+ break;
+
+ // set active on the first matching partition
+ for (i = 0; i < new_mbr_part_count; i++) {
+ if ((iter >= 0 && (new_mbr_parts[i].mbr_type == 0x07 ||
+ new_mbr_parts[i].mbr_type == 0x0b ||
+ new_mbr_parts[i].mbr_type == 0x0c)) ||
+ (iter >= 1 && (new_mbr_parts[i].mbr_type == 0x83)) ||
+ (iter >= 2 && i > 0)) {
+ new_mbr_parts[i].active = TRUE;
+ break;
+ }
+ }
+ }
+
+ // dump table
+ Print(L"\nProposed new MBR partition table:\n");
+ Print(L" # A Start LBA End LBA Type\n");
+ for (i = 0; i < new_mbr_part_count; i++) {
+ Print(L" %d %s %12lld %12lld %02x %s\n",
+ new_mbr_parts[i].index + 1,
+ new_mbr_parts[i].active ? STR("*") : STR(" "),
+ new_mbr_parts[i].start_lba,
+ new_mbr_parts[i].end_lba,
+ new_mbr_parts[i].mbr_type,
+ mbr_parttype_name(new_mbr_parts[i].mbr_type));
+ }
+
+ return 0;
+}
+
+//
+// sync algorithm entry point
+//
+
+UINTN gptsync(VOID)
+{
+ UINTN status = 0;
+ UINTN status_gpt, status_mbr;
+ // BOOLEAN proceed = FALSE;
+
+ // get full information from disk
+ status_gpt = read_gpt();
+ status_mbr = read_mbr();
+ if (status_gpt != 0 || status_mbr != 0)
+ return (status_gpt || status_mbr);
+
+ // cross-check current situation
+ Print(L"\n");
+ status = check_gpt(); // check GPT for consistency
+ if (status != 0)
+ return status;
+ status = check_mbr(); // check MBR for consistency
+ if (status != 0)
+ return status;
+ status = analyze(); // analyze the situation
+ if (status != 0)
+ return status;
+ if (new_mbr_part_count == 0)
+ return status;
+
+ // offer user the choice what to do
+ // status = input_boolean(STR("\nMay I update the MBR as printed above? [y/N] "), &proceed);
+ // if (status != 0 || proceed != TRUE)
+ // return status;
+
+ // adjust the MBR and write it back
+ status = write_mbr();
+ if (status != 0)
+ return status;
+
+ return status;
+}