summaryrefslogtreecommitdiff
path: root/cgpt
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2010-06-11 09:15:55 -0700
committerBill Richardson <wfrichar@chromium.org>2010-06-11 09:15:55 -0700
commitf1372d9109d638fbb1a177a89ebaf64e7ee0637e (patch)
tree243cdacbc1028e6a987d582d33927560af0b47e0 /cgpt
parent6a97b3e2a1bee35bf3c00f2fb0faafde4aaab9e2 (diff)
downloadvboot-f1372d9109d638fbb1a177a89ebaf64e7ee0637e.tar.gz
Nearly complete rewrite of cgpt tool.
This fixes a number of bugs, adds a bunch of commands, and essentially makes cgpt ready to use as a replacement for gpt. Still to do is to add commands and options that will let it generated intentionally bad partitions, for use in testing. Review URL: http://codereview.chromium.org/2719008
Diffstat (limited to 'cgpt')
-rw-r--r--cgpt/Makefile45
-rw-r--r--cgpt/cgpt.c75
-rw-r--r--cgpt/cgpt.h130
-rw-r--r--cgpt/cgpt_common.c726
-rw-r--r--cgpt/cmd_add.c276
-rw-r--r--cgpt/cmd_boot.c167
-rw-r--r--cgpt/cmd_create.c106
-rw-r--r--cgpt/cmd_repair.c85
-rw-r--r--cgpt/cmd_show.c413
-rw-r--r--cgpt/endian.h44
10 files changed, 2067 insertions, 0 deletions
diff --git a/cgpt/Makefile b/cgpt/Makefile
new file mode 100644
index 00000000..1f8deded
--- /dev/null
+++ b/cgpt/Makefile
@@ -0,0 +1,45 @@
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+CC ?= gcc
+TOP ?= ..
+CFLAGS ?= -Wall -DNDEBUG -O3 -Werror
+CFLAGS += -static
+LDFLAGS += -luuid
+FWDIR=$(TOP)/vboot_firmware
+
+INCLUDES = -I$(FWDIR)/lib/cgptlib/include
+LIBS = $(FWDIR)/vboot_fw.a
+
+DESTDIR ?= /usr/bin
+
+PROGNAME = cgpt
+
+OBJS= \
+ cgpt.o \
+ cmd_show.o \
+ cmd_repair.o \
+ cmd_create.o \
+ cmd_add.o \
+ cmd_boot.o \
+ cgpt_common.o
+
+
+all: $(PROGNAME)
+
+$(PROGNAME): $(OBJS) $(LIBS)
+ $(CC) -o $(PROGNAME) $(CFLAGS) $^ $(LDFLAGS)
+
+.c.o:
+ $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+clean:
+ rm -f $(PROGNAME) *.o *~
+
+install: $(PROGNAME)
+ mkdir -p $(DESTDIR)
+ cp -f $^ $(DESTDIR)
+ chmod a+rx $(patsubst %,$(DESTDIR)/%,$^)
+
+.PHONY: all clean install
diff --git a/cgpt/cgpt.c b/cgpt/cgpt.c
new file mode 100644
index 00000000..d80bffea
--- /dev/null
+++ b/cgpt/cgpt.c
@@ -0,0 +1,75 @@
+/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
+ * files for more details.
+ */
+
+#include "cgpt.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+
+const char* progname;
+const char* command;
+
+struct {
+ const char *name;
+ int (*fp)(int argc, char *argv[]);
+ const char *comment;
+} cmds[] = {
+ {"create", cmd_create, "Create or reset GPT headers and tables"},
+ {"add", cmd_add, "Add, edit or remove a partition entry"},
+ {"show", cmd_show, "Show partition table and entries"},
+ {"repair", cmd_repair, "Repair damaged GPT headers and tables"},
+ {"boot", cmd_boot, "Edit the PMBR sector for legacy BIOSes"},
+};
+
+
+void Usage(void) {
+ int i;
+
+ printf("Usage: %s COMMAND [OPTIONS] DRIVE\n\n"
+ "Supported COMMANDs:\n\n",
+ progname);
+
+ for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
+ printf(" %-10s %s\n", cmds[i].name, cmds[i].comment);
+ }
+ printf("\nFor more detailed usage, use %s COMMAND -h\n\n", progname);
+}
+
+
+
+int main(int argc, char *argv[]) {
+ int i;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (argc < 2) {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ // increment optind now, so that getopt skips argv[0] in command function
+ command = argv[optind++];
+
+ // Find the command to invoke.
+ for (i = 0; command && i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
+ if (0 == strcmp(cmds[i].name, command)) {
+ return cmds[i].fp(argc, argv);
+ }
+ }
+
+ // Couldn't find the command.
+ Usage();
+
+ return CGPT_FAILED;
+}
diff --git a/cgpt/cgpt.h b/cgpt/cgpt.h
new file mode 100644
index 00000000..504e6dd7
--- /dev/null
+++ b/cgpt/cgpt.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_
+#define VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <features.h>
+#include <stdint.h>
+#include "endian.h"
+#include "gpt.h"
+#include "cgptlib.h"
+
+
+// Just for clarity
+enum {
+ CGPT_OK = 0,
+ CGPT_FAILED,
+};
+
+
+struct legacy_partition {
+ uint8_t status;
+ uint8_t f_head;
+ uint8_t f_sect;
+ uint8_t f_cyl;
+ uint8_t type;
+ uint8_t l_head;
+ uint8_t l_sect;
+ uint8_t l_cyl;
+ uint32_t f_lba;
+ uint32_t num_sect;
+} __attribute__((packed));
+
+
+// syslinux uses this format:
+struct pmbr {
+ uint8_t bootcode[424];
+ Guid boot_guid;
+ uint32_t disk_id;
+ uint8_t magic[2]; // 0x1d, 0x9a
+ struct legacy_partition part[4];
+ uint8_t sig[2]; // 0x55, 0xaa
+} __attribute__((packed));
+
+void PMBRToStr(struct pmbr *pmbr, char *str);
+
+// Handle to the drive storing the GPT.
+struct drive {
+ int fd; /* file descriptor */
+ uint64_t size; /* total size (in bytes) */
+ GptData gpt;
+ struct pmbr pmbr;
+};
+
+
+int DriveOpen(const char *drive_path, struct drive *drive);
+int DriveClose(struct drive *drive, int update_as_needed);
+int CheckValid(const struct drive *drive);
+
+/* GUID conversion functions. Accepted format:
+ *
+ * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
+ *
+ * At least GUID_STRLEN bytes should be reserved in 'str' (included the tailing
+ * '\0').
+ */
+#define GUID_STRLEN 37
+int StrToGuid(const char *str, Guid *guid);
+void GuidToStr(const Guid *guid, char *str);
+int IsZero(const Guid *guid);
+
+
+int ReadPMBR(struct drive *drive);
+int WritePMBR(struct drive *drive);
+
+
+/* Convert UTF16 string to UTF8. Rewritten from gpt utility.
+ * Caller must prepare enough space for UTF8. The rough estimation is:
+ *
+ * utf8 length = bytecount(utf16) * 1.5
+ */
+void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8);
+/* Convert UTF8 string to UTF16. Rewritten from gpt utility.
+ * Caller must prepare enough space for UTF16. The conservative estimation is:
+ *
+ * utf16 bytecount = bytecount(utf8) / 3 * 4
+ */
+void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16);
+
+/* Helper functions for supported GPT types. */
+int ResolveType(const Guid *type, char *buf);
+int SupportedType(const char *name, Guid *type);
+void PrintTypes(void);
+void EntryDetails(GptEntry *entry, int index, int raw);
+
+uint32_t GetNumberOfEntries(const GptData *gpt);
+GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index);
+void SetPriority(GptData *gpt, int secondary, int entry_index, int priority);
+int GetPriority(GptData *gpt, int secondary, int entry_index);
+void SetTries(GptData *gpt, int secondary, int entry_index, int tries);
+int GetTries(GptData *gpt, int secondary, int entry_index);
+void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success);
+int GetSuccessful(GptData *gpt, int secondary, int entry_index);
+
+uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers);
+uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries);
+void UpdateCrc(GptData *gpt);
+int IsSynonymous(const GptHeader* a, const GptHeader* b);
+
+// For usage and error messages.
+extern const char* progname;
+extern const char* command;
+void Error(const char *format, ...);
+
+
+// Command functions.
+int cmd_show(int argc, char *argv[]);
+int cmd_repair(int argc, char *argv[]);
+int cmd_create(int argc, char *argv[]);
+int cmd_add(int argc, char *argv[]);
+int cmd_boot(int argc, char *argv[]);
+
+#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0]))
+const char *GptError(int errnum);
+
+
+#endif // VBOOT_REFERENCE_UTILITY_CGPT_CGPT_H_
diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
new file mode 100644
index 00000000..f165f7a7
--- /dev/null
+++ b/cgpt/cgpt_common.c
@@ -0,0 +1,726 @@
+/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
+ * files for more details.
+ */
+
+#include "cgpt.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "cgptlib_internal.h"
+#include "crc32.h"
+
+
+void Error(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "ERROR: %s %s: ", progname, command);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+
+int CheckValid(const struct drive *drive) {
+ if ((drive->gpt.valid_headers != MASK_BOTH) ||
+ (drive->gpt.valid_entries != MASK_BOTH)) {
+ fprintf(stderr, "\nWARNING: one of the GPT header/entries is invalid, "
+ "please run '%s repair'\n", progname);
+ return CGPT_FAILED;
+ }
+ return CGPT_OK;
+}
+
+/* Loads sectors from 'fd'.
+ * *buf is pointed to an allocated memory when returned, and should be
+ * freed by cgpt_close().
+ *
+ * fd -- file descriptot.
+ * buf -- pointer to buffer pointer
+ * sector -- offset of starting sector (in sectors)
+ * sector_bytes -- bytes per sector
+ * sector_count -- number of sectors to load
+ *
+ * Returns CGPT_OK for successful. Aborts if any error occurs.
+ */
+static int Load(const int fd, uint8_t **buf,
+ const uint64_t sector,
+ const uint64_t sector_bytes,
+ const uint64_t sector_count) {
+ int count; /* byte count to read */
+ int nread;
+
+ assert(buf);
+ count = sector_bytes * sector_count;
+ *buf = malloc(count);
+ assert(*buf);
+
+ if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
+ goto error_free;
+
+ nread = read(fd, *buf, count);
+ if (nread < count)
+ goto error_free;
+
+ return CGPT_OK;
+
+error_free:
+ free(*buf);
+ *buf = 0;
+ return CGPT_FAILED;
+}
+
+
+int ReadPMBR(struct drive *drive) {
+ if (-1 == lseek(drive->fd, 0, SEEK_SET))
+ return CGPT_FAILED;
+
+ int nread = read(drive->fd, &drive->pmbr, sizeof(struct pmbr));
+ if (nread != sizeof(struct pmbr))
+ return CGPT_FAILED;
+
+ return CGPT_OK;
+}
+
+int WritePMBR(struct drive *drive) {
+ if (-1 == lseek(drive->fd, 0, SEEK_SET))
+ return CGPT_FAILED;
+
+ int nwrote = write(drive->fd, &drive->pmbr, sizeof(struct pmbr));
+ if (nwrote != sizeof(struct pmbr))
+ return CGPT_FAILED;
+
+ return CGPT_OK;
+}
+
+/* Saves sectors to 'fd'.
+ *
+ * fd -- file descriptot.
+ * buf -- pointer to buffer
+ * sector -- starting sector offset
+ * sector_bytes -- bytes per sector
+ * sector_count -- number of sector to save
+ *
+ * Returns CGPT_OK for successful, CGPT_FAILED for failed.
+ */
+static int Save(const int fd, const uint8_t *buf,
+ const uint64_t sector,
+ const uint64_t sector_bytes,
+ const uint64_t sector_count) {
+ int count; /* byte count to write */
+ int nwrote;
+
+ assert(buf);
+ count = sector_bytes * sector_count;
+
+ if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
+ return CGPT_FAILED;
+
+ nwrote = write(fd, buf, count);
+ if (nwrote < count)
+ return CGPT_FAILED;
+
+ return CGPT_OK;
+}
+
+
+// Opens a block device or file, loads raw GPT data from it.
+//
+// Returns CGPT_FAILED if any error happens.
+// Returns CGPT_OK if success and information are stored in 'drive'. */
+int DriveOpen(const char *drive_path, struct drive *drive) {
+ struct stat stat;
+
+ assert(drive_path);
+ assert(drive);
+
+ // Clear struct for proper error handling.
+ memset(drive, 0, sizeof(struct drive));
+
+ drive->fd = open(drive_path, O_RDWR | O_LARGEFILE);
+ if (drive->fd == -1) {
+ Error("Can't open %s: %s\n", drive_path, strerror(errno));
+ return CGPT_FAILED;
+ }
+
+ if (fstat(drive->fd, &stat) == -1) {
+ goto error_close;
+ }
+ if ((stat.st_mode & S_IFMT) != S_IFREG) {
+ if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
+ Error("Can't read drive size from %s: %s\n", drive_path, strerror(errno));
+ goto error_close;
+ }
+ if (ioctl(drive->fd, BLKSSZGET, &drive->gpt.sector_bytes) < 0) {
+ Error("Can't read sector size from %s: %s\n",
+ drive_path, strerror(errno));
+ goto error_close;
+ }
+ } else {
+ drive->gpt.sector_bytes = 512; /* bytes */
+ drive->size = stat.st_size;
+ }
+ if (drive->size % drive->gpt.sector_bytes) {
+ Error("Media size (%llu) is not a multiple of sector size(%d)\n",
+ (long long unsigned int)drive->size, drive->gpt.sector_bytes);
+ goto error_close;
+ }
+ drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
+
+ // Read the data.
+ if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_header,
+ GPT_PMBR_SECTOR,
+ drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
+ goto error_close;
+ }
+ if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_header,
+ drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
+ drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
+ goto error_close;
+ }
+ if (CGPT_OK != Load(drive->fd, &drive->gpt.primary_entries,
+ GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
+ drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
+ goto error_close;
+ }
+ if (CGPT_OK != Load(drive->fd, &drive->gpt.secondary_entries,
+ drive->gpt.drive_sectors - GPT_HEADER_SECTOR
+ - GPT_ENTRIES_SECTORS,
+ drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
+ goto error_close;
+ }
+
+ // We just load the data. Caller must validate it.
+ return CGPT_OK;
+
+error_close:
+ (void) DriveClose(drive, 0);
+ return CGPT_FAILED;
+}
+
+
+int DriveClose(struct drive *drive, int update_as_needed) {
+ int errors = 0;
+
+ if (update_as_needed) {
+ if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
+ if (CGPT_OK != Save(drive->fd, drive->gpt.primary_header,
+ GPT_PMBR_SECTOR,
+ drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
+ errors++;
+ Error("Cannot write primary header: %s\n", strerror(errno));
+ }
+ }
+
+ if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
+ if(CGPT_OK != Save(drive->fd, drive->gpt.secondary_header,
+ drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
+ drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
+ errors++;
+ Error("Cannot write secondary header: %s\n", strerror(errno));
+ }
+ }
+ if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
+ if (CGPT_OK != Save(drive->fd, drive->gpt.primary_entries,
+ GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
+ drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
+ errors++;
+ Error("Cannot write primary entries: %s\n", strerror(errno));
+ }
+ }
+ if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
+ if (CGPT_OK != Save(drive->fd, drive->gpt.secondary_entries,
+ drive->gpt.drive_sectors - GPT_HEADER_SECTOR
+ - GPT_ENTRIES_SECTORS,
+ drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
+ errors++;
+ Error("Cannot write secondary entries: %s\n", strerror(errno));
+ }
+ }
+ }
+
+ close(drive->fd);
+
+ if (drive->gpt.primary_header)
+ free(drive->gpt.primary_header);
+ drive->gpt.primary_header = 0;
+ if (drive->gpt.primary_entries)
+ free(drive->gpt.primary_entries);
+ drive->gpt.primary_entries = 0;
+ if (drive->gpt.secondary_header)
+ free(drive->gpt.secondary_header);
+ drive->gpt.secondary_header = 0;
+ if (drive->gpt.secondary_entries)
+ free(drive->gpt.secondary_entries);
+ drive->gpt.secondary_entries = 0;
+
+ return errors ? CGPT_FAILED : CGPT_OK;
+}
+
+
+
+/* GUID conversion functions. Accepted format:
+ *
+ * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
+ *
+ * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
+ */
+int StrToGuid(const char *str, Guid *guid) {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_high_and_version;
+ unsigned int chunk[11];
+
+ if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ chunk+0,
+ chunk+1,
+ chunk+2,
+ chunk+3,
+ chunk+4,
+ chunk+5,
+ chunk+6,
+ chunk+7,
+ chunk+8,
+ chunk+9,
+ chunk+10)) {
+ printf("FAILED\n");
+ return CGPT_FAILED;
+ }
+
+ time_low = chunk[0] & 0xffffffff;
+ time_mid = chunk[1] & 0xffff;
+ time_high_and_version = chunk[2] & 0xffff;
+
+ guid->u.Uuid.time_low = htole32(time_low);
+ guid->u.Uuid.time_mid = htole16(time_mid);
+ guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
+
+ guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
+ guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
+ guid->u.Uuid.node[0] = chunk[5] & 0xff;
+ guid->u.Uuid.node[1] = chunk[6] & 0xff;
+ guid->u.Uuid.node[2] = chunk[7] & 0xff;
+ guid->u.Uuid.node[3] = chunk[8] & 0xff;
+ guid->u.Uuid.node[4] = chunk[9] & 0xff;
+ guid->u.Uuid.node[5] = chunk[10] & 0xff;
+
+ return CGPT_OK;
+}
+void GuidToStr(const Guid *guid, char *str) {
+ sprintf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ le32toh(guid->u.Uuid.time_low), le16toh(guid->u.Uuid.time_mid),
+ le16toh(guid->u.Uuid.time_high_and_version),
+ guid->u.Uuid.clock_seq_high_and_reserved, guid->u.Uuid.clock_seq_low,
+ guid->u.Uuid.node[0], guid->u.Uuid.node[1], guid->u.Uuid.node[2],
+ guid->u.Uuid.node[3], guid->u.Uuid.node[4], guid->u.Uuid.node[5]);
+}
+
+/* Convert UTF16 string to UTF8. Rewritten from gpt utility.
+ * Caller must prepare enough space for UTF8. The rough estimation is:
+ *
+ * utf8 length = bytecount(utf16) * 1.5
+ */
+#define SIZEOF_GPTENTRY_NAME 36 /* sizeof(GptEntry.name[]) */
+void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8)
+{
+ size_t s8idx, s16idx, s16len;
+ uint32_t utfchar;
+ unsigned int next_utf16;
+
+ for (s16len = 0; s16len < SIZEOF_GPTENTRY_NAME && utf16[s16len]; ++s16len);
+
+ *utf8 = s8idx = s16idx = 0;
+ while (s16idx < s16len) {
+ utfchar = le16toh(utf16[s16idx++]);
+ if ((utfchar & 0xf800) == 0xd800) {
+ next_utf16 = le16toh(utf16[s16idx]);
+ if ((utfchar & 0x400) != 0 || (next_utf16 & 0xfc00) != 0xdc00)
+ utfchar = 0xfffd;
+ else
+ s16idx++;
+ }
+ if (utfchar < 0x80) {
+ utf8[s8idx++] = utfchar;
+ } else if (utfchar < 0x800) {
+ utf8[s8idx++] = 0xc0 | (utfchar >> 6);
+ utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ } else if (utfchar < 0x10000) {
+ utf8[s8idx++] = 0xe0 | (utfchar >> 12);
+ utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
+ utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ } else if (utfchar < 0x200000) {
+ utf8[s8idx++] = 0xf0 | (utfchar >> 18);
+ utf8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
+ utf8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
+ utf8[s8idx++] = 0x80 | (utfchar & 0x3f);
+ }
+ }
+ utf8[s8idx++] = 0;
+}
+
+/* Convert UTF8 string to UTF16. Rewritten from gpt utility.
+ * Caller must prepare enough space for UTF16. The conservative estimation is:
+ *
+ * utf16 bytecount = bytecount(utf8) / 3 * 4
+ */
+void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16)
+{
+ size_t s16idx, s8idx, s8len;
+ uint32_t utfchar;
+ unsigned int c, utfbytes;
+
+ for (s8len = 0; utf8[s8len]; ++s8len);
+
+ s8idx = s16idx = 0;
+ utfbytes = 0;
+ do {
+ c = utf8[s8idx++];
+ if ((c & 0xc0) != 0x80) {
+ /* Initial characters. */
+ if (utfbytes != 0) {
+ /* Incomplete encoding. */
+ utf16[s16idx++] = 0xfffd;
+ }
+ if ((c & 0xf8) == 0xf0) {
+ utfchar = c & 0x07;
+ utfbytes = 3;
+ } else if ((c & 0xf0) == 0xe0) {
+ utfchar = c & 0x0f;
+ utfbytes = 2;
+ } else if ((c & 0xe0) == 0xc0) {
+ utfchar = c & 0x1f;
+ utfbytes = 1;
+ } else {
+ utfchar = c & 0x7f;
+ utfbytes = 0;
+ }
+ } else {
+ /* Followup characters. */
+ if (utfbytes > 0) {
+ utfchar = (utfchar << 6) + (c & 0x3f);
+ utfbytes--;
+ } else if (utfbytes == 0)
+ utfbytes = -1;
+ utfchar = 0xfffd;
+ }
+ if (utfbytes == 0) {
+ if (utfchar >= 0x10000) {
+ utf16[s16idx++] = htole16(0xd800 | ((utfchar>>10)-0x40));
+ if (s16idx >= SIZEOF_GPTENTRY_NAME) break;
+ utf16[s16idx++] = htole16(0xdc00 | (utfchar & 0x3ff));
+ } else {
+ utf16[s16idx++] = htole16(utfchar);
+ }
+ }
+ } while (c != 0 && s16idx < SIZEOF_GPTENTRY_NAME);
+ if (s16idx < SIZEOF_GPTENTRY_NAME)
+ utf16[s16idx++] = 0;
+}
+
+struct {
+ Guid type;
+ char *name;
+ char *description;
+} supported_types[] = {
+ {GPT_ENT_TYPE_CHROMEOS_KERNEL, "kernel", "ChromeOS kernel"},
+ {GPT_ENT_TYPE_CHROMEOS_ROOTFS, "rootfs", "ChromeOS rootfs"},
+ {GPT_ENT_TYPE_LINUX_DATA, "data", "Linux data"},
+ {GPT_ENT_TYPE_CHROMEOS_RESERVED, "reserved", "ChromeOS reserved"},
+ {GPT_ENT_TYPE_EFI, "efi", "EFI System Partition"},
+ {GPT_ENT_TYPE_UNUSED, "unused", "Unused (nonexistent) partition"},
+};
+
+/* Resolves human-readable GPT type.
+ * Returns CGPT_OK if found.
+ * Returns CGPT_FAILED if no known type found. */
+int ResolveType(const Guid *type, char *buf) {
+ int i;
+ for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
+ if (!memcmp(type, &supported_types[i].type, sizeof(Guid))) {
+ strcpy(buf, supported_types[i].description);
+ return CGPT_OK;
+ }
+ }
+ return CGPT_FAILED;
+}
+
+int SupportedType(const char *name, Guid *type) {
+ int i;
+ for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
+ if (!strcmp(name, supported_types[i].name)) {
+ memcpy(type, &supported_types[i].type, sizeof(Guid));
+ return CGPT_OK;
+ }
+ }
+ return CGPT_FAILED;
+}
+
+void PrintTypes(void) {
+ int i;
+ printf("The partition type may also be given as one of these aliases:\n\n");
+ for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
+ printf(" %-10s %s\n", supported_types[i].name,
+ supported_types[i].description);
+ }
+ printf("\n");
+}
+
+uint32_t GetNumberOfEntries(const GptData *gpt) {
+ GptHeader *header = 0;
+ if (gpt->valid_headers & MASK_PRIMARY)
+ header = (GptHeader*)gpt->primary_header;
+ else if (gpt->valid_headers & MASK_SECONDARY)
+ header = (GptHeader*)gpt->secondary_header;
+ else
+ return 0;
+ return header->number_of_entries;
+}
+
+static uint32_t GetSizeOfEntries(const GptData *gpt) {
+ GptHeader *header = 0;
+ if (gpt->valid_headers & MASK_PRIMARY)
+ header = (GptHeader*)gpt->primary_header;
+ else if (gpt->valid_headers & MASK_SECONDARY)
+ header = (GptHeader*)gpt->secondary_header;
+ else
+ return 0;
+ return header->number_of_entries;
+}
+
+GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
+ uint8_t *entries;
+ int stride = GetSizeOfEntries(gpt);
+ if (!stride)
+ return 0;
+
+ if (secondary == PRIMARY) {
+ entries = gpt->primary_entries;
+ } else {
+ entries = gpt->secondary_entries;
+ }
+
+ return (GptEntry*)(&entries[stride * entry_index]);
+}
+
+void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+
+ assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
+ entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
+ entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
+}
+
+int GetPriority(GptData *gpt, int secondary, int entry_index) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+ return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
+ CGPT_ATTRIBUTE_PRIORITY_OFFSET;
+}
+
+void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+
+ assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
+ entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK;
+ entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
+}
+
+int GetTries(GptData *gpt, int secondary, int entry_index) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+ return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
+ CGPT_ATTRIBUTE_TRIES_OFFSET;
+}
+
+void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+
+ assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
+ entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
+ entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
+}
+
+int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, entry_index);
+ return (entry->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
+ CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
+}
+
+
+#define TOSTRING(A) #A
+const char *GptError(int errnum) {
+ const char *error_string[] = {
+ TOSTRING(GPT_SUCCESS),
+ TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
+ TOSTRING(GPT_ERROR_INVALID_HEADERS),
+ TOSTRING(GPT_ERROR_INVALID_ENTRIES),
+ TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
+ TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
+ TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
+ };
+ if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
+ return "<illegal value>";
+ return error_string[errnum];
+}
+
+/* Update CRC value if necessary. */
+void UpdateCrc(GptData *gpt) {
+ GptHeader *primary_header, *secondary_header;
+
+ primary_header = (GptHeader*)gpt->primary_header;
+ secondary_header = (GptHeader*)gpt->secondary_header;
+
+ if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
+ primary_header->entries_crc32 =
+ Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
+ }
+ if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
+ secondary_header->entries_crc32 =
+ Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
+ }
+ if (gpt->modified & GPT_MODIFIED_HEADER1) {
+ primary_header->header_crc32 = 0;
+ primary_header->header_crc32 = Crc32(
+ (const uint8_t *)primary_header, primary_header->size);
+ }
+ if (gpt->modified & GPT_MODIFIED_HEADER2) {
+ secondary_header->header_crc32 = 0;
+ secondary_header->header_crc32 = Crc32(
+ (const uint8_t *)secondary_header, secondary_header->size);
+ }
+}
+/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
+ * itself so that my_lba in primary and secondary is definitely different.
+ * Only the following fields should be identical.
+ *
+ * first_usable_lba
+ * last_usable_lba
+ * number_of_entries
+ * size_of_entry
+ * disk_uuid
+ *
+ * If any of above field are not matched, overwrite secondary with primary since
+ * we always trust primary.
+ * If any one of header is invalid, copy from another. */
+int IsSynonymous(const GptHeader* a, const GptHeader* b) {
+ if ((a->first_usable_lba == b->first_usable_lba) &&
+ (a->last_usable_lba == b->last_usable_lba) &&
+ (a->number_of_entries == b->number_of_entries) &&
+ (a->size_of_entry == b->size_of_entry) &&
+ (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
+ return 1;
+ return 0;
+}
+
+/* Primary entries and secondary entries should be bitwise identical.
+ * If two entries tables are valid, compare them. If not the same,
+ * overwrites secondary with primary (primary always has higher priority),
+ * and marks secondary as modified.
+ * If only one is valid, overwrites invalid one.
+ * If all are invalid, does nothing.
+ * This function returns bit masks for GptData.modified field.
+ * Note that CRC is NOT re-computed in this function.
+ */
+uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
+ if (valid_entries == MASK_BOTH) {
+ if (memcmp(gpt->primary_entries, gpt->secondary_entries,
+ TOTAL_ENTRIES_SIZE)) {
+ memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
+ return GPT_MODIFIED_ENTRIES2;
+ }
+ } else if (valid_entries == MASK_PRIMARY) {
+ memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
+ return GPT_MODIFIED_ENTRIES2;
+ } else if (valid_entries == MASK_SECONDARY) {
+ memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
+ return GPT_MODIFIED_ENTRIES1;
+ }
+
+ return 0;
+}
+
+/* The above five fields are shared between primary and secondary headers.
+ * We can recover one header from another through copying those fields. */
+void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
+ target->first_usable_lba = source->first_usable_lba;
+ target->last_usable_lba = source->last_usable_lba;
+ target->number_of_entries = source->number_of_entries;
+ target->size_of_entry = source->size_of_entry;
+ memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
+}
+
+/* This function repairs primary and secondary headers if possible.
+ * If both headers are valid (CRC32 is correct) but
+ * a) indicate inconsistent usable LBA ranges,
+ * b) inconsistent partition entry size and number,
+ * c) inconsistent disk_uuid,
+ * we will use the primary header to overwrite secondary header.
+ * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
+ * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
+ * This function returns the bitmasks for modified header.
+ * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
+ * do it later.
+ */
+uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
+ GptHeader *primary_header, *secondary_header;
+
+ primary_header = (GptHeader*)gpt->primary_header;
+ secondary_header = (GptHeader*)gpt->secondary_header;
+
+ if (valid_headers == MASK_BOTH) {
+ if (!IsSynonymous(primary_header, secondary_header)) {
+ CopySynonymousParts(secondary_header, primary_header);
+ return GPT_MODIFIED_HEADER2;
+ }
+ } else if (valid_headers == MASK_PRIMARY) {
+ memcpy(secondary_header, primary_header, primary_header->size);
+ secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
+ secondary_header->alternate_lba = primary_header->my_lba;
+ secondary_header->entries_lba = secondary_header->my_lba -
+ GPT_ENTRIES_SECTORS;
+ return GPT_MODIFIED_HEADER2;
+ } else if (valid_headers == MASK_SECONDARY) {
+ memcpy(primary_header, secondary_header, secondary_header->size);
+ primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
+ primary_header->alternate_lba = secondary_header->my_lba;
+ primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
+ return GPT_MODIFIED_HEADER1;
+ }
+
+ return 0;
+}
+
+
+int IsZero(const Guid *gp) {
+ return (0 == memcmp(gp, &guid_unused, sizeof(Guid)));
+}
+
+void PMBRToStr(struct pmbr *pmbr, char *str) {
+ char buf[256];
+ if (IsZero(&pmbr->boot_guid)) {
+ sprintf(str, "PMBR");
+ } else {
+ GuidToStr(&pmbr->boot_guid, buf);
+ sprintf(str, "PMBR (Boot GUID: %s)", buf);
+ }
+}
+
diff --git a/cgpt/cmd_add.c b/cgpt/cmd_add.c
new file mode 100644
index 00000000..18a0285e
--- /dev/null
+++ b/cgpt/cmd_add.c
@@ -0,0 +1,276 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "cgptlib_internal.h"
+
+static void Usage(void)
+{
+ printf("\nUsage: %s add [OPTIONS] DRIVE\n\n"
+ "Add, edit, or remove a partition entry.\n\n"
+ "Options:\n"
+ " -i NUM Specify partition (default is next available)\n"
+ " -b NUM Beginning sector\n"
+ " -s NUM Size in sectors\n"
+ " -t GUID Partition Type GUID\n"
+ " -u GUID Partition Unique ID\n"
+ " -l LABEL Label\n"
+ " -S NUM set Successful flag (0|1)\n"
+ " -T NUM set Tries flag (0-15)\n"
+ " -P NUM set Priority flag (0-15)\n"
+ " -A NUM set raw 64-bit attribute value\n"
+ "\n"
+ "Use the -i option to modify an existing partition.\n"
+ "The -b, -s, and -t options must be given for new partitions.\n"
+ "\n", progname);
+ PrintTypes();
+}
+
+int cmd_add(int argc, char *argv[]) {
+ struct drive drive;
+ int partition = 0;
+ uint64_t begin = 0;
+ uint64_t size = 0;
+ Guid type_guid;
+ Guid unique_guid;
+ char *label = 0;
+ int successful = 0;
+ int tries = 0;
+ int priority = 0;
+ uint64_t raw_value = 0;
+ int set_begin = 0;
+ int set_size = 0;
+ int set_type = 0;
+ int set_unique = 0;
+ int set_successful = 0;
+ int set_tries = 0;
+ int set_priority = 0;
+ int set_raw = 0;
+
+ int gpt_retval;
+ GptEntry *entry;
+ int index;
+
+ int c;
+ int errorcnt = 0;
+ char *e = 0;
+
+ opterr = 0; // quiet, you
+ while ((c=getopt(argc, argv, ":hi:b:s:t:u:l:S:T:P:A:")) != -1)
+ {
+ switch (c)
+ {
+ case 'i':
+ partition = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 'b':
+ set_begin = 1;
+ begin = strtoull(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 's':
+ set_size = 1;
+ size = strtoull(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 't':
+ set_type = 1;
+ if (CGPT_OK != SupportedType(optarg, &type_guid) &&
+ CGPT_OK != StrToGuid(optarg, &type_guid)) {
+ Error("invalid argument to -%c: %s\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 'u':
+ set_unique = 1;
+ if (CGPT_OK != StrToGuid(optarg, &unique_guid)) {
+ Error("invalid argument to -%c: %s\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 'l':
+ label = optarg;
+ break;
+ case 'S':
+ set_successful = 1;
+ successful = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ if (successful < 0 || successful > 1) {
+ Error("value for -%c must be between 0 and 1", c);
+ errorcnt++;
+ }
+ break;
+ case 'T':
+ set_tries = 1;
+ tries = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
+ progname, c, optarg);
+ errorcnt++;
+ }
+ if (tries < 0 || tries > 15) {
+ Error("value for -%c must be between 0 and 15", c);
+ errorcnt++;
+ }
+ break;
+ case 'P':
+ set_priority = 1;
+ priority = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ if (priority < 0 || priority > 15) {
+ Error("value for -%c must be between 0 and 15", c);
+ errorcnt++;
+ }
+ break;
+ case 'A':
+ set_raw = 1;
+ raw_value = strtoull(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+
+ case 'h':
+ Usage();
+ return CGPT_OK;
+ case '?':
+ Error("unrecognized option: -%c\n", optopt);
+ errorcnt++;
+ break;
+ case ':':
+ Error("missing argument to -%c\n", optopt);
+ errorcnt++;
+ break;
+ default:
+ errorcnt++;
+ break;
+ }
+ }
+ if (errorcnt)
+ {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (optind >= argc) {
+ Error("missing drive argument\n");
+ return CGPT_FAILED;
+ }
+
+ if (CGPT_OK != DriveOpen(argv[optind], &drive))
+ return CGPT_FAILED;
+
+ if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
+ Error("GptSanityCheck() returned %d: %s\n",
+ gpt_retval, GptError(gpt_retval));
+ return CGPT_FAILED;
+ }
+
+ int max_part = GetNumberOfEntries(&drive.gpt);
+ if (partition) {
+ if (partition > max_part) {
+ Error("invalid partition number: %d\n", partition);
+ goto bad;
+ }
+ index = partition - 1;
+ entry = GetEntry(&drive.gpt, PRIMARY, index);
+ } else {
+ // find next empty partition
+ for (index = 0; index < max_part; index++) {
+ entry = GetEntry(&drive.gpt, PRIMARY, index);
+ if (IsZero(&entry->type)) {
+ partition = index + 1;
+ break;
+ }
+ }
+ if (index >= max_part) {
+ Error("no unused partitions available\n");
+ goto bad;
+ }
+ }
+
+ // New partitions must specify type, begin, and size.
+ if (IsZero(&entry->type)) {
+ if (!set_begin || !set_size || !set_type) {
+ Error("-t, -b, and -s options are required for new partitions\n");
+ goto bad;
+ }
+ if (IsZero(&type_guid)) {
+ Error("New partitions must have a type other than \"unused\"\n");
+ goto bad;
+ }
+ if (!set_unique)
+ uuid_generate((uint8_t *)&entry->unique);
+ }
+
+ if (set_begin)
+ entry->starting_lba = begin;
+ if (set_size)
+ entry->ending_lba = begin + size - 1;
+ if (set_type)
+ memcpy(&entry->type, &type_guid, sizeof(Guid));
+ if (set_unique)
+ memcpy(&entry->unique, &unique_guid, sizeof(Guid));
+ if (label) {
+ uint16_t buf[128];
+ UTF8ToUTF16((uint8_t *)label, buf);
+ memcpy(entry->name, buf, sizeof(entry->name));
+ }
+ if (set_raw) {
+ entry->attributes = raw_value;
+ } else {
+ if (set_successful)
+ SetSuccessful(&drive.gpt, PRIMARY, index, successful);
+ if (set_tries)
+ SetTries(&drive.gpt, PRIMARY, index, tries);
+ if (set_priority)
+ SetPriority(&drive.gpt, PRIMARY, index, priority);
+ }
+
+ RepairEntries(&drive.gpt, MASK_PRIMARY);
+ RepairHeader(&drive.gpt, MASK_PRIMARY);
+
+ drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
+ GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
+ UpdateCrc(&drive.gpt);
+
+
+ // Write it all out
+ return DriveClose(&drive, 1);
+
+bad:
+ (void) DriveClose(&drive, 0);
+ return CGPT_FAILED;
+}
diff --git a/cgpt/cmd_boot.c b/cgpt/cmd_boot.c
new file mode 100644
index 00000000..784684a2
--- /dev/null
+++ b/cgpt/cmd_boot.c
@@ -0,0 +1,167 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#include "cgptlib_internal.h"
+#include "endian.h"
+
+static void Usage(void)
+{
+ printf("\nUsage: %s boot [OPTIONS] DRIVE\n\n"
+ "Edit the PMBR sector for legacy BIOSes\n\n"
+ "Options:\n"
+ " -i NUM Set bootable partition\n"
+ " -b FILE Install bootloader code in the PMBR\n"
+ " -p Create legacy PMBR partition table\n"
+ "\n"
+ "With no options, it will just print the PMBR boot guid\n"
+ "\n", progname);
+}
+
+
+int cmd_boot(int argc, char *argv[]) {
+ struct drive drive;
+ int partition = 0;
+ char *bootfile = 0;
+ int create_pmbr = 0;
+ int retval = 1;
+ int gpt_retval;
+
+ int c;
+ int errorcnt = 0;
+ char *e = 0;
+
+ opterr = 0; // quiet, you
+ while ((c=getopt(argc, argv, ":hi:b:p")) != -1)
+ {
+ switch (c)
+ {
+ case 'i':
+ partition = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 'b':
+ bootfile = optarg;
+ break;
+ case 'p':
+ create_pmbr = 1;
+ break;
+
+ case 'h':
+ Usage();
+ return CGPT_OK;
+ case '?':
+ Error("unrecognized option: -%c\n", optopt);
+ errorcnt++;
+ break;
+ case ':':
+ Error("missing argument to -%c\n", optopt);
+ errorcnt++;
+ break;
+ default:
+ errorcnt++;
+ break;
+ }
+ }
+ if (errorcnt)
+ {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (optind >= argc) {
+ Error("missing drive argument\n");
+ return CGPT_FAILED;
+ }
+
+ if (CGPT_OK != DriveOpen(argv[optind], &drive))
+ return CGPT_FAILED;
+
+ if (CGPT_OK != ReadPMBR(&drive)) {
+ Error("Unable to read PMBR\n");
+ goto done;
+ }
+
+ if (create_pmbr) {
+ drive.pmbr.magic[0] = 0x1d;
+ drive.pmbr.magic[1] = 0x9a;
+ drive.pmbr.sig[0] = 0x55;
+ drive.pmbr.sig[1] = 0xaa;
+ memset(&drive.pmbr.part, 0, sizeof(drive.pmbr.part));
+ drive.pmbr.part[0].f_head = 0x00;
+ drive.pmbr.part[0].f_sect = 0x02;
+ drive.pmbr.part[0].f_cyl = 0x00;
+ drive.pmbr.part[0].type = 0xee;
+ drive.pmbr.part[0].l_head = 0xff;
+ drive.pmbr.part[0].l_sect = 0xff;
+ drive.pmbr.part[0].l_cyl = 0xff;
+ drive.pmbr.part[0].f_lba = htole32(1);
+ uint32_t max = 0xffffffff;
+ if (drive.gpt.drive_sectors < 0xffffffff)
+ max = drive.gpt.drive_sectors - 1;
+ drive.pmbr.part[0].num_sect = htole32(max);
+ }
+
+ if (partition) {
+ if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
+ Error("GptSanityCheck() returned %d: %s\n",
+ gpt_retval, GptError(gpt_retval));
+ goto done;
+ }
+
+ if (partition > GetNumberOfEntries(&drive.gpt)) {
+ Error("invalid partition number: %d\n", partition);
+ goto done;
+ }
+
+ int index = partition - 1;
+ GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
+ memcpy(&drive.pmbr.boot_guid, &entry->unique, sizeof(Guid));
+ }
+
+ if (bootfile) {
+ int fd = open(bootfile, O_RDONLY);
+ if (fd < 0) {
+ Error("Can't read %s: %s\n", bootfile, strerror(errno));
+ goto done;
+ }
+
+ int n = read(fd, drive.pmbr.bootcode, sizeof(drive.pmbr.bootcode));
+ if (n < 1) {
+ Error("problem reading %s: %s\n", bootfile, strerror(errno));
+ close(fd);
+ goto done;
+ }
+
+ close(fd);
+ }
+
+ char buf[256];
+ GuidToStr(&drive.pmbr.boot_guid, buf);
+ printf("%s\n", buf);
+
+ // Write it all out
+ if (CGPT_OK == WritePMBR(&drive))
+ retval = 0;
+
+done:
+ (void) DriveClose(&drive, 1);
+ return retval;
+}
diff --git a/cgpt/cmd_create.c b/cgpt/cmd_create.c
new file mode 100644
index 00000000..4e6eb653
--- /dev/null
+++ b/cgpt/cmd_create.c
@@ -0,0 +1,106 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "cgptlib_internal.h"
+
+static void Usage(void)
+{
+ printf("\nUsage: %s create [OPTIONS] DRIVE\n\n"
+ "Create or reset an empty GPT.\n\n"
+ "Options:\n"
+ " -z Zero the sectors of the GPT table and entries\n"
+ "\n", progname);
+}
+
+int cmd_create(int argc, char *argv[]) {
+ struct drive drive;
+ int zap = 0;
+
+ int c;
+ int errorcnt = 0;
+
+ opterr = 0; // quiet, you
+ while ((c=getopt(argc, argv, ":hz")) != -1)
+ {
+ switch (c)
+ {
+ case 'z':
+ zap = 1;
+ break;
+
+ case 'h':
+ Usage();
+ return CGPT_OK;
+ case '?':
+ Error("unrecognized option: -%c\n", optopt);
+ errorcnt++;
+ break;
+ case ':':
+ Error("missing argument to -%c\n", optopt);
+ errorcnt++;
+ break;
+ default:
+ errorcnt++;
+ break;
+ }
+ }
+ if (errorcnt)
+ {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (optind >= argc) {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (CGPT_OK != DriveOpen(argv[optind], &drive))
+ return CGPT_FAILED;
+
+ // Erase the data
+ memset(drive.gpt.primary_header, 0,
+ drive.gpt.sector_bytes * GPT_HEADER_SECTOR);
+ memset(drive.gpt.secondary_header, 0,
+ drive.gpt.sector_bytes * GPT_HEADER_SECTOR);
+ memset(drive.gpt.primary_entries, 0,
+ drive.gpt.sector_bytes * GPT_ENTRIES_SECTORS);
+ memset(drive.gpt.secondary_entries, 0,
+ drive.gpt.sector_bytes * GPT_ENTRIES_SECTORS);
+
+ drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
+ GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
+
+ // Initialize a blank set
+ if (!zap)
+ {
+ GptHeader *h = (GptHeader *)drive.gpt.primary_header;
+ memcpy(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE);
+ h->revision = GPT_HEADER_REVISION;
+ h->size = sizeof(GptHeader);
+ h->my_lba = 1;
+ h->first_usable_lba = 1 + 1 + GPT_ENTRIES_SECTORS;
+ h->last_usable_lba = drive.gpt.drive_sectors - 1 - GPT_ENTRIES_SECTORS - 1;
+ uuid_generate((uint8_t *)&h->disk_uuid);
+ h->entries_lba = 2;
+ h->number_of_entries = 128;
+ h->size_of_entry = sizeof(GptEntry);
+
+ // Copy to secondary
+ RepairHeader(&drive.gpt, MASK_PRIMARY);
+
+ UpdateCrc(&drive.gpt);
+ }
+
+ // Write it all out
+ return DriveClose(&drive, 1);
+}
diff --git a/cgpt/cmd_repair.c b/cgpt/cmd_repair.c
new file mode 100644
index 00000000..aafdc938
--- /dev/null
+++ b/cgpt/cmd_repair.c
@@ -0,0 +1,85 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cgptlib_internal.h"
+
+static void Usage(void)
+{
+ printf("\nUsage: %s repair [OPTIONS] DRIVE\n\n"
+ "Repair damaged GPT headers and tables.\n\n"
+ "Options:\n"
+ " -v Verbose\n"
+ "\n", progname);
+}
+
+int cmd_repair(int argc, char *argv[]) {
+ struct drive drive;
+ int verbose = 0;
+
+ int c;
+ int errorcnt = 0;
+
+ opterr = 0; // quiet, you
+ while ((c=getopt(argc, argv, ":hv")) != -1)
+ {
+ switch (c)
+ {
+ case 'v':
+ verbose++;
+ break;
+
+ case 'h':
+ Usage();
+ return CGPT_OK;
+ case '?':
+ Error("unrecognized option: -%c\n", optopt);
+ errorcnt++;
+ break;
+ case ':':
+ Error("missing argument to -%c\n", optopt);
+ errorcnt++;
+ break;
+ default:
+ errorcnt++;
+ break;
+ }
+ }
+ if (errorcnt)
+ {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (optind >= argc) {
+ Error("missing drive argument\n");
+ return CGPT_FAILED;
+ }
+
+ if (CGPT_OK != DriveOpen(argv[optind], &drive))
+ return CGPT_FAILED;
+
+ int gpt_retval = GptSanityCheck(&drive.gpt);
+ if (verbose)
+ printf("GptSanityCheck() returned %d: %s\n",
+ gpt_retval, GptError(gpt_retval));
+
+ GptRepair(&drive.gpt);
+ if (drive.gpt.modified & GPT_MODIFIED_HEADER1)
+ printf("Primary Header is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_ENTRIES1)
+ printf("Primary Entries is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_ENTRIES2)
+ printf("Secondary Entries is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_HEADER2)
+ printf("Secondary Header is updated.\n");
+
+ return DriveClose(&drive, 1);
+}
diff --git a/cgpt/cmd_show.c b/cgpt/cmd_show.c
new file mode 100644
index 00000000..9f52cb1d
--- /dev/null
+++ b/cgpt/cmd_show.c
@@ -0,0 +1,413 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cgpt.h"
+
+#define __STDC_FORMAT_MACROS
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cgptlib_internal.h"
+
+static void Usage(void)
+{
+ printf("\nUsage: %s show [OPTIONS] DRIVE\n\n"
+ "Display the GPT table\n\n"
+ "Options:\n"
+ " -n Numeric output only\n"
+ " -v Verbose output\n"
+ " -q Quick output\n"
+ " -i NUM Show specified partition only - pick one of:\n"
+ " -b beginning sector\n"
+ " -s partition size\n"
+ " -t type guid\n"
+ " -u unique guid\n"
+ " -l label\n"
+ " -S Successful flag\n"
+ " -T Tries flag\n"
+ " -P Priority flag\n"
+ " -A raw 64-bit attribute value\n"
+ "\n", progname);
+}
+
+
+/* Generate output like:
+ *
+ * [AB-CD-EF-01] for group = 1
+ * [ABCD-EF01] for group = 3 (low byte first)
+ *
+ * Needs (size*3-1+3) bytes of space in 'buf' (included the tailing '\0').
+ */
+#define BUFFER_SIZE(size) (size *3 - 1 + 3)
+static short Uint8To2Chars(const uint8_t t) {
+ int h = t >> 4;
+ int l = t & 0xf;
+ h = (h >= 0xA) ? h - 0xA + 'A' : h + '0';
+ l = (l >= 0xA) ? l - 0xA + 'A' : l + '0';
+ return (h << 8) + l;
+}
+static void RawDump(const uint8_t *memory, const int size,
+ char *buf, int group) {
+ int i, outlen = 0;
+ buf[outlen++] = '[';
+ for (i = 0; i < size; ++i) {
+ short c2 = Uint8To2Chars(memory[i]);
+ buf[outlen++] = c2 >> 8;
+ buf[outlen++] = c2 & 0xff;
+ if (i != (size - 1) && ((i + 1) % group) == 0)
+ buf[outlen++] = '-';
+ }
+ buf[outlen++] = ']';
+ buf[outlen++] = '\0';
+}
+
+/* Output formatters */
+
+
+
+#define TITLE_FMT "%10s%10s%8s %s\n"
+#define GPT_FMT "%10d%10d%8s %s\n"
+#define GPT_MORE "%10s%10s%8s ", "", "", ""
+#define PARTITION_FMT "%10d%10d%8d %s\n"
+#define PARTITION_MORE "%10s%10s%8s %s%s\n", "", "", ""
+
+static void HeaderDetails(GptHeader *header, const char *indent, int raw) {
+ int i;
+
+ printf("%sSig: ", indent);
+ if (!raw) {
+ printf("[");
+ for (i = 0; i < sizeof(header->signature); ++i)
+ printf("%c", header->signature[i]);
+ printf("]");
+ } else {
+ char buf[BUFFER_SIZE(sizeof(header->signature))];
+ RawDump((uint8_t *)header->signature, sizeof(header->signature), buf, 1);
+ printf("%s", buf);
+ }
+ printf("\n");
+
+ printf("%sRev: 0x%08x\n", indent, header->revision);
+ printf("%sSize: %d\n", indent, header->size);
+ printf("%sHeader CRC: 0x%08x\n", indent, header->header_crc32);
+ printf("%sMy LBA: %lld\n", indent, (long long)header->my_lba);
+ printf("%sFirst LBA: %lld\n", indent, (long long)header->first_usable_lba);
+ printf("%sLast LBA: %lld\n", indent, (long long)header->last_usable_lba);
+
+ { /* For disk guid */
+ char buf[GUID_STRLEN];
+ GuidToStr(&header->disk_uuid, buf);
+ printf("%sDisk UUID: %s\n", indent, buf);
+ }
+
+ printf("%sEntries LBA: %lld\n", indent, (long long)header->entries_lba);
+ printf("%sNumber of entries: %d\n", indent, header->number_of_entries);
+ printf("%sSize of entry: %d\n", indent, header->size_of_entry);
+ printf("%sEntries CRC: 0x%08x\n", indent, header->entries_crc32);
+}
+
+void EntryDetails(GptEntry *entry, int index, int raw) {
+ char contents[256];
+ uint8_t label[sizeof(entry->name) * 3 / 2];
+
+ if (!raw) {
+ char type[GUID_STRLEN], unique[GUID_STRLEN];;
+
+ UTF16ToUTF8(entry->name, label);
+ snprintf(contents, sizeof(contents), "Label: \"%s\"", label);
+ printf(PARTITION_FMT, (int)entry->starting_lba,
+ (int)(entry->ending_lba - entry->starting_lba + 1),
+ index+1, contents);
+ if (CGPT_OK == ResolveType(&entry->type, type)) {
+ printf(PARTITION_MORE, "Type: ", type);
+ } else {
+ GuidToStr(&entry->type, type);
+ printf(PARTITION_MORE, "Type: ", type);
+ }
+ GuidToStr(&entry->unique, unique);
+ printf(PARTITION_MORE, "UUID: ", unique);
+ if (!memcmp(&guid_chromeos_kernel, &entry->type, sizeof(Guid))) {
+ int tries = (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
+ CGPT_ATTRIBUTE_TRIES_OFFSET;
+ int successful = (entry->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
+ CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
+ int priority = (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
+ CGPT_ATTRIBUTE_PRIORITY_OFFSET;
+ snprintf(contents, sizeof(contents),
+ "priority=%d tries=%d successful=%d",
+ priority, tries, successful);
+ printf(PARTITION_MORE, "Attr: ", contents);
+ }
+ } else {
+ char type[GUID_STRLEN], unique[GUID_STRLEN];
+
+ UTF16ToUTF8(entry->name, label);
+ snprintf(contents, sizeof(contents), "Label: \"%s\"", label);
+ printf(PARTITION_FMT, (int)entry->starting_lba,
+ (int)(entry->ending_lba - entry->starting_lba + 1),
+ index+1, contents);
+ GuidToStr(&entry->type, type);
+ printf(PARTITION_MORE, "Type: ", type);
+ GuidToStr(&entry->unique, unique);
+ printf(PARTITION_MORE, "UUID: ", unique);
+ snprintf(contents, sizeof(contents), "[%" PRIx64 "]", entry->attributes);
+ printf(PARTITION_MORE, "Attr: ", contents);
+ }
+}
+
+
+void EntriesDetails(GptData *gpt, const int secondary, int raw) {
+ int i;
+
+ for (i = 0; i < GetNumberOfEntries(gpt); ++i) {
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, i);
+
+ if (!memcmp(&guid_unused, &entry->type, sizeof(Guid))) continue;
+
+ EntryDetails(entry, i, raw);
+ }
+}
+
+int cmd_show(int argc, char *argv[]) {
+ struct drive drive;
+ int numeric = 0;
+ int verbose = 0;
+ int quick = 0;
+ int partition = 0;
+ int single_item = 0;
+ int gpt_retval;
+
+ int c;
+ int errorcnt = 0;
+ char *e = 0;
+
+ opterr = 0; // quiet, you
+ while ((c=getopt(argc, argv, ":hnvqi:bstulSTPA")) != -1)
+ {
+ switch (c)
+ {
+ case 'n':
+ numeric = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'q':
+ quick = 1;
+ break;
+ case 'i':
+ partition = (uint32_t)strtoul(optarg, &e, 0);
+ if (!*optarg || (e && *e))
+ {
+ Error("invalid argument to -%c: \"%s\"\n", c, optarg);
+ errorcnt++;
+ }
+ break;
+ case 'b':
+ case 's':
+ case 't':
+ case 'u':
+ case 'l':
+ case 'S':
+ case 'T':
+ case 'P':
+ case 'A':
+ single_item = c;
+ break;
+
+ case 'h':
+ Usage();
+ return CGPT_OK;
+ case '?':
+ Error("unrecognized option: -%c\n", optopt);
+ errorcnt++;
+ break;
+ case ':':
+ Error("missing argument to -%c\n", optopt);
+ errorcnt++;
+ break;
+ default:
+ errorcnt++;
+ break;
+ }
+ }
+ if (errorcnt)
+ {
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (optind >= argc) {
+ Error("missing drive argument\n");
+ Usage();
+ return CGPT_FAILED;
+ }
+
+ if (CGPT_OK != DriveOpen(argv[optind], &drive))
+ return CGPT_FAILED;
+
+ if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
+ Error("GptSanityCheck() returned %d: %s\n",
+ gpt_retval, GptError(gpt_retval));
+ return CGPT_FAILED;
+ }
+
+ if (partition) { // show single partition
+
+ if (partition > GetNumberOfEntries(&drive.gpt)) {
+ Error("invalid partition number: %d\n", partition);
+ return CGPT_FAILED;
+ }
+
+ int index = partition - 1;
+ GptEntry *entry = GetEntry(&drive.gpt, PRIMARY, index);
+ char buf[256];
+
+ if (single_item) {
+ switch(single_item) {
+ case 'b':
+ printf("%" PRId64 "\n", entry->starting_lba);
+ break;
+ case 's':
+ printf("%" PRId64 "\n", entry->ending_lba - entry->starting_lba + 1);
+ break;
+ case 't':
+ GuidToStr(&entry->type, buf);
+ printf("%s\n", buf);
+ break;
+ case 'u':
+ GuidToStr(&entry->unique, buf);
+ printf("%s\n", buf);
+ break;
+ case 'l':
+ UTF16ToUTF8(entry->name, (uint8_t *)buf);
+ printf("%s\n", buf);
+ break;
+ case 'S':
+ printf("%d\n", GetSuccessful(&drive.gpt, PRIMARY, index));
+ break;
+ case 'T':
+ printf("%d\n", GetTries(&drive.gpt, PRIMARY, index));
+ break;
+ case 'P':
+ printf("%d\n", GetPriority(&drive.gpt, PRIMARY, index));
+ break;
+ case 'A':
+ printf("0x%" PRIx64 "\n", entry->attributes);
+ break;
+ }
+ } else {
+ printf(TITLE_FMT, "start", "size", "part", "contents");
+ EntryDetails(entry, index, numeric);
+ }
+
+ } else if (quick) { // show all partitions, quickly
+ int i;
+ GptEntry *entry;
+ char type[GUID_STRLEN];
+
+ for (i = 0; i < GetNumberOfEntries(&drive.gpt); ++i) {
+ entry = GetEntry(&drive.gpt, PRIMARY, i);
+
+ if (IsZero(&entry->type))
+ continue;
+
+ if (!numeric && CGPT_OK == ResolveType(&entry->type, type)) {
+ } else {
+ GuidToStr(&entry->type, type);
+ }
+ printf(PARTITION_FMT, (int)entry->starting_lba,
+ (int)(entry->ending_lba - entry->starting_lba + 1),
+ i+1, type);
+ }
+
+ } else { // show all partitions
+
+ if (CGPT_OK != ReadPMBR(&drive)) {
+ Error("Unable to read PMBR\n");
+ return CGPT_FAILED;
+ }
+
+ printf(TITLE_FMT, "start", "size", "part", "contents");
+ char buf[256];
+ PMBRToStr(&drive.pmbr, buf);
+ printf(GPT_FMT, 0, GPT_PMBR_SECTOR, "", buf);
+
+ if (drive.gpt.valid_headers & MASK_PRIMARY) {
+ printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
+ (int)GPT_HEADER_SECTOR, "", "Pri GPT header");
+ if (verbose) {
+ GptHeader *header;
+ char indent[64];
+
+ snprintf(indent, sizeof(indent), GPT_MORE);
+ header = (GptHeader*)drive.gpt.primary_header;
+ HeaderDetails(header, indent, numeric);
+ }
+ } else {
+ printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
+ (int)GPT_HEADER_SECTOR, "INVALID", "Pri GPT header");
+ }
+
+ printf(GPT_FMT, (int)(GPT_PMBR_SECTOR + GPT_HEADER_SECTOR),
+ (int)GPT_ENTRIES_SECTORS,
+ drive.gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
+ "Pri GPT table");
+
+ if (drive.gpt.valid_entries & MASK_PRIMARY)
+ EntriesDetails(&drive.gpt, PRIMARY, numeric);
+
+ printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR -
+ GPT_ENTRIES_SECTORS),
+ (int)GPT_ENTRIES_SECTORS,
+ drive.gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
+ "Sec GPT table");
+ /* We show secondary table details if any of following is true.
+ * 1. only secondary is valid.
+ * 2. secondary is not identical to promary.
+ */
+ if ((drive.gpt.valid_entries & MASK_SECONDARY) &&
+ (!(drive.gpt.valid_entries & MASK_PRIMARY) ||
+ memcmp(drive.gpt.primary_entries, drive.gpt.secondary_entries,
+ TOTAL_ENTRIES_SIZE))) {
+ EntriesDetails(&drive.gpt, SECONDARY, numeric);
+ }
+
+ if (drive.gpt.valid_headers & MASK_SECONDARY)
+ printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR),
+ (int)GPT_HEADER_SECTOR, "", "Sec GPT header");
+ else
+ printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
+ (int)GPT_HEADER_SECTOR, "INVALID", "Sec GPT header");
+ /* We show secondary header if any of following is true:
+ * 1. only secondary is valid.
+ * 2. secondary is not synonymous to primary.
+ */
+ if ((drive.gpt.valid_headers & MASK_SECONDARY) &&
+ (!(drive.gpt.valid_headers & MASK_PRIMARY) ||
+ !IsSynonymous((GptHeader*)drive.gpt.primary_header,
+ (GptHeader*)drive.gpt.secondary_header))) {
+ if (verbose) {
+ GptHeader *header;
+ char indent[64];
+
+ snprintf(indent, sizeof(indent), GPT_MORE);
+ header = (GptHeader*)drive.gpt.secondary_header;
+ HeaderDetails(header, indent, numeric);
+ }
+ }
+ }
+
+ (void) CheckValid(&drive);
+ (void) DriveClose(&drive, 0);
+
+ return CGPT_OK;
+}
+
+
+
diff --git a/cgpt/endian.h b/cgpt/endian.h
new file mode 100644
index 00000000..ab62ebc9
--- /dev/null
+++ b/cgpt/endian.h
@@ -0,0 +1,44 @@
+#ifndef VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_
+#define VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_
+
+// Newer distros already have this. For those that don't, we add it here.
+#include <endian.h>
+
+#ifndef le16toh
+
+# include <byteswap.h>
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define htobe16(x) __bswap_16 (x)
+# define htole16(x) (x)
+# define be16toh(x) __bswap_16 (x)
+# define le16toh(x) (x)
+
+# define htobe32(x) __bswap_32 (x)
+# define htole32(x) (x)
+# define be32toh(x) __bswap_32 (x)
+# define le32toh(x) (x)
+
+# define htobe64(x) __bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) __bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe16(x) (x)
+# define htole16(x) __bswap_16 (x)
+# define be16toh(x) (x)
+# define le16toh(x) __bswap_16 (x)
+
+# define htobe32(x) (x)
+# define htole32(x) __bswap_32 (x)
+# define be32toh(x) (x)
+# define le32toh(x) __bswap_32 (x)
+
+# define htobe64(x) (x)
+# define htole64(x) __bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) __bswap_64 (x)
+# endif
+
+#endif
+#endif // VBOOT_REFERENCE_UTILITY_CGPT_ENDIAN_H_