From 406f651b53ec93761f5ddf009d9acd32964aa481 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Sat, 10 Jun 2006 02:44:57 +0000 Subject: supposed to be here --- gptsync/Makefile | 47 ++++ gptsync/README | 41 +++ gptsync/gptsync.c | 695 +++++++++++++++++++++++++++++++++++++++++++++++++ gptsync/gptsync.h | 122 +++++++++ gptsync/os_unix.c | 261 +++++++++++++++++++ gptsync/syslinux_mbr.h | 90 +++++++ 6 files changed, 1256 insertions(+) create mode 100644 gptsync/Makefile create mode 100644 gptsync/README create mode 100644 gptsync/gptsync.c create mode 100644 gptsync/gptsync.h create mode 100644 gptsync/os_unix.c create mode 100644 gptsync/syslinux_mbr.h (limited to 'gptsync') diff --git a/gptsync/Makefile b/gptsync/Makefile new file mode 100644 index 000000000..8302933d3 --- /dev/null +++ b/gptsync/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for gptsync on Unix platforms +# + +OBJS = gptsync.o os_unix.o + +TARGET = gptsync + +CPPFLAGS = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +CFLAGS = -Wall -Werror +LDFLAGS = +LIBS = + +# real making + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(LDFLAGS) -o $(TARGET) $(OBJS) $(LIBS) + +$(OBJS): %.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< + +# cleanup + +clean: + $(RM) *.o *~ *% $(TARGET) + +distclean: clean + $(RM) .depend + +# automatic dependencies + +depend: dep +dep: + for i in $(OBJS:.o=.c) ; do $(CC) $(CPPFLAGS) -MM $$i ; done > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +# eof + +# install target for anaconda +install: all + mkdir -p $(DESTDIR)/usr/sbin + install -m 755 gptsync $(DESTDIR)/usr/sbin/gptsync diff --git a/gptsync/README b/gptsync/README new file mode 100644 index 000000000..f364b6367 --- /dev/null +++ b/gptsync/README @@ -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 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 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; +} diff --git a/gptsync/gptsync.h b/gptsync/gptsync.h new file mode 100644 index 000000000..c14d32959 --- /dev/null +++ b/gptsync/gptsync.h @@ -0,0 +1,122 @@ +/* + * gptsync/gptsync.h + * Common header 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. + */ + +// +// config +// + +#ifdef EFI32 +#define CONFIG_EFI +#endif + +// +// types +// + +#ifdef CONFIG_EFI + +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef unsigned int UINTN; +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned long UINT32; +typedef unsigned long long 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 + +// +// 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); + +// +// common platform-independent function +// + +UINTN gptsync(VOID); + +/* EOF */ diff --git a/gptsync/os_unix.c b/gptsync/os_unix.c new file mode 100644 index 000000000..775554d7d --- /dev/null +++ b/gptsync/os_unix.c @@ -0,0 +1,261 @@ +/* + * 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 + +// 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, "gptsync: %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, "gptsync: %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: gptsync \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); + if (fd >= 0) + printf("Warning: %.300s opened read-only\n", filename); + } + 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 = gptsync(); + printf("\n"); + + // close file + if (close(fd) != 0) { + errore("Error while closing %.300s", filename); + return 1; + } + + return status; +} diff --git a/gptsync/syslinux_mbr.h b/gptsync/syslinux_mbr.h new file mode 100644 index 000000000..1c33e1159 --- /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 */ -- cgit