diff options
Diffstat (limited to 'gptsync')
-rw-r--r-- | gptsync/Makefile | 21 | ||||
-rw-r--r-- | gptsync/README | 2 | ||||
-rw-r--r-- | gptsync/gptsync.c | 408 | ||||
-rw-r--r-- | gptsync/gptsync.h | 104 | ||||
-rw-r--r-- | gptsync/lib.c | 469 | ||||
-rw-r--r-- | gptsync/os_unix.c | 14 | ||||
-rw-r--r-- | gptsync/showpart.c | 257 |
7 files changed, 941 insertions, 334 deletions
diff --git a/gptsync/Makefile b/gptsync/Makefile index 8ba73595a..8cc91dd51 100644 --- a/gptsync/Makefile +++ b/gptsync/Makefile @@ -2,29 +2,33 @@ # Makefile for gptsync on Unix platforms # -OBJS = gptsync.o os_unix.o -TARGET = gptsync +TARGETS = gptsync showpart +OBJS = showpart.c lib.c os_unix.c gptsync.c CPPFLAGS = -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 CFLAGS = -Wall -Werror LDFLAGS = LIBS = +CC = gcc # real making -all: $(TARGET) +all: $(TARGETS) -$(TARGET): $(OBJS) - $(CC) $(LDFLAGS) -o $(TARGET) $(OBJS) $(LIBS) +gptsync: gptsync.o lib.o + $(CC) $(LDFLAGS) -DPROGNAME=$@ -o $@ os_unix.c $^ $(LIBS) -$(OBJS): %.o: %.c - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< +showpart: showpart.o lib.o + $(CC) $(LDFLAGS) -DPROGNAME=$@ -o $@ os_unix.c $^ $(LIBS) + +%.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< # cleanup clean: - $(RM) *.o *~ *% $(TARGET) .depend + $(RM) *.o *~ *% $(TARGETS) .depend # automatic dependencies @@ -42,3 +46,4 @@ endif install: all mkdir -p $(DESTDIR)/usr/sbin install -m 755 gptsync $(DESTDIR)/usr/sbin/gptsync + install -m 755 showpart $(DESTDIR)/usr/sbin/showpart diff --git a/gptsync/README b/gptsync/README index f364b6367..cb306bd76 100644 --- a/gptsync/README +++ b/gptsync/README @@ -8,7 +8,7 @@ The original license follows. rEFIt License =============== -Copyright (c) 2006 Christoph Pfisterer +Copyright (c) 2006-2007 Christoph Pfisterer All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/gptsync/gptsync.c b/gptsync/gptsync.c index 8de9b8093..2909c5935 100644 --- a/gptsync/gptsync.c +++ b/gptsync/gptsync.c @@ -2,7 +2,7 @@ * gptsync/gptsync.c * Platform-independent code for syncing GPT and MBR * - * Copyright (c) 2006 Christoph Pfisterer + * Copyright (c) 2006-2007 Christoph Pfisterer * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,216 +38,10 @@ #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; @@ -350,7 +144,6 @@ static UINTN write_mbr(VOID) } } if (!have_bootcode) { - Print(L"Writing boot code to MBR\n"); // 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); @@ -370,83 +163,6 @@ static UINTN write_mbr(VOID) // 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; @@ -502,14 +218,49 @@ static UINTN check_gpt(VOID) static UINTN analyze(VOID) { - UINTN action = ACTION_NONE; - UINTN i, k, iter; + UINTN action; + UINTN i, k, iter, count_active, detected_parttype; + CHARN *fsname; UINT64 min_start_lba; - BOOLEAN have_active; + 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; @@ -519,7 +270,7 @@ static UINTN analyze(VOID) } if (action == ACTION_NONE && mbr_part_count > 0) { if (mbr_parts[0].mbr_type == 0xee && - gpt_parts[0].gpt_parttype->mbr_type == 0xef && + 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 @@ -533,11 +284,18 @@ static UINTN analyze(VOID) 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)) + (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) { @@ -547,7 +305,7 @@ static UINTN analyze(VOID) 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)) + (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; } @@ -571,7 +329,7 @@ static UINTN analyze(VOID) new_mbr_parts[0].mbr_type = 0xee; new_mbr_part_count = 1; - if (gpt_parts[0].gpt_parttype->mbr_type == 0xef) { + if (gpt_parts[0].mbr_type == 0xef) { new_mbr_parts[0].end_lba = gpt_parts[0].end_lba; i = 1; } else { @@ -585,54 +343,70 @@ static UINTN analyze(VOID) } // 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].gpt_parttype->mbr_type; + 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) { - // 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; - } + 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++; } - // if no partition is active, pick one + // make sure there's exactly one active partition for (iter = 0; iter < 3; iter++) { // check - have_active = FALSE; + count_active = 0; for (i = 0; i < new_mbr_part_count; i++) if (new_mbr_parts[i].active) - have_active = TRUE; - if (have_active) + count_active++; + if (count_active == 1) 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; + 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++; + } } } @@ -660,7 +434,7 @@ UINTN gptsync(VOID) { UINTN status = 0; UINTN status_gpt, status_mbr; - // BOOLEAN proceed = FALSE; + BOOLEAN proceed = FALSE; // get full information from disk status_gpt = read_gpt(); @@ -676,16 +450,16 @@ UINTN gptsync(VOID) status = check_mbr(); // check MBR for consistency if (status != 0) return status; - status = analyze(); // analyze the situation + 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; + // 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(); diff --git a/gptsync/gptsync.h b/gptsync/gptsync.h index c14d32959..8bb5da4dc 100644 --- a/gptsync/gptsync.h +++ b/gptsync/gptsync.h @@ -1,6 +1,6 @@ /* * gptsync/gptsync.h - * Common header for gptsync + * Common header for gptsync and showpart * * Copyright (c) 2006 Christoph Pfisterer * All rights reserved. @@ -38,12 +38,12 @@ // config // -#ifdef EFI32 +#if defined(EFI32) || defined(EFIX64) #define CONFIG_EFI #endif // -// types +// platform-dependent types // #ifdef CONFIG_EFI @@ -73,6 +73,7 @@ typedef CHAR16 CHARN; #include <sys/time.h> #include <fcntl.h> +typedef int INTN; typedef unsigned int UINTN; typedef unsigned char UINT8; typedef unsigned short UINT16; @@ -106,6 +107,72 @@ void Print(wchar_t *format, ...); #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 // @@ -114,9 +181,38 @@ UINTN write_sector(UINT64 lba, UINT8 *buffer); UINTN input_boolean(CHARN *prompt, BOOLEAN *bool_out); // -// common platform-independent function +// 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 000000000..271dc9999 --- /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 index 775554d7d..b43685b58 100644 --- a/gptsync/os_unix.c +++ b/gptsync/os_unix.c @@ -38,6 +38,10 @@ #include <stdarg.h> +#define STRINGIFY(s) #s +#define STRINGIFY2(s) STRINGIFY(s) +#define PROGNAME_S STRINGIFY2(PROGNAME) + // variables static int fd; @@ -55,7 +59,7 @@ void error(const char *msg, ...) vsnprintf(buf, 4096, msg, par); va_end(par); - fprintf(stderr, "gptsync: %s\n", buf); + fprintf(stderr, PROGNAME_S ": %s\n", buf); } void errore(const char *msg, ...) @@ -67,7 +71,7 @@ void errore(const char *msg, ...) vsnprintf(buf, 4096, msg, par); va_end(par); - fprintf(stderr, "gptsync: %s: %s\n", buf, strerror(errno)); + fprintf(stderr, PROGNAME_S ": %s: %s\n", buf, strerror(errno)); } // @@ -187,7 +191,7 @@ int main(int argc, char *argv[]) // argument check if (argc != 2) { - fprintf(stderr, "Usage: gptsync <device>\n"); + fprintf(stderr, "Usage: " PROGNAME_S " <device>\n"); return 1; } filename = argv[1]; @@ -231,8 +235,10 @@ int main(int argc, char *argv[]) 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); @@ -248,7 +254,7 @@ int main(int argc, char *argv[]) } // run sync algorithm - status = gptsync(); + status = PROGNAME(); printf("\n"); // close file diff --git a/gptsync/showpart.c b/gptsync/showpart.c new file mode 100644 index 000000000..3d52ba34e --- /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; +} |