diff options
author | Louis Yung-Chieh Lo <yjlou@chromium.org> | 2010-05-27 11:21:17 +0800 |
---|---|---|
committer | Louis Yung-Chieh Lo <yjlou@chromium.org> | 2010-05-27 11:21:17 +0800 |
commit | 418ad3b620fe71fa26c1f04154497b4b450c3923 (patch) | |
tree | 2c11e966121215ecf50ba28c3837de89fb6b3e62 | |
parent | bcd8f4a07c664d55b67d4e8dc35e362b50b376bd (diff) | |
download | vboot-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.c | 145 | ||||
-rw-r--r-- | cgptlib/cgptlib.h | 3 | ||||
-rw-r--r-- | cgptlib/cgptlib_internal.h | 13 | ||||
-rw-r--r-- | cgptlib/tests/cgptlib_test.c | 14 | ||||
-rw-r--r-- | utility/cgpt/Makefile | 2 | ||||
-rw-r--r-- | utility/cgpt/cgpt.c | 164 | ||||
-rw-r--r-- | utility/cgpt/cgpt.h | 25 | ||||
-rw-r--r-- | utility/cgpt/cgpt_attribute.c | 2 | ||||
-rw-r--r-- | utility/cgpt/cgpt_repair.c | 10 | ||||
-rw-r--r-- | utility/cgpt/cgpt_show.c | 282 |
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; |