summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Yung-Chieh Lo <yjlou@chromium.org>2010-05-27 11:21:17 +0800
committerLouis Yung-Chieh Lo <yjlou@chromium.org>2010-05-27 11:21:17 +0800
commit418ad3b620fe71fa26c1f04154497b4b450c3923 (patch)
tree2c11e966121215ecf50ba28c3837de89fb6b3e62
parentbcd8f4a07c664d55b67d4e8dc35e362b50b376bd (diff)
downloadvboot-418ad3b620fe71fa26c1f04154497b4b450c3923.tar.gz
complete 'cgpt show' and refactor for incoming commands.
Sorry for late. I spent some time to handle Guid endian issue and UTF16/UTF8 consversion. Also, refactored code for incoming commands. Review URL: http://codereview.chromium.org/2231002
-rw-r--r--cgptlib/cgptlib.c145
-rw-r--r--cgptlib/cgptlib.h3
-rw-r--r--cgptlib/cgptlib_internal.h13
-rw-r--r--cgptlib/tests/cgptlib_test.c14
-rw-r--r--utility/cgpt/Makefile2
-rw-r--r--utility/cgpt/cgpt.c164
-rw-r--r--utility/cgpt/cgpt.h25
-rw-r--r--utility/cgpt/cgpt_attribute.c2
-rw-r--r--utility/cgpt/cgpt_repair.c10
-rw-r--r--utility/cgpt/cgpt_show.c282
10 files changed, 538 insertions, 122 deletions
diff --git a/cgptlib/cgptlib.c b/cgptlib/cgptlib.c
index 9a0f784b..a871b1aa 100644
--- a/cgptlib/cgptlib.c
+++ b/cgptlib/cgptlib.c
@@ -243,7 +243,6 @@ uint32_t CheckValidUsableLbas(GptData *gpt) {
/* Checks header CRC */
uint32_t CheckHeaderCrc(GptData *gpt) {
uint32_t crc32, original_crc32;
- uint32_t valid_headers = MASK_BOTH;
GptHeader *headers[] = {
(GptHeader*)gpt->primary_header,
(GptHeader*)gpt->secondary_header,
@@ -251,20 +250,20 @@ uint32_t CheckHeaderCrc(GptData *gpt) {
int i;
for (i = PRIMARY; i <= SECONDARY; ++i) {
+ if (!(gpt->valid_headers & (1 << i))) continue;
original_crc32 = headers[i]->header_crc32;
headers[i]->header_crc32 = 0;
crc32 = Crc32((const uint8_t *)headers[i], headers[i]->size);
headers[i]->header_crc32 = original_crc32;
if (crc32 != original_crc32)
- INVALIDATE_HEADER(valid_headers, i);
+ INVALIDATE_HEADER(gpt->valid_headers, i);
}
- return valid_headers;
+ return gpt->valid_headers;
}
/* Checks entries CRC */
uint32_t CheckEntriesCrc(GptData *gpt) {
uint32_t crc32;
- uint32_t valid_entries = MASK_BOTH;
GptHeader *headers[] = {
(GptHeader*)gpt->primary_header,
(GptHeader*)gpt->secondary_header,
@@ -273,14 +272,20 @@ uint32_t CheckEntriesCrc(GptData *gpt) {
(GptEntry*)gpt->primary_entries,
(GptEntry*)gpt->secondary_entries,
};
+ uint32_t entries_crc32;
int i;
+ if (gpt->valid_headers & MASK_PRIMARY)
+ entries_crc32 = headers[PRIMARY]->entries_crc32;
+ else
+ entries_crc32 = headers[SECONDARY]->entries_crc32;
+
for (i = PRIMARY; i <= SECONDARY; ++i) {
crc32 = Crc32((const uint8_t *)entries[i], TOTAL_ENTRIES_SIZE);
- if (crc32 != headers[i]->entries_crc32)
- INVALIDATE_HEADER(valid_entries, i);
+ if (crc32 != entries_crc32)
+ INVALIDATE_ENTRIES(gpt->valid_entries, i);
}
- return valid_entries;
+ return gpt->valid_entries;
}
/* Returns non-zero if the given GUID is non-zero. */
@@ -305,20 +310,31 @@ uint32_t CheckValidEntries(GptData *gpt) {
(GptEntry*)gpt->primary_entries,
(GptEntry*)gpt->secondary_entries,
};
+ uint32_t number_of_entries, size_of_entry;
+ uint64_t first_usable_lba, last_usable_lba;
int copy, entry_index;
GptEntry *entry;
+ if (gpt->valid_headers & MASK_PRIMARY)
+ copy = PRIMARY;
+ else
+ copy = SECONDARY;
+ number_of_entries = headers[copy]->number_of_entries;
+ size_of_entry = headers[copy]->size_of_entry;
+ first_usable_lba = headers[copy]->first_usable_lba;
+ last_usable_lba = headers[copy]->last_usable_lba;
+
for (copy = PRIMARY; copy <= SECONDARY; ++copy) {
for (entry_index = 0;
- entry_index < headers[copy]->number_of_entries;
+ entry_index < number_of_entries;
++entry_index) {
entry = (GptEntry*)&(((uint8_t*)entries[copy])
- [entry_index * headers[copy]->size_of_entry]);
+ [entry_index * size_of_entry]);
if (NonZeroGuid(&entry->type)) {
- if ((entry->starting_lba < headers[copy]->first_usable_lba) ||
- (entry->ending_lba > headers[copy]->last_usable_lba) ||
+ if ((entry->starting_lba < first_usable_lba) ||
+ (entry->ending_lba > last_usable_lba) ||
(entry->ending_lba < entry->starting_lba))
- INVALIDATE_HEADER(valid_entries, copy);
+ INVALIDATE_ENTRIES(valid_entries, copy);
}
}
}
@@ -370,9 +386,15 @@ uint32_t CheckOverlappedPartition(GptData *gpt) {
(GptEntry*)gpt->secondary_entries,
};
int i;
+ uint32_t number_of_entries;
+
+ if (gpt->valid_headers & MASK_PRIMARY)
+ number_of_entries = headers[PRIMARY]->number_of_entries;
+ else
+ number_of_entries = headers[SECONDARY]->number_of_entries;
for (i = PRIMARY; i <= SECONDARY; ++i) {
- if (OverlappedEntries(entries[i], headers[i]->number_of_entries))
+ if (OverlappedEntries(entries[i], number_of_entries))
INVALIDATE_ENTRIES(valid_entries, i);
}
return valid_entries;
@@ -384,7 +406,9 @@ uint32_t CheckOverlappedPartition(GptData *gpt) {
* 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. */
+ * 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,
@@ -445,7 +469,7 @@ void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
* 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
+ * 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) {
@@ -502,48 +526,71 @@ void UpdateCrc(GptData *gpt) {
}
}
-/* Does every sanity check, and returns if any header/entries needs to be
- * written back. */
-int GptInit(GptData *gpt) {
- uint32_t valid_headers = MASK_BOTH;
- uint32_t valid_entries = MASK_BOTH;
+/* This function only checks GptData.
+ * valid_headers and valid_entries are used to store the checking results.
+ *
+ * Returns:
+ * GPT_ERROR_INVALID_HEADERS -- both headers are invalid.
+ * GPT_ERROR_INVALID_ENTRIES -- both entries are invalid.
+ * GPT_SUCCESS -- everything looks fine.
+ */
+int GptSanityCheck(GptData *gpt) {
int retval;
+ assert(gpt);
+
retval = CheckParameters(gpt);
if (retval != GPT_SUCCESS)
return retval;
/* Initialize values */
- gpt->modified = 0;
+ gpt->valid_headers = MASK_BOTH;
+ gpt->valid_entries = MASK_BOTH;
/* Start checking if header parameters are valid. */
- valid_headers &= CheckHeaderSignature(gpt);
- valid_headers &= CheckRevision(gpt);
- valid_headers &= CheckSize(gpt);
- valid_headers &= CheckReservedFields(gpt);
- valid_headers &= CheckMyLba(gpt);
- valid_headers &= CheckSizeOfPartitionEntry(gpt);
- valid_headers &= CheckNumberOfEntries(gpt);
- valid_headers &= CheckEntriesLba(gpt);
- valid_headers &= CheckValidUsableLbas(gpt);
-
- /* Checks if headers are valid. */
- valid_headers &= CheckHeaderCrc(gpt);
- gpt->modified |= RepairHeader(gpt, valid_headers);
+ CheckHeaderSignature(gpt);
+ CheckRevision(gpt);
+ CheckSize(gpt);
+ CheckReservedFields(gpt);
+ CheckMyLba(gpt);
+ CheckSizeOfPartitionEntry(gpt);
+ CheckNumberOfEntries(gpt);
+ CheckEntriesLba(gpt);
+ CheckValidUsableLbas(gpt);
+ CheckHeaderCrc(gpt);
+
+ /* Returns error if we don't have any valid header to use. */
+ if (!gpt->valid_headers)
+ return GPT_ERROR_INVALID_HEADERS;
/* Checks if entries are valid. */
- valid_entries &= CheckEntriesCrc(gpt);
- valid_entries &= CheckValidEntries(gpt);
- valid_entries &= CheckOverlappedPartition(gpt);
- gpt->modified |= RepairEntries(gpt, valid_entries);
+ CheckEntriesCrc(gpt);
+ CheckValidEntries(gpt);
+ CheckOverlappedPartition(gpt);
- /* Returns error if we don't have any valid header/entries to use. */
- if (!valid_headers)
- return GPT_ERROR_INVALID_HEADERS;
- if (!valid_entries)
+ /* Returns error if we don't have any valid entries to use. */
+ if (!gpt->valid_entries)
return GPT_ERROR_INVALID_ENTRIES;
+ return GPT_SUCCESS;
+}
+
+void GptRepair(GptData *gpt) {
+ gpt->modified |= RepairHeader(gpt, gpt->valid_headers);
+ gpt->modified |= RepairEntries(gpt, gpt->valid_entries);
UpdateCrc(gpt);
+}
+
+/* Does every sanity check, and returns if any header/entries needs to be
+ * written back. */
+int GptInit(GptData *gpt) {
+ int retval;
+
+ retval = GptSanityCheck(gpt);
+ if (GPT_SUCCESS != retval) return retval;
+
+ gpt->modified = 0;
+ GptRepair(gpt);
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
@@ -555,18 +602,15 @@ int GptInit(GptData *gpt) {
* 'entry_index' is the partition index: [0, number_of_entries).
*/
GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
- GptHeader *header;
uint8_t *entries;
if (secondary == PRIMARY) {
- header = (GptHeader*)gpt->primary_header;
entries = gpt->primary_entries;
} else {
- header = (GptHeader*)gpt->secondary_header;
entries = gpt->secondary_entries;
}
- return (GptEntry*)(&entries[header->size_of_entry * entry_index]);
+ return (GptEntry*)(&entries[GetNumberOfEntries(gpt) * entry_index]);
}
/* The following functions are helpers to access attributes bit more easily.
@@ -640,8 +684,13 @@ int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
}
uint32_t GetNumberOfEntries(const GptData *gpt) {
- GptHeader *header;
- header = (GptHeader*)gpt->primary_header;
+ 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
+ assert(0);
return header->number_of_entries;
}
diff --git a/cgptlib/cgptlib.h b/cgptlib/cgptlib.h
index af077464..7195ee7a 100644
--- a/cgptlib/cgptlib.h
+++ b/cgptlib/cgptlib.h
@@ -88,6 +88,9 @@ typedef struct {
* 0x08 = table2 */
int current_kernel; /* the current chromeos kernel index in partition table.
* -1 means not found on drive. */
+
+ /* Internal variables */
+ uint32_t valid_headers, valid_entries;
} GptData;
int GptInit(GptData *gpt);
diff --git a/cgptlib/cgptlib_internal.h b/cgptlib/cgptlib_internal.h
index 2ff9e059..25cbec4b 100644
--- a/cgptlib/cgptlib_internal.h
+++ b/cgptlib/cgptlib_internal.h
@@ -22,16 +22,19 @@ uint32_t CheckValidUsableLbas(GptData *gpt);
uint32_t CheckHeaderCrc(GptData *gpt);
uint32_t CheckEntriesCrc(GptData *gpt);
uint32_t CheckValidEntries(GptData *gpt);
-int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries);
-uint32_t CheckOverlappedPartition(GptData *gpt);
-uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries);
-uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers);
typedef struct {
uint64_t starting;
uint64_t ending;
} pair_t;
-
+int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries);
+uint32_t CheckOverlappedPartition(GptData *gpt);
+int IsSynonymous(const GptHeader* a, const GptHeader* b);
+uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries);
+uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers);
void UpdateCrc(GptData *gpt);
+int GptSanityCheck(GptData *gpt);
+void GptRepair(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);
diff --git a/cgptlib/tests/cgptlib_test.c b/cgptlib/tests/cgptlib_test.c
index 73fb9efe..72fb5e6c 100644
--- a/cgptlib/tests/cgptlib_test.c
+++ b/cgptlib/tests/cgptlib_test.c
@@ -114,6 +114,8 @@ void BuildTestGptData(GptData *gpt) {
gpt->sector_bytes = DEFAULT_SECTOR_SIZE;
gpt->drive_sectors = DEFAULT_DRIVE_SECTORS;
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
+ gpt->valid_headers = MASK_BOTH;
+ gpt->valid_entries = MASK_BOTH;
/* build primary */
header = (GptHeader*)gpt->primary_header;
@@ -600,6 +602,12 @@ int HeaderCrcTest() {
gpt->primary_header[primary_header->size] ^= 0x87;
EXPECT(MASK_BOTH == CheckHeaderCrc(gpt));
+ /* Very long header (actually invalid header). Expect will be ignored. */
+ primary_header->size = 0x12345678;
+ secondary_header->size = 0x87654321;
+ gpt->valid_headers = MASK_NONE;
+ EXPECT(MASK_NONE == CheckHeaderCrc(gpt));
+
return TEST_OK;
}
@@ -719,7 +727,8 @@ int SynonymousHeaderTest() {
++secondary_header->first_usable_lba;
RefreshCrc32(gpt);
EXPECT(GPT_SUCCESS == GptInit(gpt));
- EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
+ EXPECT((gpt->modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_HEADER2)) ==
+ GPT_MODIFIED_HEADER2);
EXPECT(primary_header->first_usable_lba ==
secondary_header->first_usable_lba);
@@ -892,7 +901,8 @@ int CorruptCombinationTest() {
primary_header->entries_crc32 ^= 0x73;
EXPECT(GPT_SUCCESS == GptInit(gpt));
/* After header is repaired, the entries are valid actually. */
- EXPECT((GPT_MODIFIED_HEADER1) == gpt->modified);
+ EXPECT((gpt->modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_HEADER2)) ==
+ GPT_MODIFIED_HEADER1);
/* We expect the modified header/entries can pass GptInit(). */
EXPECT(GPT_SUCCESS == GptInit(gpt));
EXPECT(0 == gpt->modified);
diff --git a/utility/cgpt/Makefile b/utility/cgpt/Makefile
index e678c03e..3c1f0bfd 100644
--- a/utility/cgpt/Makefile
+++ b/utility/cgpt/Makefile
@@ -4,7 +4,7 @@
TOP ?= ../../
CC ?= cc
-INCLUDES += -I$(TOP)/common/include
+INCLUDES += -I$(TOP)/common/include -I$(TOP)/../../../chroot/usr/include/
CFLAGS += -Wall -Werror -ggdb
LIBS += $(TOP)/cgptlib/libcgpt.a $(FWLIB)
diff --git a/utility/cgpt/cgpt.c b/utility/cgpt/cgpt.c
index a6671138..ea82b468 100644
--- a/utility/cgpt/cgpt.c
+++ b/utility/cgpt/cgpt.c
@@ -5,6 +5,14 @@
* Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
* files for more details.
*/
+/* To compile on host without compatility to BSD, we include
+ * endian.h under chroot. */
+#define _BSD_SOURCE
+#include "endian.h"
+
+#define __USE_LARGEFILE64
+#define __USE_FILE_OFFSET64
+#define _LARGEFILE64_SOURCE
#include "cgpt.h"
#include <errno.h>
#include <fcntl.h>
@@ -18,6 +26,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "cgptlib_internal.h"
#include "utility.h"
/* For usage print */
@@ -50,6 +59,141 @@ void Usage(const char *message) {
printf("\nFor more detailed usage, use %s COMMAND --help.\n\n", progname);
}
+/* GUID conversion functions. Accepted format:
+ *
+ * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
+ */
+void StrToGuid(const char *str, Guid *guid) {
+ uint32_t time_low, time_mid, time_high_and_version;
+
+ sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ &time_low,
+ (unsigned int *)&time_mid,
+ (unsigned int *)&time_high_and_version,
+ (unsigned int *)&guid->u.Uuid.clock_seq_high_and_reserved,
+ (unsigned int *)&guid->u.Uuid.clock_seq_low,
+ (unsigned int *)&guid->u.Uuid.node[0],
+ (unsigned int *)&guid->u.Uuid.node[1],
+ (unsigned int *)&guid->u.Uuid.node[2],
+ (unsigned int *)&guid->u.Uuid.node[3],
+ (unsigned int *)&guid->u.Uuid.node[4],
+ (unsigned int *)&guid->u.Uuid.node[5]);
+
+ 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);
+}
+
+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);
+ }
+ }
+}
+
+/* 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;
+}
+
/* Loads sectors from 'fd'.
* *buf is pointed to an allocated memory when returned, and should be
* freed by cgpt_close().
@@ -74,7 +218,7 @@ int Load(const int fd, uint8_t **buf,
*buf = Malloc(count);
assert(*buf);
- if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
+ if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET))
goto error_free;
nread = read(fd, *buf, count);
@@ -109,7 +253,7 @@ int Save(const int fd, const uint8_t *buf,
assert(buf);
count = sector_bytes * sector_count;
- if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
+ if (-1 == lseek64(fd, sector * sector_bytes, SEEK_SET))
return CGPT_FAILED;
nwrote = write(fd, buf, count);
@@ -119,6 +263,16 @@ int Save(const int fd, const uint8_t *buf,
return CGPT_OK;
}
+int CheckValid(const struct drive *drive) {
+ if ((drive->gpt.valid_headers != MASK_BOTH) ||
+ (drive->gpt.valid_entries != MASK_BOTH)) {
+ printf("\n[ERROR] any of GPT header/entries is invalid, "
+ "please run --repair first\n");
+ return CGPT_FAILED;
+ }
+ return CGPT_OK;
+}
+
/* Opens a block device (a regular file works well too).
*
* Returns CGPT_FAILED if any error happens.
@@ -131,7 +285,7 @@ int DriveOpen(const char *drive_path, struct drive *drive) {
assert(drive);
Memset(drive, 0, sizeof(struct drive));
- drive->fd = open(drive_path, O_RDWR);
+ drive->fd = open(drive_path, O_RDWR | O_LARGEFILE);
if (drive->fd == -1) {
printf("[ERROR] Cannot open drive file [%s]: %s\n",
drive_path, strerror(errno));
@@ -178,8 +332,8 @@ int DriveOpen(const char *drive_path, struct drive *drive) {
drive->gpt.drive_sectors - GPT_HEADER_SECTOR - GPT_ENTRIES_SECTORS,
drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS);
- if (GPT_SUCCESS != (gpt_retval = GptInit(&drive->gpt))) {
- printf("[ERROR] GptInit(): %s\n", GptError(gpt_retval));
+ if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) {
+ printf("[ERROR] GptSanityCheck(): %s\n", GptError(gpt_retval));
goto error_close;
}
diff --git a/utility/cgpt/cgpt.h b/utility/cgpt/cgpt.h
index 1b837efe..9e5800c1 100644
--- a/utility/cgpt/cgpt.h
+++ b/utility/cgpt/cgpt.h
@@ -89,6 +89,30 @@ int OpenDriveInLastArgument(const int argc,
char *const *argv,
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
+void StrToGuid(const char *str, Guid *guid);
+void GuidToStr(const Guid *guid, char *str);
+
+/* 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);
+
/* Describes the drive storing the GPT. */
struct drive {
int inited; /* indicated if this structure is valid */
@@ -112,6 +136,7 @@ extern const char* progname;
*/
int DriveOpen(const char *drive_path, struct drive *drive);
int DriveClose(struct drive *drive);
+int CheckValid(const struct drive *drive);
/* Function declarations for commands.
* The return value of these functions is passed to main()'s exit value. */
diff --git a/utility/cgpt/cgpt_attribute.c b/utility/cgpt/cgpt_attribute.c
index 82600c4f..4c3ed804 100644
--- a/utility/cgpt/cgpt_attribute.c
+++ b/utility/cgpt/cgpt_attribute.c
@@ -99,6 +99,8 @@ int CgptAttribute(int argc, char *argv[]) {
if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive))
return CGPT_FAILED;
+ if (CheckValid(&drive) != CGPT_OK) return CGPT_FAILED;
+
debug("[OPTION] i:%d b:%d s:%d t:%d p:%d\n", partition, bad, successful, tries, priority); /* FIXME */
/* partition is not specified, search for the first Chromeos kernel. */
diff --git a/utility/cgpt/cgpt_repair.c b/utility/cgpt/cgpt_repair.c
index 63a68f03..de5f2e43 100644
--- a/utility/cgpt/cgpt_repair.c
+++ b/utility/cgpt/cgpt_repair.c
@@ -66,6 +66,16 @@ int CgptRepair(int argc, char *argv[]) {
if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive))
return CGPT_FAILED;
+ GptRepair(&drive.gpt);
+ if (drive.gpt.modified & GPT_MODIFIED_HEADER1)
+ printf("* Pri Header is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_ENTRIES1)
+ printf("* Pri Table Entries is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_ENTRIES2)
+ printf("* Sec Table Entries is updated.\n");
+ if (drive.gpt.modified & GPT_MODIFIED_HEADER2)
+ printf("* Sec Header is updated.\n");
+
DriveClose(&drive);
return CGPT_OK;
diff --git a/utility/cgpt/cgpt_show.c b/utility/cgpt/cgpt_show.c
index c34e793d..59fd46a8 100644
--- a/utility/cgpt/cgpt_show.c
+++ b/utility/cgpt/cgpt_show.c
@@ -12,13 +12,14 @@
#include "utility.h"
/* Integers to store parsed argument. */
-static int help, raw;
+static int help, number, verbose;
/* The structure for getopt_long(). When you add/delete any line, please refine
* attribute_comments[] and third parameter of getopt_long() too. */
static struct option show_options[] = {
{.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'},
- {.name = "raw", .has_arg = no_argument, .flag = 0, .val = 'r'},
+ {.name = "number", .has_arg = no_argument, .flag = 0, .val = 'n'},
+ {.name = "verbose", .has_arg = no_argument, .flag = 0, .val = 'v'},
};
/* Extra information than struct option, please update this structure if you
@@ -29,11 +30,16 @@ static struct option_details show_options_details[] = {
.validator = AssignTrue,
.valid_range = 0,
.parsed = &help},
- /* raw */
- { .comment = "print raw data (byte-by-byte)",
+ /* number */
+ { .comment = "print raw numbers (don't interpret)",
.validator = AssignTrue,
.valid_range = 0,
- .parsed = &raw},
+ .parsed = &number},
+ /* verbose */
+ { .comment = "verbose print",
+ .validator = AssignTrue,
+ .valid_range = 0,
+ .parsed = &verbose},
};
void ShowHelp() {
@@ -44,10 +50,12 @@ void ShowHelp() {
/* Generate output like:
*
- * {AB-CD-EF-01}
+ * [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'.
+ * 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;
@@ -55,25 +63,159 @@ static short Uint8To2Chars(const uint8_t t) {
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 i;
- buf[0] = '{';
+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[i * 3 + 1] = c2 >> 8;
- buf[i * 3 + 2] = c2 & 0xff;
- if (i != (size - 1))
- buf[i * 3 + 3] = '-';
+ buf[outlen++] = c2 >> 8;
+ buf[outlen++] = c2 & 0xff;
+ if (i != (size - 1) && ((i + 1) % group) == 0)
+ buf[outlen++] = '-';
}
- buf[i * 3 + 0] = '}';
- buf[i * 3 + 1] = '\0';
+ buf[outlen++] = ']';
+ buf[outlen++] = '\0';
}
-/* Parses all options (and validates them), then opens the drive and sets
- * corresponding bits in GPT entry. */
+/* Outpur 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 i;
+
+ printf("%sSig: ", indent);
+ if (number == NOT_INITED) {
+ 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("%sAlter LBA: %lld\n", indent, (long long)header->alternate_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);
+}
+
+/* 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) {
+ struct {
+ Guid type;
+ char *description;
+ } known[] = {
+ {GPT_ENT_TYPE_UNUSED, "Unused partition"},
+ {GPT_ENT_TYPE_EFI, "EFI partition"},
+ {GPT_ENT_TYPE_CHROMEOS_KERNEL, "ChromeOS kernel"},
+ {GPT_ENT_TYPE_CHROMEOS_ROOTFS, "ChromeOS rootfs"},
+ {GPT_ENT_TYPE_CHROMEOS_RESERVED, "ChromeOS reserved"},
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_COUNT(known); ++i) {
+ if (!Memcmp(type, &known[i].type, sizeof(Guid))) {
+ strcpy(buf, known[i].description);
+ return CGPT_OK;
+ }
+ }
+ return CGPT_FAILED;
+}
+
+void EntriesDetails(GptData *gpt, const int secondary) {
+ int i;
+
+ for (i = 0; i < GetNumberOfEntries(gpt); ++i) {
+ static Guid unused = GPT_ENT_TYPE_UNUSED;
+ char contents[256];
+
+ GptEntry *entry;
+ entry = GetEntry(gpt, secondary, i);
+
+ if (!Memcmp(&unused, &entry->type, sizeof(unused))) continue;
+
+ if (number == NOT_INITED) {
+ uint8_t label[sizeof(entry->name) * 3 / 2];
+ 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),
+ i, 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);
+ } else {
+ char label[BUFFER_SIZE(sizeof(entry->name))];
+ char type[GUID_STRLEN], unique[GUID_STRLEN],
+ attributes[BUFFER_SIZE(sizeof(uint64_t))];
+
+ RawDump((void*)entry->name, sizeof(entry->name), label, 2);
+ snprintf(contents, sizeof(contents), "Label: %s", label);
+ printf(PARTITION_FMT, (int)entry->starting_lba,
+ (int)(entry->ending_lba - entry->starting_lba + 1),
+ i, contents);
+ GuidToStr(&entry->type, type);
+ printf(PARTITION_MORE, "Type: ", type);
+ GuidToStr(&entry->unique, unique);
+ printf(PARTITION_MORE, "UUID: ", unique);
+ RawDump((uint8_t*)&entry->attributes, 8, attributes, 4);
+ printf(PARTITION_MORE, "Attr: ", attributes);
+ }
+ }
+}
+
+/* Parses all options (and validates them), then opens the drive.
+ * Show GPT information in following order:
+ *
+ * Primary header sector
+ * details (if -v applied)
+ *
+ * Primary table sectors
+ *
+ * 1st partition
+ * details (if -v applied)
+ * :
+ * last partition
+ * details (if -v applied)
+ *
+ * Secondary table sectors
+ *
+ * Secondary header sector
+ * details (if -v applied)
+ */
int CgptShow(int argc, char *argv[]) {
struct drive drive;
- int i;
/* I know this is NOT the perfect place to put code to make options[] and
* details[] are synced. But this is the best place we have right now since C
@@ -81,10 +223,10 @@ int CgptShow(int argc, char *argv[]) {
assert(ARRAY_COUNT(show_options) ==
ARRAY_COUNT(show_options_details));
- help = raw = NOT_INITED;
+ help = number = NOT_INITED;
if (CGPT_OK != HandleOptions(argc, argv,
- "hr",
+ "hnv",
ARRAY_COUNT(show_options),
show_options,
show_options_details))
@@ -97,56 +239,74 @@ int CgptShow(int argc, char *argv[]) {
if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive))
return CGPT_FAILED;
- #define TITLE_FMT "%7s%7s%7s %s\n"
- #define GPT_FMT "%7d%7d%7s %s\n"
- #define GPT_MORE "%7s%7s%7s %s\n", "", "", ""
- #define PARTITION_FMT "%7d%7d%7d %s\n"
- #define PARTITION_MORE "%7s%7s%7s %s%s\n", "", "", ""
printf(TITLE_FMT, "start", "size", "index", "contents");
printf(GPT_FMT, 0, GPT_PMBR_SECTOR, "", "PMBR");
- printf(GPT_FMT, (int)GPT_PMBR_SECTOR,
- (int)GPT_HEADER_SECTOR, "", "Pri GPT header");
- printf(GPT_FMT, (int)(GPT_PMBR_SECTOR + GPT_HEADER_SECTOR),
- (int)GPT_ENTRIES_SECTORS, "", "Pri GPT table");
-
- for (i = 0; i < GetNumberOfEntries(&drive.gpt); ++i) {
- static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
- char contents[128];
- GptEntry *entry;
- entry = GetEntry(&drive.gpt, PRIMARY, i);
- if (!Memcmp(&zero, &entry->type, sizeof(zero))) continue;
+ 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];
- if (raw == NOT_INITED) {
- /* TODO(yjlou): support pretty dump */
- snprintf(contents, sizeof(contents),
- "* Not supported yet *");
- printf(PARTITION_FMT, (int)entry->starting_lba,
- (int)(entry->ending_lba - entry->starting_lba + 1),
- i, contents);
- } else {
- char type[50], unique[50], attributes[26];
-
- snprintf(contents, sizeof(contents),
- "%s", "");
- printf(PARTITION_FMT, (int)entry->starting_lba,
- (int)(entry->ending_lba - entry->starting_lba + 1),
- i, contents);
- RawDump((uint8_t*)&entry->type, 16, type);
- printf(PARTITION_MORE, "type: ", type);
- RawDump((uint8_t*)&entry->unique, 16, unique);
- printf(PARTITION_MORE, "uuid: ", unique);
- RawDump((uint8_t*)&entry->attributes, 8, attributes);
- printf(PARTITION_MORE, "attr: ", attributes);
+ snprintf(indent, sizeof(indent), GPT_MORE);
+ header = (GptHeader*)drive.gpt.primary_header;
+ HeaderDetails(header, indent);
}
+ } 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);
+
printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR -
GPT_ENTRIES_SECTORS),
- (int)GPT_ENTRIES_SECTORS, "", "Sec GPT table");
- printf(GPT_FMT, (int)(drive.gpt.drive_sectors - GPT_HEADER_SECTOR),
- (int)GPT_HEADER_SECTOR, "", "Sec GPT header");
+ (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);
+ }
+
+ 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);
+ }
+ }
+ CheckValid(&drive);
DriveClose(&drive);
return CGPT_OK;