[Ovirt-devel] [PATCH node] import gptsync

Alan Pevec apevec at redhat.com
Sat Nov 29 11:57:16 UTC 2008


>From http://git.fedorahosted.org/git/?p=anaconda.git;a=tree;f=gptsync
Needed to enable legacy boot from the local storage with GPT label.
License is GPL compatible - BSD License (no advertising).
---
 Makefile.am            |    2 +-
 configure.ac           |    1 +
 gptsync/.gitignore     |    4 +
 gptsync/Makefile.am    |   20 ++
 gptsync/README.gptsync |   41 +++++
 gptsync/gptsync.c      |  470 ++++++++++++++++++++++++++++++++++++++++++++++++
 gptsync/gptsync.h      |  219 ++++++++++++++++++++++
 gptsync/lib.c          |  469 +++++++++++++++++++++++++++++++++++++++++++++++
 gptsync/os_unix.c      |  267 +++++++++++++++++++++++++++
 gptsync/showpart.c     |  257 ++++++++++++++++++++++++++
 gptsync/syslinux_mbr.h |   90 +++++++++
 ovirt-node.spec.in     |    7 +
 12 files changed, 1846 insertions(+), 1 deletions(-)
 create mode 100644 gptsync/.gitignore
 create mode 100644 gptsync/Makefile.am
 create mode 100644 gptsync/README.gptsync
 create mode 100644 gptsync/gptsync.c
 create mode 100644 gptsync/gptsync.h
 create mode 100644 gptsync/lib.c
 create mode 100644 gptsync/os_unix.c
 create mode 100644 gptsync/showpart.c
 create mode 100644 gptsync/syslinux_mbr.h

diff --git a/Makefile.am b/Makefile.am
index ea49b43..9b54ae6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,7 +16,7 @@
 # also available at http://www.gnu.org/copyleft/gpl.html.
 
 OVIRT_CACHE_DIR	?= $(HOME)/ovirt-cache
-SUBDIRS = ovirt-identify-node ovirt-listen-awake
+SUBDIRS = ovirt-identify-node ovirt-listen-awake gptsync
 
 EXTRA_DIST =			\
   .gitignore			\
diff --git a/configure.ac b/configure.ac
index bf66b05..7ba459a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,6 +15,7 @@ test x"$ac_ct_CC:$CFLAGS" = 'xgcc:-g -O2' \
 AC_CONFIG_FILES([Makefile
   ovirt-identify-node/Makefile
   ovirt-listen-awake/Makefile
+  gptsync/Makefile
   ovirt-node.spec
   ])
 AC_OUTPUT
diff --git a/gptsync/.gitignore b/gptsync/.gitignore
new file mode 100644
index 0000000..2f54301
--- /dev/null
+++ b/gptsync/.gitignore
@@ -0,0 +1,4 @@
+*.o
+.deps
+gptsync
+showpart
diff --git a/gptsync/Makefile.am b/gptsync/Makefile.am
new file mode 100644
index 0000000..f4ff1c2
--- /dev/null
+++ b/gptsync/Makefile.am
@@ -0,0 +1,20 @@
+bin_PROGRAMS = gptsync showpart
+
+EXTRA_DIST = README.gptsync
+
+gptsync_SOURCES = \
+  gptsync.h \
+  syslinux_mbr.h \
+  gptsync.c \
+  lib.c \
+  os_unix.c
+
+showpart_SOURCES = \
+  gptsync.h \
+  showpart.c \
+  lib.c \
+  os_unix.c
+
+gptsync_CPPFLAGS = $(AM_CPPFLAGS) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DPROGNAME=gptsync
+showpart_CPPFLAGS = $(AM_CPPFLAGS) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DPROGNAME=showpart
+
diff --git a/gptsync/README.gptsync b/gptsync/README.gptsync
new file mode 100644
index 0000000..cb306bd
--- /dev/null
+++ b/gptsync/README.gptsync
@@ -0,0 +1,41 @@
+gptsync is from refit (refit.sf.net).  It has been modified to
+1) Not prompt if you want to copy
+2) Default to Linux native (0x83) instead of fat32 partition id
+
+The original license follows.
+
+
+ rEFIt License
+===============
+
+Copyright (c) 2006-2007 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.
+
diff --git a/gptsync/gptsync.c b/gptsync/gptsync.c
new file mode 100644
index 0000000..7074bb8
--- /dev/null
+++ b/gptsync/gptsync.c
@@ -0,0 +1,470 @@
+/*
+ * gptsync/gptsync.c
+ * Platform-independent code for syncing GPT and MBR
+ *
+ * Copyright (c) 2006-2007 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"
+
+//
+// MBR functions
+//
+
+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 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;
+    UINTN   i, k, iter, count_active, detected_parttype;
+    CHARN   *fsname;
+    UINT64  min_start_lba;
+    UINTN   status;
+    BOOLEAN have_esp;
+    
+    new_mbr_part_count = 0;
+    
+    // determine correct MBR types for GPT partitions
+    if (gpt_part_count == 0) {
+        Print(L"Status: No GPT partitions defined, nothing to sync.\n");
+        return 0;
+    }
+    have_esp = FALSE;
+    for (i = 0; i < gpt_part_count; i++) {
+        gpt_parts[i].mbr_type = gpt_parts[i].gpt_parttype->mbr_type;
+        if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) {
+            // Basic Data: need to look at data in the partition
+            status = detect_mbrtype_fs(gpt_parts[i].start_lba, &detected_parttype, &fsname);
+            if (detected_parttype)
+                gpt_parts[i].mbr_type = detected_parttype;
+            else
+                gpt_parts[i].mbr_type = 0x0b;  // fallback: FAT32
+        } else if (gpt_parts[i].mbr_type == 0xef) {
+            // EFI System Partition: GNU parted can put this on any partition,
+            // need to detect file systems
+            status = detect_mbrtype_fs(gpt_parts[i].start_lba, &detected_parttype, &fsname);
+            if (!have_esp && (detected_parttype == 0x01 || detected_parttype == 0x0e || detected_parttype == 0x0c))
+                ;  // seems to be a legitimate ESP, don't change
+            else if (detected_parttype)
+                gpt_parts[i].mbr_type = detected_parttype;
+            else if (have_esp)    // make sure there's no more than one ESP per disk
+                gpt_parts[i].mbr_type = 0x83;  // fallback: Linux
+        }
+        // NOTE: mbr_type may still be 0 if content detection fails for exotic GPT types or file systems
+        
+        if (gpt_parts[i].mbr_type == 0xef)
+            have_esp = TRUE;
+    }
+    
+    // check for common scenarios
+    action = ACTION_NONE;
+    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].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].mbr_type && mbr_parts[i].mbr_type != gpt_parts[i].mbr_type))
+                        // position or type has changed
+                        action = ACTION_REWRITE;
+                }
+            }
+            // check number of active partitions
+            count_active = 0;
+            for (i = 0; i < mbr_part_count; i++)
+                if (mbr_parts[i].active)
+                    count_active++;
+            if (count_active!= 1)
+                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].mbr_type && mbr_parts[i].mbr_type != gpt_parts[i].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].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
+    // TODO: in the future, prioritize partitions by kind
+    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].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) {
+                // keep type if not detected
+                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;
+                // keep active flag
+                new_mbr_parts[new_mbr_part_count].active = mbr_parts[k].active;
+                break;
+            }
+        }
+        
+        if (new_mbr_parts[new_mbr_part_count].mbr_type == 0)
+            // final fallback: set to a (hopefully) unused type
+            new_mbr_parts[new_mbr_part_count].mbr_type = 0xc0;
+        
+        new_mbr_part_count++;
+    }
+    
+    // make sure there's exactly one active partition
+    for (iter = 0; iter < 3; iter++) {
+        // check
+        count_active = 0;
+        for (i = 0; i < new_mbr_part_count; i++)
+            if (new_mbr_parts[i].active)
+                count_active++;
+        if (count_active == 1)
+            break;
+        
+        // set active on the first matching partition
+        if (count_active == 0) {
+            for (i = 0; i < new_mbr_part_count; i++) {
+                if ((iter >= 0 && (new_mbr_parts[i].mbr_type == 0x07 ||    // NTFS
+                                   new_mbr_parts[i].mbr_type == 0x0b ||    // FAT32
+                                   new_mbr_parts[i].mbr_type == 0x0c)) ||  // FAT32 (LBA)
+                    (iter >= 1 && (new_mbr_parts[i].mbr_type == 0x83)) ||  // Linux
+                    (iter >= 2 && i > 0)) {
+                    new_mbr_parts[i].active = TRUE;
+                    break;
+                }
+            }
+        } else if (count_active > 1 && iter == 0) {
+            // too many active partitions, try deactivating the ESP / EFI Protective entry
+            if ((new_mbr_parts[0].mbr_type == 0xee || new_mbr_parts[0].mbr_type == 0xef) &&
+                new_mbr_parts[0].active) {
+                new_mbr_parts[0].active = FALSE;
+            }
+        } else if (count_active > 1 && iter > 0) {
+            // too many active partitions, deactivate all but the first one
+            count_active = 0;
+            for (i = 0; i < new_mbr_part_count; i++)
+                if (new_mbr_parts[i].active) {
+                    if (count_active > 0)
+                        new_mbr_parts[i].active = FALSE;
+                    count_active++;
+                }
+        }
+    }
+    
+    // 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 & compose new MBR table
+    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;
+}
diff --git a/gptsync/gptsync.h b/gptsync/gptsync.h
new file mode 100644
index 0000000..d1bf3c2
--- /dev/null
+++ b/gptsync/gptsync.h
@@ -0,0 +1,219 @@
+/*
+ * gptsync/gptsync.h
+ * Common header for gptsync and showpart
+ *
+ * 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.
+ */
+
+//
+// config
+//
+
+#if defined(EFI32) || defined(EFIX64)
+#define CONFIG_EFI
+#endif
+
+//
+// platform-dependent types
+//
+
+#ifdef CONFIG_EFI
+
+#include <efi.h>
+#include <efilib.h>
+
+#define copy_guid(destguid, srcguid) (CopyMem(destguid, srcguid, 16))
+#define guids_are_equal(guid1, guid2) (CompareMem(guid1, guid2, 16) == 0)
+
+typedef CHAR16 CHARN;
+#define STR(x) L##x
+
+#endif
+
+
+#ifndef CONFIG_EFI
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+typedef int                 INTN;
+typedef unsigned int        UINTN;
+typedef uint8_t             UINT8;
+typedef uint16_t            UINT16;
+typedef uint32_t            UINT32;
+typedef uint64_t            UINT64;
+typedef void                VOID;
+
+typedef int                 BOOLEAN;
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE  (1)
+#endif
+
+typedef unsigned short      CHAR16;
+typedef char                CHARN;
+#define STR(x) x
+
+void Print(wchar_t *format, ...);
+
+// FUTURE: use STR(),  #define Print printf
+
+#define CopyMem     memcpy
+#define SetMem      memset
+#define CompareMem  memcmp
+
+#define copy_guid(destguid, srcguid) (memcpy(destguid, srcguid, 16))
+#define guids_are_equal(guid1, guid2) (memcmp(guid1, guid2, 16) == 0)
+
+#endif
+
+//
+// platform-independent 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;
+
+//
+// functions provided by the OS-specific module
+//
+
+UINTN read_sector(UINT64 lba, UINT8 *buffer);
+UINTN write_sector(UINT64 lba, UINT8 *buffer);
+UINTN input_boolean(CHARN *prompt, BOOLEAN *bool_out);
+
+//
+// vars and functions provided by the common lib module
+//
+
+extern UINT8           empty_guid[16];
+
+extern PARTITION_INFO  mbr_parts[4];
+extern UINTN           mbr_part_count;
+extern PARTITION_INFO  gpt_parts[128];
+extern UINTN           gpt_part_count;
+
+extern PARTITION_INFO  new_mbr_parts[4];
+extern UINTN           new_mbr_part_count;
+
+extern UINT8           sector[512];
+
+extern MBR_PARTTYPE    mbr_types[];
+extern GPT_PARTTYPE    gpt_types[];
+extern GPT_PARTTYPE    gpt_dummy_type;
+
+CHARN * mbr_parttype_name(UINT8 type);
+UINTN read_mbr(VOID);
+
+GPT_PARTTYPE * gpt_parttype(UINT8 *type_guid);
+UINTN read_gpt(VOID);
+
+UINTN detect_mbrtype_fs(UINT64 partlba, UINTN *parttype, CHARN **fsname);
+
+//
+// actual platform-independent programs
+//
+
+UINTN gptsync(VOID);
+UINTN showpart(VOID);
+
+/* EOF */
diff --git a/gptsync/lib.c b/gptsync/lib.c
new file mode 100644
index 0000000..271dc99
--- /dev/null
+++ b/gptsync/lib.c
@@ -0,0 +1,469 @@
+/*
+ * gptsync/lib.c
+ * Platform-independent code common to gptsync and showpart
+ *
+ * Copyright (c) 2006-2007 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"
+
+// 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 (CHS)") },
+    { 0x04, STR("FAT16 <32M (CHS)") },
+    { 0x05, STR("Extended (CHS)") },
+    { 0x06, STR("FAT16 (CHS)") },
+    { 0x07, STR("NTFS/HPFS") },
+    { 0x0b, STR("FAT32 (CHS)") },
+    { 0x0c, STR("FAT32 (LBA)") },
+    { 0x0e, STR("FAT16 (LBA)") },
+    { 0x0f, STR("Extended (LBA)") },
+    { 0x11, STR("Hidden FAT12 (CHS)") },
+    { 0x14, STR("Hidden FAT16 <32M (CHS)") },
+    { 0x16, STR("Hidden FAT16 (CHS)") },
+    { 0x17, STR("Hidden NTFS/HPFS") },
+    { 0x1b, STR("Hidden FAT32 (CHS)") },
+    { 0x1c, STR("Hidden FAT32 (LBA)") },
+    { 0x1e, STR("Hidden FAT16 (LBA)") },
+    { 0x82, STR("Linux swap / Solaris") },
+    { 0x83, STR("Linux") },
+    { 0x85, STR("Linux Extended") },
+    { 0x86, STR("NT FAT volume set") },
+    { 0x87, STR("NTFS volume set") },
+    { 0x8e, STR("Linux LVM") },
+    { 0xa5, STR("FreeBSD") },
+    { 0xa6, STR("OpenBSD") },
+    { 0xa7, STR("NeXTSTEP") },
+    { 0xa8, STR("Mac OS X UFS") },
+    { 0xa9, STR("NetBSD") },
+    { 0xab, STR("Mac OS X Boot") },
+    { 0xac, STR("Apple RAID") },
+    { 0xaf, STR("Mac OS X HFS+") },
+    { 0xbe, STR("Solaris Boot") },
+    { 0xbf, STR("Solaris") },
+    { 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 },
+    { "\x1E\x4C\x89\x75\xEB\x3A\xD3\x11\xB7\xC1\x7B\x03\xA0\x00\x00\x00", 0x00, STR("HP/UX Data"), GPT_KIND_DATA },
+    { "\x28\xE7\xA1\xE2\xE3\x32\xD6\x11\xA6\x82\x7B\x03\xA0\x00\x00\x00", 0x00, STR("HP/UX Service"), GPT_KIND_SYSTEM },
+    { "\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_SYSTEM },
+    { "\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 },
+    { "\xB4\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", 0xa5, STR("FreeBSD Data"), GPT_KIND_DATA },
+    { "\xB5\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", 0x00, STR("FreeBSD Swap"), GPT_KIND_SYSTEM },
+    { "\xB6\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", 0xa5, STR("FreeBSD UFS"), GPT_KIND_DATA },
+    { "\xB8\x7C\x6E\x51\xCF\x6E\xD6\x11\x8F\xF8\x00\x02\x2D\x09\x71\x2B", 0x00, STR("FreeBSD Vinum"), GPT_KIND_DATA },
+    { "\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 },
+    { "\x00\x53\x46\x55\x00\x00\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0xa8, STR("Mac OS X UFS"), GPT_KIND_DATA },
+    { "\x74\x6F\x6F\x42\x00\x00\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0xab, STR("Mac OS X Boot"), GPT_KIND_DATA },
+    { "\x44\x49\x41\x52\x00\x00\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0xac, STR("Apple RAID"), GPT_KIND_DATA },
+    { "\x44\x49\x41\x52\x4F\x5F\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0xac, STR("Apple RAID (Offline)"), GPT_KIND_DATA },
+    { "\x65\x62\x61\x4C\x00\x6C\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0x00, STR("Apple Label"), GPT_KIND_SYSTEM },
+    { "\x6F\x63\x65\x52\x65\x76\xAA\x11\xAA\x11\x00\x30\x65\x43\xEC\xAC", 0x00, STR("Apple Recovery"), GPT_KIND_BASIC_DATA },
+    { "\x7f\x23\x96\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0x00, STR("Solaris Reserved"), GPT_KIND_SYSTEM },
+    { "\x45\xCB\x82\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Boot"), GPT_KIND_DATA },
+    { "\x4D\xCF\x85\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Root"), GPT_KIND_DATA },
+    { "\x6F\xC4\x87\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0x00, STR("Solaris Swap"), GPT_KIND_SYSTEM },
+    { "\xC3\x8C\x89\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Usr"), GPT_KIND_DATA },
+    { "\x2B\x64\x8B\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0x00, STR("Solaris Backup"), GPT_KIND_SYSTEM },
+    { "\xC7\x2A\x8D\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Stand"), GPT_KIND_DATA },
+    { "\xE9\xF2\x8E\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Var"), GPT_KIND_DATA },
+    { "\x39\xBA\x90\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris Home"), GPT_KIND_DATA },
+    { "\xA5\x83\x92\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0xbf, STR("Solaris ALTSCTR"), GPT_KIND_DATA },
+    { "\x3B\x5A\x94\x6A\xD2\x1D\xB2\x11\x99\xa6\x08\x00\x20\x73\x66\x31", 0x00, STR("Solaris Cache"), GPT_KIND_SYSTEM },
+    { { 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
+//
+
+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");
+}
+
+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;
+}
+
+//
+// GPT functions
+//
+
+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;
+}
+
+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;
+    
+    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;
+        if (gpt_part_count == 0) {
+            Print(L" #      Start LBA      End LBA  Type\n");
+        }
+        
+        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++;
+    }
+    if (gpt_part_count == 0) {
+        Print(L" No partitions defined\n");
+        return 0;
+    }
+    
+    return 0;
+}
+
+//
+// detect file system type
+//
+
+UINTN detect_mbrtype_fs(UINT64 partlba, UINTN *parttype, CHARN **fsname)
+{
+    UINTN   status;
+    UINTN   signature, score;
+    UINTN   sectsize, clustersize, reserved, fatcount, dirsize, sectcount, fatsize, clustercount;
+    
+    *fsname = STR("Unknown");
+    *parttype = 0;
+    
+    // READ sector 0 / offset 0K
+    status = read_sector(partlba, sector);
+    if (status != 0)
+        return status;
+    
+    // detect XFS
+    signature = *((UINT32 *)(sector));
+    if (signature == 0x42534658) {
+        *parttype = 0x83;
+        *fsname = STR("XFS");
+        return 0;
+    }
+    
+    // detect FAT and NTFS
+    sectsize = *((UINT16 *)(sector + 11));
+    clustersize = sector[13];
+    if (sectsize >= 512 && (sectsize & (sectsize - 1)) == 0 &&
+        clustersize > 0 && (clustersize & (clustersize - 1)) == 0) {
+        // preconditions for both FAT and NTFS are now met
+        
+        if (CompareMem(sector + 3, "NTFS    ", 8) == 0) {
+            *parttype = 0x07;
+            *fsname = STR("NTFS");
+            return 0;
+        }
+        
+        score = 0;
+        // boot jump
+        if ((sector[0] == 0xEB && sector[2] == 0x90) || 
+            sector[0] == 0xE9)
+            score++;
+        // boot signature
+        if (sector[510] == 0x55 && sector[511] == 0xAA)
+            score++;
+        // reserved sectors
+        reserved = *((UINT16 *)(sector + 14));
+        if (reserved == 1 || reserved == 32)
+            score++;
+        // number of FATs
+        fatcount = sector[16];
+        if (fatcount == 2)
+            score++;
+        // number of root dir entries
+        dirsize = *((UINT16 *)(sector + 17));
+        // sector count (16-bit and 32-bit versions)
+        sectcount = *((UINT16 *)(sector + 19));
+        if (sectcount == 0)
+            sectcount = *((UINT32 *)(sector + 32));
+        // media byte
+        if (sector[21] == 0xF0 || sector[21] >= 0xF8)
+            score++;
+        // FAT size in sectors
+        fatsize = *((UINT16 *)(sector + 22));
+        if (fatsize == 0)
+            fatsize = *((UINT32 *)(sector + 36));
+        
+        // determine FAT type
+        dirsize = ((dirsize * 32) + (sectsize - 1)) / sectsize;
+        clustercount = sectcount - (reserved + (fatcount * fatsize) + dirsize);
+        clustercount /= clustersize;
+        
+        if (score >= 3) {
+            if (clustercount < 4085) {
+                *parttype = 0x01;
+                *fsname = STR("FAT12");
+            } else if (clustercount < 65525) {
+                *parttype = 0x0e;
+                *fsname = STR("FAT16");
+            } else {
+                *parttype = 0x0c;
+                *fsname = STR("FAT32");
+            }
+            // TODO: check if 0e and 0c are okay to use, maybe we should use 06 and 0b instead...
+            return 0;
+        }
+    }
+    
+    // READ sector 2 / offset 1K
+    status = read_sector(partlba + 2, sector);
+    if (status != 0)
+        return status;
+    
+    // detect HFS+
+    signature = *((UINT16 *)(sector));
+    if (signature == 0x4442) {
+        *parttype = 0xaf;
+        if (*((UINT16 *)(sector + 0x7c)) == 0x2B48)
+            *fsname = STR("HFS Extended (HFS+)");
+        else
+            *fsname = STR("HFS Standard");
+        return 0;
+    } else if (signature == 0x2B48) {
+        *parttype = 0xaf;
+        *fsname = STR("HFS Extended (HFS+)");
+        return 0;
+    }
+    
+    // detect ext2/ext3
+    signature = *((UINT16 *)(sector + 56));
+    if (signature == 0xEF53) {
+        *parttype = 0x83;
+        if (*((UINT16 *)(sector + 92)) & 0x0004)
+            *fsname = STR("ext3");
+        else
+            *fsname = STR("ext2");
+        return 0;
+    }
+    
+    // READ sector 128 / offset 64K
+    status = read_sector(partlba + 128, sector);
+    if (status != 0)
+        return status;
+    
+    // detect ReiserFS
+    if (CompareMem(sector + 52, "ReIsErFs", 8) == 0 ||
+        CompareMem(sector + 52, "ReIsEr2Fs", 9) == 0 ||
+        CompareMem(sector + 52, "ReIsEr3Fs", 9) == 0) {
+        *parttype = 0x83;
+        *fsname = STR("ReiserFS");
+        return 0;
+    }
+    
+    // detect Reiser4
+    if (CompareMem(sector, "ReIsEr4", 7) == 0) {
+        *parttype = 0x83;
+        *fsname = STR("Reiser4");
+        return 0;
+    }
+    
+    // READ sector 64 / offset 32K
+    status = read_sector(partlba + 64, sector);
+    if (status != 0)
+        return status;
+    
+    // detect JFS
+    if (CompareMem(sector, "JFS1", 4) == 0) {
+        *parttype = 0x83;
+        *fsname = STR("JFS");
+        return 0;
+    }
+    
+    // READ sector 16 / offset 8K
+    status = read_sector(partlba + 16, sector);
+    if (status != 0)
+        return status;
+    
+    // detect ReiserFS
+    if (CompareMem(sector + 52, "ReIsErFs", 8) == 0 ||
+        CompareMem(sector + 52, "ReIsEr2Fs", 9) == 0 ||
+        CompareMem(sector + 52, "ReIsEr3Fs", 9) == 0) {
+        *parttype = 0x83;
+        *fsname = STR("ReiserFS");
+        return 0;
+    }
+    
+    return 0;
+}
diff --git a/gptsync/os_unix.c b/gptsync/os_unix.c
new file mode 100644
index 0000000..b43685b
--- /dev/null
+++ b/gptsync/os_unix.c
@@ -0,0 +1,267 @@
+/*
+ * gptsync/os_unix.c
+ * Unix OS glue for gptsync
+ *
+ * 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 <stdarg.h>
+
+#define STRINGIFY(s) #s
+#define STRINGIFY2(s) STRINGIFY(s)
+#define PROGNAME_S STRINGIFY2(PROGNAME)
+
+// variables
+
+static int      fd;
+
+//
+// error functions
+//
+
+void error(const char *msg, ...)
+{
+    va_list par;
+    char buf[4096];
+    
+    va_start(par, msg);
+    vsnprintf(buf, 4096, msg, par);
+    va_end(par);
+    
+    fprintf(stderr, PROGNAME_S ": %s\n", buf);
+}
+
+void errore(const char *msg, ...)
+{
+    va_list par;
+    char buf[4096];
+    
+    va_start(par, msg);
+    vsnprintf(buf, 4096, msg, par);
+    va_end(par);
+    
+    fprintf(stderr, PROGNAME_S ": %s: %s\n", buf, strerror(errno));
+}
+
+//
+// sector I/O functions
+//
+
+UINTN read_sector(UINT64 lba, UINT8 *buffer)
+{
+    off_t   offset;
+    off_t   result_seek;
+    ssize_t result_read;
+    
+    offset = lba * 512;
+    result_seek = lseek(fd, offset, SEEK_SET);
+    if (result_seek != offset) {
+        errore("Seek to %llu failed", offset);
+        return 1;
+    }
+    
+    result_read = read(fd, buffer, 512);
+    if (result_read < 0) {
+        errore("Data read failed at position %llu", offset);
+        return 1;
+    }
+    if (result_read != 512) {
+        errore("Data read fell short at position %llu", offset);
+        return 1;
+    }
+    return 0;
+}
+
+UINTN write_sector(UINT64 lba, UINT8 *buffer)
+{
+    off_t   offset;
+    off_t   result_seek;
+    ssize_t result_write;
+    
+    offset = lba * 512;
+    result_seek = lseek(fd, offset, SEEK_SET);
+    if (result_seek != offset) {
+        errore("Seek to %llu failed", offset);
+        return 1;
+    }
+    
+    result_write = write(fd, buffer, 512);
+    if (result_write < 0) {
+        errore("Data write failed at position %llu", offset);
+        return 1;
+    }
+    if (result_write != 512) {
+        errore("Data write fell short at position %llu", offset);
+        return 1;
+    }
+    return 0;
+}
+
+//
+// keyboard input
+//
+
+UINTN input_boolean(CHARN *prompt, BOOLEAN *bool_out)
+{
+    int c;
+    
+    printf("%s", prompt);
+    fflush(NULL);
+    
+    c = getchar();
+    if (c == EOF)
+        return 1;
+    
+    if (c == 'y' || c == 'Y') {
+        printf("Yes\n");
+        *bool_out = TRUE;
+    } else {
+        printf("No\n");
+        *bool_out = FALSE;
+    }
+    
+    return 0;
+}
+
+//
+// EFI-style print function
+//
+
+void Print(wchar_t *format, ...)
+{
+    va_list par;
+    char formatbuf[256];
+    char buf[4096];
+    int i;
+    
+    for (i = 0; format[i]; i++)
+        formatbuf[i] = (format[i] > 255) ? '?' : (char)(format[i] & 0xff);
+    formatbuf[i] = 0;
+    
+    va_start(par, format);
+    vsnprintf(buf, 4096, formatbuf, par);
+    va_end(par);
+    
+    printf("%s", buf);
+}
+
+//
+// main entry point
+//
+
+int main(int argc, char *argv[])
+{
+    char        *filename;
+    struct stat sb;
+    int         filekind;
+    UINT64      filesize;
+    char        *reason;
+    int         status;
+    
+    // argument check
+    if (argc != 2) {
+        fprintf(stderr, "Usage: " PROGNAME_S " <device>\n");
+        return 1;
+    }
+    filename = argv[1];
+    
+    // set input to unbuffered
+    fflush(NULL);
+    setvbuf(stdin, NULL, _IONBF, 0);
+    
+    // stat check
+    if (stat(filename, &sb) < 0) {
+        errore("Can't stat %.300s", filename);
+        return 1;
+    }
+    
+    filekind = 0;
+    filesize = 0;
+    reason = NULL;
+    if (S_ISREG(sb.st_mode))
+        filesize = sb.st_size;
+    else if (S_ISBLK(sb.st_mode))
+        filekind = 1;
+    else if (S_ISCHR(sb.st_mode))
+        filekind = 2;
+    else if (S_ISDIR(sb.st_mode))
+        reason = "Is a directory";
+    else if (S_ISFIFO(sb.st_mode))
+        reason = "Is a FIFO";
+#ifdef S_ISSOCK
+    else if (S_ISSOCK(sb.st_mode))
+        reason = "Is a socket";
+#endif
+    else
+        reason = "Is an unknown kind of special file";
+    
+    if (reason != NULL) {
+        error("%.300s: %s", filename, reason);
+        return 1;
+    }
+    
+    // open file
+    fd = open(filename, O_RDWR);
+    if (fd < 0 && errno == EBUSY) {
+        fd = open(filename, O_RDONLY);
+#ifndef NOREADONLYWARN
+        if (fd >= 0)
+            printf("Warning: %.300s opened read-only\n", filename);
+#endif
+    }
+    if (fd < 0) {
+        errore("Can't open %.300s", filename);
+        return 1;
+    }
+    
+    // (try to) guard against TTY character devices
+    if (filekind == 2) {
+        if (isatty(fd)) {
+            error("%.300s: Is a TTY device", filename);
+            return 1;
+        }
+    }
+    
+    // run sync algorithm
+    status = PROGNAME();
+    printf("\n");
+    
+    // close file
+    if (close(fd) != 0) {
+        errore("Error while closing %.300s", filename);
+        return 1;
+    }
+    
+    return status;
+}
diff --git a/gptsync/showpart.c b/gptsync/showpart.c
new file mode 100644
index 0000000..3d52ba3
--- /dev/null
+++ b/gptsync/showpart.c
@@ -0,0 +1,257 @@
+/*
+ * gptsync/showpart.c
+ * Platform-independent code for analyzing hard disk partitioning
+ *
+ * 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"
+
+//
+// memory string search
+//
+
+static INTN FindMem(VOID *Buffer, UINTN BufferLength, VOID *SearchString, UINTN SearchStringLength)
+{
+    UINT8 *BufferPtr;
+    UINTN Offset;
+    
+    BufferPtr = Buffer;
+    BufferLength -= SearchStringLength;
+    for (Offset = 0; Offset < BufferLength; Offset++, BufferPtr++) {
+        if (CompareMem(BufferPtr, SearchString, SearchStringLength) == 0)
+            return (INTN)Offset;
+    }
+    
+    return -1;
+}
+
+//
+// detect boot code
+//
+
+static UINTN detect_bootcode(UINT64 partlba, CHARN **bootcodename)
+{
+    UINTN   status;
+    BOOLEAN bootable;
+    
+    // read MBR data
+    status = read_sector(partlba, sector);
+    if (status != 0)
+        return status;
+    
+    // check bootable signature
+    if (*((UINT16 *)(sector + 510)) == 0xaa55 && sector[0] != 0)
+        bootable = TRUE;
+    else
+        bootable = FALSE;
+    *bootcodename = NULL;
+    
+    // detect specific boot codes
+    if (CompareMem(sector + 2, "LILO", 4) == 0 ||
+        CompareMem(sector + 6, "LILO", 4) == 0) {
+        *bootcodename = STR("LILO");
+        
+    } else if (CompareMem(sector + 3, "SYSLINUX", 8) == 0) {
+        *bootcodename = STR("SYSLINUX");
+        
+    } else if (FindMem(sector, 512, "ISOLINUX", 8) >= 0) {
+        *bootcodename = STR("ISOLINUX");
+        
+    } else if (FindMem(sector, 512, "Geom\0Hard Disk\0Read\0 Error\0", 27) >= 0) {
+        *bootcodename = STR("GRUB");
+        
+    } else if ((*((UINT32 *)(sector + 502)) == 0 &&
+                *((UINT32 *)(sector + 506)) == 50000 &&
+                *((UINT16 *)(sector + 510)) == 0xaa55) ||
+               FindMem(sector, 512, "Starting the BTX loader", 23) >= 0) {
+        *bootcodename = STR("FreeBSD");
+        
+    } else if (FindMem(sector, 512, "!Loading", 8) >= 0 ||
+               FindMem(sector, 512, "/cdboot\0/CDBOOT\0", 16) >= 0) {
+        *bootcodename = STR("OpenBSD");
+        
+    } else if (FindMem(sector, 512, "NTLDR", 5) >= 0) {
+        *bootcodename = STR("Windows NTLDR");
+        
+    } else if (FindMem(sector, 512, "BOOTMGR", 7) >= 0) {
+        *bootcodename = STR("Windows BOOTMGR (Vista)");
+        
+    } else if (FindMem(sector, 512, "CPUBOOT SYS", 11) >= 0 ||
+               FindMem(sector, 512, "KERNEL  SYS", 11) >= 0) {
+        *bootcodename = STR("FreeDOS");
+        
+    } else if (FindMem(sector, 512, "OS2LDR", 6) >= 0 ||
+               FindMem(sector, 512, "OS2BOOT", 7) >= 0) {
+        *bootcodename = STR("eComStation");
+        
+    } else if (FindMem(sector, 512, "Be Boot Loader", 14) >= 0) {
+        *bootcodename = STR("BeOS");
+        
+    } else if (FindMem(sector, 512, "yT Boot Loader", 14) >= 0) {
+        *bootcodename = STR("ZETA");
+        
+    } else if (FindMem(sector, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0) {
+        *bootcodename = STR("Haiku");
+        
+    }
+    
+    if (FindMem(sector, 512, "Non-system disk", 15) >= 0)   // dummy FAT boot sector
+        *bootcodename = STR("None (Non-system disk message)");
+    
+    // TODO: Add a note if a specific code was detected, but the sector is not bootable?
+    
+    if (*bootcodename == NULL) {
+        if (bootable)
+            *bootcodename = STR("Unknown, but bootable");
+        else
+            *bootcodename = STR("None");
+    }
+    
+    return 0;
+}
+
+//
+// check one partition
+//
+
+static UINTN analyze_part(UINT64 partlba)
+{
+    UINTN   status;
+    UINTN   i;
+    CHARN   *bootcodename;
+    UINTN   parttype;
+    CHARN   *fsname;
+    
+    if (partlba == 0)
+        Print(L"\nMBR contents:\n");
+    else
+        Print(L"\nPartition at LBA %lld:\n", partlba);
+    
+    // detect boot code
+    status = detect_bootcode(partlba, &bootcodename);
+    if (status)
+        return status;
+    Print(L" Boot Code: %s\n", bootcodename);
+    
+    if (partlba == 0)
+        return 0;   // short-circuit MBR analysis
+    
+    // detect file system
+    status = detect_mbrtype_fs(partlba, &parttype, &fsname);
+    if (status)
+        return status;
+    Print(L" File System: %s\n", fsname);
+    
+    // cross-reference with partition table
+    for (i = 0; i < gpt_part_count; i++) {
+        if (gpt_parts[i].start_lba == partlba) {
+            Print(L" Listed in GPT as partition %d, type %s\n", i+1,
+                  gpt_parts[i].gpt_parttype->name);
+        }
+    }
+    for (i = 0; i < mbr_part_count; i++) {
+        if (mbr_parts[i].start_lba == partlba) {
+            Print(L" Listed in MBR as partition %d, type %02x  %s%s\n", i+1,
+                  mbr_parts[i].mbr_type,
+                  mbr_parttype_name(mbr_parts[i].mbr_type),
+                  mbr_parts[i].active ? STR(", active") : STR(""));
+        }
+    }
+    
+    return 0;
+}
+
+//
+// check all partitions
+//
+
+static UINTN analyze_parts(VOID)
+{
+    UINTN   i, k;
+    UINTN   status;
+    BOOLEAN is_dupe;
+    
+    // check MBR (bootcode only)
+    status = analyze_part(0);
+    if (status)
+        return status;
+    
+    // check partitions listed in GPT
+    for (i = 0; i < gpt_part_count; i++) {
+        status = analyze_part(gpt_parts[i].start_lba);
+        if (status)
+            return status;
+    }
+    
+    // check partitions listed in MBR, but not in GPT
+    for (i = 0; i < mbr_part_count; i++) {
+        if (mbr_parts[i].start_lba == 1 && mbr_parts[i].mbr_type == 0xee)
+            continue;   // skip EFI Protective entry
+        
+        is_dupe = FALSE;
+        for (k = 0; k < gpt_part_count; k++)
+            if (gpt_parts[k].start_lba == mbr_parts[i].start_lba)
+                is_dupe = TRUE;
+        
+        if (!is_dupe) {
+            status = analyze_part(mbr_parts[i].start_lba);
+            if (status)
+                return status;
+        }
+    }
+    
+    return 0;
+}
+
+//
+// display algorithm entry point
+//
+
+UINTN showpart(VOID)
+{
+    UINTN   status = 0;
+    UINTN   status_gpt, status_mbr;
+    
+    // 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);
+    
+    // analyze all partitions
+    status = analyze_parts();
+    if (status != 0)
+        return status;
+    
+    return status;
+}
diff --git a/gptsync/syslinux_mbr.h b/gptsync/syslinux_mbr.h
new file mode 100644
index 0000000..1c33e11
--- /dev/null
+++ b/gptsync/syslinux_mbr.h
@@ -0,0 +1,90 @@
+/*
+ * include/syslinux_mbr.h
+ * MBR boot code
+ *
+ * The boot code in this file was taken from syslinux-3.11. It is covered
+ * by the following license:
+ *
+ ; -----------------------------------------------------------------------
+ ;   
+ ;   Copyright 2003-2004 H. Peter Anvin - All Rights Reserved
+ ;
+ ;   Permission is hereby granted, free of charge, to any person
+ ;   obtaining a copy of this software and associated documentation
+ ;   files (the "Software"), to deal in the Software without
+ ;   restriction, including without limitation the rights to use,
+ ;   copy, modify, merge, publish, distribute, sublicense, and/or
+ ;   sell copies of the Software, and to permit persons to whom
+ ;   the Software is furnished to do so, subject to the following
+ ;   conditions:
+ ;   
+ ;   The above copyright notice and this permission notice shall
+ ;   be included in all copies or substantial portions of the Software.
+ ;   
+ ;   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ ;   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ ;   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ ;   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ ;   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ ;   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ ;   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ ;   OTHER DEALINGS IN THE SOFTWARE.
+ ;
+ ; -----------------------------------------------------------------------
+ *
+ */
+
+#ifndef __SYSLINUX_MBR_H__
+#define __SYSLINUX_MBR_H__
+
+
+#define MBR_BOOTCODE_SIZE (440)
+
+
+#define SYSLINUX_MBR_SIZE (304)
+
+static UINT8 syslinux_mbr[SYSLINUX_MBR_SIZE] = {
+    0xfa, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0x8e,
+    0xd0, 0xbc, 0x00, 0x7c, 0xfb, 0xfc, 0x89, 0xe6,
+    0xbf, 0x00, 0x06, 0xb9, 0x00, 0x01, 0xf3, 0xa5,
+    0xea, 0x1d, 0x06, 0x00, 0x00, 0x88, 0x16, 0x00,
+    0x08, 0xb4, 0x08, 0xcd, 0x13, 0x31, 0xc0, 0x88,
+    0xf0, 0x40, 0xa3, 0xf0, 0x06, 0x80, 0xe1, 0x3f,
+    0x88, 0x0e, 0xf2, 0x06, 0xbe, 0xbe, 0x07, 0x31,
+    0xc0, 0xb9, 0x04, 0x00, 0xf6, 0x04, 0x80, 0x74,
+    0x03, 0x40, 0x89, 0xf7, 0x83, 0xc6, 0x10, 0xe2,
+    0xf3, 0x83, 0xf8, 0x01, 0x75, 0x73, 0x8a, 0x16,
+    0x00, 0x08, 0xb8, 0x00, 0x41, 0xbb, 0xaa, 0x55,
+    0x31, 0xc9, 0x30, 0xf6, 0xf9, 0xcd, 0x13, 0x72,
+    0x23, 0x81, 0xfb, 0x55, 0xaa, 0x75, 0x1d, 0xf6,
+    0xc1, 0x01, 0x74, 0x18, 0x57, 0xbe, 0xe0, 0x06,
+    0x8b, 0x5d, 0x08, 0x89, 0x5c, 0x08, 0x8b, 0x5d,
+    0x0a, 0x89, 0x5c, 0x0a, 0x8a, 0x16, 0x00, 0x08,
+    0xb4, 0x42, 0xeb, 0x2a, 0x57, 0x8b, 0x45, 0x08,
+    0x8b, 0x55, 0x0a, 0xf7, 0x36, 0xf2, 0x06, 0x42,
+    0x89, 0xd1, 0x31, 0xd2, 0xf7, 0x36, 0xf0, 0x06,
+    0x88, 0xc5, 0xd1, 0xe8, 0xd1, 0xe8, 0x24, 0xc0,
+    0x08, 0xc1, 0x88, 0xd6, 0x8a, 0x16, 0x00, 0x08,
+    0xbb, 0x00, 0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13,
+    0x72, 0x16, 0x5e, 0x81, 0x3e, 0xfe, 0x7d, 0x55,
+    0xaa, 0x75, 0x08, 0xfa, 0xea, 0x00, 0x7c, 0x00,
+    0x00, 0x77, 0x05, 0xbe, 0xf4, 0x06, 0xeb, 0x03,
+    0xbe, 0x0f, 0x07, 0xac, 0x20, 0xc0, 0x74, 0x0c,
+    0xb4, 0x0e, 0x8a, 0x3e, 0x62, 0x04, 0xb3, 0x07,
+    0xcd, 0x10, 0xeb, 0xef, 0xeb, 0xfe, 0x00, 0x00,
+    0x10, 0x00, 0x01, 0x00, 0x00, 0x7c, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x4d, 0x69, 0x73, 0x73,
+    0x69, 0x6e, 0x67, 0x20, 0x6f, 0x70, 0x65, 0x72,
+    0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x79,
+    0x73, 0x74, 0x65, 0x6d, 0x0d, 0x0a, 0x00, 0x4f,
+    0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67,
+    0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
+    0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20,
+    0x65, 0x72, 0x72, 0x6f, 0x72, 0x0d, 0x0a, 0x00
+};
+
+
+#endif /* __SYSLINUX_MBR_H__ */
+
+/* EOF */
diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in
index 1d3679f..ef65bf0 100644
--- a/ovirt-node.spec.in
+++ b/ovirt-node.spec.in
@@ -99,6 +99,11 @@ cd -
 %{__install} -Dp -m0755 ovirt-listen-awake/ovirt-install-node %{buildroot}%{_sbindir}
 %{__install} -Dp -m0755 ovirt-listen-awake/ovirt-uninstall-node %{buildroot}%{_sbindir}
 
+# gptsync
+%{__install} -p -m0755 gptsync/gptsync %{buildroot}%{_sbindir}
+%{__install} -p -m0755 gptsync/showpart %{buildroot}%{_sbindir}
+#
+
 %{__install} -p -m0644 scripts/ovirt-functions %{buildroot}%{_initrddir}
 %{__install} -p -m0755 scripts/ovirt-early %{buildroot}%{_initrddir}
 %{__install} -p -m0755 scripts/ovirt-firstboot %{buildroot}%{_initrddir}
@@ -176,6 +181,8 @@ fi
 %{_sbindir}/ovirt-listen-awake
 %{_sbindir}/ovirt-install-node
 %{_sbindir}/ovirt-uninstall-node
+%{_sbindir}/gptsync
+%{_sbindir}/showpart
 %{_initrddir}/ovirt-early
 %{_initrddir}/ovirt-firstboot
 %{_initrddir}/ovirt
-- 
1.5.6.5




More information about the ovirt-devel mailing list