diff --git a/utility/cgpt/Makefile b/utility/cgpt/Makefile index 70375ccca8..adb12ab752 100644 --- a/utility/cgpt/Makefile +++ b/utility/cgpt/Makefile @@ -10,7 +10,8 @@ LIBS += $(FWLIB) all: cgpt -cgpt: cgpt.o cgpt_attribute.o cgpt_options.o cgpt_repair.o cgpt_show.o $(LIBS) +cgpt: cgpt.o cgpt_add_modify_delete.o cgpt_attribute.o cgpt_dev.o \ + cgpt_options.o cgpt_repair.o cgpt_show.o $(LIBS) $(CC) -o cgpt $(CFLAGS) $^ .c.o: $(INCLUDES) diff --git a/utility/cgpt/cgpt.c b/utility/cgpt/cgpt.c index ea82b46867..eaec6f8902 100644 --- a/utility/cgpt/cgpt.c +++ b/utility/cgpt/cgpt.c @@ -38,8 +38,12 @@ struct { int (*fp)(int argc, char *argv[]); const char *comment; } cmds[] = { + {"add", CgptAdm, "Add a partition to drive"}, + {"delete", CgptAdm, "Delete a partition on drive"}, + {"modify", CgptAdm, "Modify the partition on drive"}, {"attribute", CgptAttribute, "Update GPT attribute bits " "(for ChromeOS kernel entry only)"}, + {"dev", CgptDev, "Developper mode"}, {"repair", CgptRepair, "Repair primary and secondary headers and tables"}, {"show", CgptShow, "Show partition details"}, }; @@ -51,7 +55,7 @@ void Usage(const char *message) { if (message) printf("%s\n", message); printf("Usage: %s COMMAND [OPTIONS]\n\n" - "Supported commands:\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); @@ -62,26 +66,30 @@ void Usage(const char *message) { /* GUID conversion functions. Accepted format: * * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" + * + * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED. */ -void StrToGuid(const char *str, Guid *guid) { +int 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]); + if (11 > 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])) return CGPT_FAILED; 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); + + return CGPT_OK; } void GuidToStr(const Guid *guid, char *str) { @@ -194,6 +202,54 @@ void UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16) utf16[s16idx++] = 0; } +struct { + Guid type; + char *name; + char *description; +} supported_types[] = { + {GPT_ENT_TYPE_UNUSED, "unused", "Unused partition"}, + {GPT_ENT_TYPE_EFI, "efi", "EFI partition"}, + {GPT_ENT_TYPE_CHROMEOS_KERNEL, "croskern", "ChromeOS kernel"}, + {GPT_ENT_TYPE_CHROMEOS_ROOTFS, "crosroot", "ChromeOS rootfs"}, + {GPT_ENT_TYPE_CHROMEOS_RESERVED, "crosresv", "ChromeOS reserved"}, +}; + +/* 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("\n* For --type option, you can use the following alias, " + "instead of hex values:\n"); + for (i = 0; i < ARRAY_COUNT(supported_types); ++i) { + printf(" %-10s %s\n", supported_types[i].name, + supported_types[i].description); + } + printf("\n"); +} + /* Loads sectors from 'fd'. * *buf is pointed to an allocated memory when returned, and should be * freed by cgpt_close(). @@ -267,7 +323,7 @@ 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"); + "please run '%s repair' first\n", progname); return CGPT_FAILED; } return CGPT_OK; @@ -316,9 +372,6 @@ int DriveOpen(const char *drive_path, struct drive *drive) { goto error_close; } drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes; - debug("drive: size:%llu sector_size:%d num_sector:%llu\n", - (long long unsigned int)drive->size, drive->gpt.sector_bytes, - (long long unsigned int)drive->gpt.drive_sectors); Load(drive->fd, &drive->gpt.primary_header, GPT_PMBR_SECTOR, drive->gpt.sector_bytes, GPT_HEADER_SECTOR); @@ -390,6 +443,7 @@ int main(int argc, char *argv[]) { int i; progname = argv[0]; + printf("Copyright (c) 2010 The Chromium OS Authors. All rights reserved.\n"); cmd = argv[optind++]; for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) { if (cmd && !strcmp(cmds[i].name, cmd)) diff --git a/utility/cgpt/cgpt.h b/utility/cgpt/cgpt.h index 9e5800c15b..3bc1f82fd0 100644 --- a/utility/cgpt/cgpt.h +++ b/utility/cgpt/cgpt.h @@ -51,13 +51,15 @@ struct option_details { * i.e. help, to indicate the option is present. */ int AssignTrue(const char *argument, void *pointer, void *integer); +/* Special validator. Copy string to 'parsed' with max 'valid_range' bytes. */ +int CopyString(const char *argument, void *max_len, void *dst); + +/* Validator function. Returns 1 if 'argument' is between 'max' and 'min' + * in 'valid_range'. */ struct number_range { int max; int min; }; - -/* Validator function. Returns 1 if 'argument' is between 'max' and 'min' - * in 'valid_range'. */ int InNumberRange(const char *argument, void *valid_range, void *parsed); void ShowOptions(const struct option *opts, @@ -97,7 +99,7 @@ int OpenDriveInLastArgument(const int argc, * '\0'). */ #define GUID_STRLEN 37 -void StrToGuid(const char *str, Guid *guid); +int StrToGuid(const char *str, Guid *guid); void GuidToStr(const Guid *guid, char *str); /* Convert UTF16 string to UTF8. Rewritten from gpt utility. @@ -113,6 +115,11 @@ void UTF16ToUTF8(const uint16_t *utf16, uint8_t *utf8); */ 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); + /* Describes the drive storing the GPT. */ struct drive { int inited; /* indicated if this structure is valid */ @@ -140,7 +147,9 @@ int CheckValid(const struct drive *drive); /* Function declarations for commands. * The return value of these functions is passed to main()'s exit value. */ +int CgptAdm(int argc, char *argv[]); int CgptAttribute(int argc, char *argv[]); +int CgptDev(int argc, char *argv[]); int CgptRepair(int argc, char *argv[]); int CgptShow(int argc, char *argv[]); diff --git a/utility/cgpt/cgpt_add_modify_delete.c b/utility/cgpt/cgpt_add_modify_delete.c new file mode 100644 index 0000000000..38a568a602 --- /dev/null +++ b/utility/cgpt/cgpt_add_modify_delete.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. + * + * Update GPT attribute bits. + */ +#include +#include +#include +#include "cgpt.h" +#include "cgptlib_internal.h" +#include "utility.h" + +static struct number_range + range_127_0 = {127, 0}; + +/* Integers to store parsed argument. */ +static int help, partition, begin_lba, size_lba; +static char type[128], unique[128], name[128]; + +/* 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 adm_options[] = { + {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'}, + {.name = "partition", .has_arg = required_argument, .flag = 0, .val = 'i'}, +#if 0//FIXME + {.name = "bad", .has_arg = required_argument, .flag = 0, .val = 'b'}, + {.name = "successful", .has_arg = required_argument, .flag = 0, .val = 's'}, + {.name = "tries", .has_arg = required_argument, .flag = 0, .val = 't'}, + {.name = "priority", .has_arg = required_argument, .flag = 0, .val = 'p'}, +#endif + {.name = "type", .has_arg = required_argument, .flag = 0, .val = 't'}, + {.name = "unique", .has_arg = required_argument, .flag = 0, .val = 'u'}, + {.name = "begin", .has_arg = required_argument, .flag = 0, .val = 'b'}, + {.name = "size", .has_arg = required_argument, .flag = 0, .val = 's'}, + {.name = "name", .has_arg = required_argument, .flag = 0, .val = 'n'}, + { /* last element, which should be zero. */ } +}; + +/* Extra information than struct option, please update this structure if you + * add/remove any line in attribute_options[]. */ +static struct option_details adm_options_details[] = { + /* help */ + { .comment = "print this help", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &help}, + /* partition */ + { .comment = "partition number (MUST HAVE)", + .validator = InNumberRange, + .valid_range = &range_127_0, + .parsed = &partition}, +#if 0//FIXME + /* bad */ + { .comment = "mark partition bad", + .validator = InNumberRange, + .valid_range = &range_1_0, + .parsed = &bad}, + /* successful */ + { .comment = "mark partition successful", + .validator = InNumberRange, + .valid_range = &range_1_0, + .parsed = &successful}, + /* tries */ + { .comment = "tries", + .validator = InNumberRange, + .valid_range = &range_15_0, + .parsed = &tries}, + /* priority */ + { .comment = "priority to boot", + .validator = InNumberRange, + .valid_range = &range_15_0, + .parsed = &priority}, +#endif + /* type */ + { .comment = "Partition Type (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", + .validator = CopyString, + .valid_range = (void*)sizeof(type), + .parsed = &type}, + /* uuid */ + { .comment = "Partition UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", + .validator = CopyString, + .valid_range = (void*)sizeof(unique), + .parsed = &unique}, + /* start */ + { .comment = "starting LBA", + .validator = InNumberRange, + .valid_range = 0, + .parsed = &begin_lba}, + /* end */ + { .comment = "ending LBA", + .validator = InNumberRange, + .valid_range = 0, + .parsed = &size_lba}, + /* name */ + { .comment = "Partition name", + .validator = CopyString, + .valid_range = (void*)sizeof(name), + .parsed = &name}, + { /* last element, which should be zero. */ } +}; + +void AdmHelp() { + printf("\nUsage: %s {add|delete|modify} [OPTIONS] device_name\n\n", progname); + ShowOptions(adm_options, adm_options_details, ARRAY_COUNT(adm_options)); + PrintTypes(); + printf("\n"); +} + +enum { + ADD, + DELETE, + MODIFY, +} command; + +/* Parses all options (and validates them), then opens the drive and sets + * corresponding bits in GPT entry. */ +int CgptAdm(int argc, char *argv[]) { + struct drive drive; + char *cmd; + GptEntry *entry; + Guid type_guid, unique_guid; + int dirty = 0; + + /* 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 + * preprocessor doesn't know sizeof() for #if directive. */ + assert(ARRAY_COUNT(adm_options) == + ARRAY_COUNT(adm_options_details)); + + cmd = argv[optind - 1]; + if (!strcmp("add", cmd)) command = ADD; + else if (!strcmp("delete", cmd)) command = DELETE; + else if (!strcmp("modify", cmd)) command = MODIFY; + +#if 0//FIXME + help = partition = bad = successful = tries = priority = +#endif + help = partition = begin_lba = size_lba = NOT_INITED; + type[0] = '\0'; + unique[0] = '\0'; + name[0] = '\0'; + + if (CGPT_OK != HandleOptions(argc, argv, + "hi:t:u:b:s:n:", + ARRAY_COUNT(adm_options), + adm_options, + adm_options_details)) + return CGPT_FAILED; + if (help != NOT_INITED) { + AdmHelp(); + return CGPT_FAILED; + } + + if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive)) + return CGPT_FAILED; + + if (CheckValid(&drive) != CGPT_OK) goto error_close; + + if (partition == NOT_INITED) { + printf("[ERROR] Please provide partition number with --partition or -i.\n"); + goto error_close; + } + + entry = GetEntry(&drive.gpt, PRIMARY, partition); + /* check before really doing something. */ + switch (command) { + case ADD: + if (NonZeroGuid(&entry->type)) { + printf("[ERROR] partition %d is not free, use '%s modify' instead.\n", + partition, progname); + goto error_close; + } + if (type[0] == '\0') { + printf("* You must give a type with '--type' or '-t'.\n"); + PrintTypes(); + goto error_close; + } + if (begin_lba == NOT_INITED) { + printf("* You didn't give the begin LBA, use '--begin' to specify.\n"); + goto error_close; + } + if (size_lba == NOT_INITED) { + printf("* You didn't give size, use '--size' to specify.\n"); + goto error_close; + } + break; + case DELETE: + if (!NonZeroGuid(&entry->type)) { + printf("[ERROR] partition %d is free already.\n", partition); + goto error_close; + } + break; + case MODIFY: + if (!NonZeroGuid(&entry->type)) { + printf("[ERROR] partition %d is free, use '%s add' first.\n", + partition, progname); + goto error_close; + } + break; + } + +#if 0 //FIXME + if (bad != NOT_INITED) + SetBad(&drive.gpt, PRIMARY, partition, bad); + if (successful != NOT_INITED) + SetSuccessful(&drive.gpt, PRIMARY, partition, successful); + if (tries != NOT_INITED) + SetTries(&drive.gpt, PRIMARY, partition, tries); + if (priority != NOT_INITED) + SetPriority(&drive.gpt, PRIMARY, partition, priority); +#endif + if (type[0]) { + if (CGPT_OK != SupportedType(type, &type_guid) && + CGPT_OK != StrToGuid(type, &type_guid)) { + printf("[ERROR] You didn't give a valid type [%s]\n", type); + goto error_close; + } + Memcpy(&entry->type, &type_guid, sizeof(Guid)); + ++dirty; + } + if (unique[0]) { + if (CGPT_OK != StrToGuid(unique, &unique_guid)) { + printf("[ERROR] You didn't give a valid UUID [%s]\n", unique); + goto error_close; + } + Memcpy(&entry->unique, &unique_guid, sizeof(Guid)); + ++dirty; + } + if (begin_lba != NOT_INITED) { + entry->starting_lba = begin_lba; + ++dirty; + } + if (size_lba != NOT_INITED) { + entry->ending_lba = entry->starting_lba + size_lba - 1; + ++dirty; + } + if (name[0]) { + UTF8ToUTF16((uint8_t*)name, entry->name); + ++dirty; + } + + if (command == DELETE) { + Guid unused = GPT_ENT_TYPE_UNUSED; + Memcpy(&entry->type, &unused, sizeof(Guid)); + } + + if (dirty) { + uint32_t valid_entries; + + valid_entries = drive.gpt.valid_entries; + if ((valid_entries != CheckValidEntries(&drive.gpt)) || + (valid_entries != CheckOverlappedPartition(&drive.gpt))) { + printf("\n[ERROR] Your change makes GPT invalid (or worse). " + "Please check your arguments.\n\n"); + drive.gpt.modified = 0; /* DriveClose() won't update hard drive. */ + goto error_close; + } + + /* Claims primary is good, then secondary will be overwritten. */ + RepairEntries(&drive.gpt, MASK_PRIMARY); + RepairHeader(&drive.gpt, MASK_PRIMARY); + + /* Forces headers and entries are modified so that CRC32 will be + * re-calculated and headers and entries will be updated to drive. */ + drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | + GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); + UpdateCrc(&drive.gpt); + } + DriveClose(&drive); + return CGPT_OK; + +error_close: + DriveClose(&drive); + return CGPT_FAILED; +} diff --git a/utility/cgpt/cgpt_attribute.c b/utility/cgpt/cgpt_attribute.c index 4c3ed80411..a54d7bab52 100644 --- a/utility/cgpt/cgpt_attribute.c +++ b/utility/cgpt/cgpt_attribute.c @@ -27,6 +27,7 @@ static struct option attribute_options[] = { {.name = "successful", .has_arg = required_argument, .flag = 0, .val = 's'}, {.name = "tries", .has_arg = required_argument, .flag = 0, .val = 't'}, {.name = "priority", .has_arg = required_argument, .flag = 0, .val = 'p'}, + { /* last element, which should be zero. */ } }; /* Extra information than struct option, please update this structure if you @@ -63,6 +64,7 @@ static struct option_details attribute_options_details[] = { .validator = InNumberRange, .valid_range = &range_15_0, .parsed = &priority}, + { /* last element, which should be zero. */ } }; void AttributeHelp() { @@ -101,8 +103,6 @@ int CgptAttribute(int argc, char *argv[]) { 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. */ if (partition == NOT_INITED) { int i; diff --git a/utility/cgpt/cgpt_dev.c b/utility/cgpt/cgpt_dev.c new file mode 100644 index 0000000000..ca53c4cfdf --- /dev/null +++ b/utility/cgpt/cgpt_dev.c @@ -0,0 +1,126 @@ +/* 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. + * + * Developper mode. + */ +#include +#include +#include +#include "cgpt.h" +#include "cgptlib_internal.h" +#include "utility.h" + +/* Integers to store parsed argument. */ +static int help, primary_header, primary_entries, + secondary_header, secondary_entries; + +/* 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 dev_options[] = { + {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'}, + {.name = "primary-header", .has_arg = no_argument, .flag = 0, .val = 'a'}, + {.name = "primary-entries", .has_arg = no_argument, .flag = 0, .val = 'b'}, + {.name = "secondary-entries", .has_arg = no_argument, .flag = 0, .val = 'c'}, + {.name = "secondary-header", .has_arg = no_argument, .flag = 0, .val = 'd'}, + { /* last element, which should be zero. */ } +}; + +/* Extra information than struct option, please update this structure if you + * add/remove any line in attribute_options[]. */ +static struct option_details dev_options_details[] = { + /* help */ + { .comment = "print this help", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &help}, + /* primary-header */ + { .comment = "damage primary header", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &primary_header}, + /* primary-entries */ + { .comment = "damage primary entries", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &primary_entries}, + /* secondary-entries */ + { .comment = "damage secondary entries", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &secondary_entries}, + /* secondary-header */ + { .comment = "damage secondary header", + .validator = AssignTrue, + .valid_range = 0, + .parsed = &secondary_header}, + { /* last element, which should be zero. */ } +}; + +void DevHelp() { + printf("\nDeveloper mode.\n\n"); + printf("\nUsage: %s dev [OPTIONS] device_name\n\n", progname); + ShowOptions(dev_options, dev_options_details, ARRAY_COUNT(dev_options)); + printf("\n"); +} + +/* Very simple function, you may choose damage one or more of the following + * sections: + * + * Primary GPT header + * Primary GPT table entries + * Secondary GPT table entries + * Secondary GPT header + */ +int CgptDev(int argc, char *argv[]) { + struct drive drive; + + /* 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 + * preprocessor doesn't know sizeof() for #if directive. */ + assert(ARRAY_COUNT(dev_options) == + ARRAY_COUNT(dev_options_details)); + + help = primary_header = primary_entries = + secondary_header = secondary_entries = NOT_INITED; + + if (CGPT_OK != HandleOptions(argc, argv, + "h", + ARRAY_COUNT(dev_options), + dev_options, + dev_options_details)) + return CGPT_FAILED; + if (help != NOT_INITED) { + DevHelp(); + return CGPT_FAILED; + } + + if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive)) + return CGPT_FAILED; + + #define ANY_PRIME 7 + if (primary_header != NOT_INITED) { + printf("* damage Pri Header\n"); + drive.gpt.primary_header[0] += ANY_PRIME; + drive.gpt.modified |= GPT_MODIFIED_HEADER1; + } + if (primary_entries != NOT_INITED) { + printf("* damage Pri Table\n"); + drive.gpt.primary_entries[0] += ANY_PRIME; + drive.gpt.modified |= GPT_MODIFIED_ENTRIES1; + } + if (secondary_entries != NOT_INITED) { + printf("* damage Sec Table\n"); + drive.gpt.secondary_entries[0] += ANY_PRIME; + drive.gpt.modified |= GPT_MODIFIED_ENTRIES2; + } + if (secondary_header != NOT_INITED) { + printf("* damage Sec Header\n"); + drive.gpt.secondary_header[0] += ANY_PRIME; + drive.gpt.modified |= GPT_MODIFIED_HEADER2; + } + + DriveClose(&drive); + + return CGPT_OK; +} diff --git a/utility/cgpt/cgpt_options.c b/utility/cgpt/cgpt_options.c index dae4bce911..c119ac9f49 100644 --- a/utility/cgpt/cgpt_options.c +++ b/utility/cgpt/cgpt_options.c @@ -5,6 +5,7 @@ * Update GPT attribute bits. */ #include +#include #include #include #include "cgpt.h" @@ -16,6 +17,12 @@ int AssignTrue(const char *argument, void *pointer, void *integer) { return CGPT_OK; } +/* Special validator. Copy string to 'parsed' with max 'valid_range' bytes. */ +int CopyString(const char *argument, void *max_len, void *dst) { + Memcpy(dst, argument, (intptr_t)max_len); + return CGPT_OK; +} + /* Validator function. Returns 1 if 'argument' is between 'max' and 'min' * in 'valid_range'. */ int InNumberRange(const char *argument, void *valid_range, void *parsed) { @@ -23,23 +30,27 @@ int InNumberRange(const char *argument, void *valid_range, void *parsed) { char *endptr; int number; - assert(valid_range); - number = strtol(argument, &endptr, 10); if (*endptr) { printf("[ERROR] argument '%s' is not a number.\n", argument); return CGPT_FAILED; } - if (number < range->min) { - printf("[ERROR] argument is too small (min is %d, but you gave: %d).\n", - range->min, number); - return CGPT_FAILED; - } else if (number > range->max) { - printf("[ERROR] argument is too large (max is %d, but you gave: %d).\n", - range->max, number); - return CGPT_FAILED; + if (range) { + if (number < range->min) { + printf("[ERROR] argument is too small (min is %d, but you gave: %d).\n", + range->min, number); + return CGPT_FAILED; + } else if (number > range->max) { + printf("[ERROR] argument is too large (max is %d, but you gave: %d).\n", + range->max, number); + return CGPT_FAILED; + } else { + if (parsed) *(int*)parsed = number; + return CGPT_OK; + } } else { + /* no range to check, assign integer. */ if (parsed) *(int*)parsed = number; return CGPT_OK; } @@ -51,6 +62,7 @@ void ShowOptions(const struct option *opts, int i; for (i = 0; i < num; ++i) { char buf[32]; + if (!opts[i].name) break; snprintf(buf, sizeof(buf), "--%s %s", opts[i].name, opts[i].has_arg ? "ARG" : ""); printf(" %-20s (-%c) %s\n", buf, opts[i].val, details[i].comment); @@ -76,6 +88,14 @@ int HandleOptions(const int argc, break; } else if (option == 0) { /* option 'val' has been saved in 'flag'. We do nothing here. */ + } else if (option == ':') { + printf("[ERROR] Missing parameter for option.\n"); + ShowOptions(options, details, option_count); + return CGPT_FAILED; + } else if (option == '?') { + printf("[ERROR] unknown option name: %s\n", argv[optind - 1]); + ShowOptions(options, details, option_count); + return CGPT_FAILED; } else { /* Short option doesn't update 'index'. We search whole array to find out * the corresponding long option. */ diff --git a/utility/cgpt/cgpt_repair.c b/utility/cgpt/cgpt_repair.c index de5f2e435e..f75e4a2c62 100644 --- a/utility/cgpt/cgpt_repair.c +++ b/utility/cgpt/cgpt_repair.c @@ -20,6 +20,7 @@ static int help; * attribute_comments[] and third parameter of getopt_long() too. */ static struct option repair_options[] = { {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'}, + { /* last element, which should be zero. */ } }; /* Extra information than struct option, please update this structure if you @@ -30,6 +31,7 @@ static struct option_details repair_options_details[] = { .validator = AssignTrue, .valid_range = 0, .parsed = &help}, + { /* last element, which should be zero. */ } }; void RepairHelp() { diff --git a/utility/cgpt/cgpt_show.c b/utility/cgpt/cgpt_show.c index 59fd46a870..703e6dffad 100644 --- a/utility/cgpt/cgpt_show.c +++ b/utility/cgpt/cgpt_show.c @@ -2,7 +2,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * - * Update GPT attribute bits. + * Show GPT details. */ #include #include @@ -20,6 +20,7 @@ static struct option show_options[] = { {.name = "help", .has_arg = no_argument, .flag = 0, .val = 'h'}, {.name = "number", .has_arg = no_argument, .flag = 0, .val = 'n'}, {.name = "verbose", .has_arg = no_argument, .flag = 0, .val = 'v'}, + { /* last element, which should be zero. */ } }; /* Extra information than struct option, please update this structure if you @@ -40,6 +41,7 @@ static struct option_details show_options_details[] = { .validator = AssignTrue, .valid_range = 0, .parsed = &verbose}, + { /* last element, which should be zero. */ } }; void ShowHelp() { @@ -121,31 +123,6 @@ static void HeaderDetails(GptHeader *header, const char *indent) { 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; @@ -236,6 +213,7 @@ int CgptShow(int argc, char *argv[]) { return CGPT_FAILED; } + OpenDriveInLastArgument(argc, argv, &drive); if (CGPT_OK != OpenDriveInLastArgument(argc, argv, &drive)) return CGPT_FAILED; diff --git a/vboot_firmware/lib/cgptlib/cgptlib.c b/vboot_firmware/lib/cgptlib/cgptlib.c index a871b1aa09..334c578967 100644 --- a/vboot_firmware/lib/cgptlib/cgptlib.c +++ b/vboot_firmware/lib/cgptlib/cgptlib.c @@ -58,7 +58,6 @@ int CheckParameters(GptData *gpt) { /* Expects header signature should be GPT_HEADER_SIGNATURE. */ uint32_t CheckHeaderSignature(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -69,15 +68,14 @@ uint32_t CheckHeaderSignature(GptData *gpt) { if (Memcmp(headers[i]->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE)) { - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } } - return valid_headers; + return gpt->valid_headers; } /* The header revision should be GPT_HEADER_REVISION. */ uint32_t CheckRevision(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -86,15 +84,14 @@ uint32_t CheckRevision(GptData *gpt) { for (i = PRIMARY; i <= SECONDARY; ++i) { if (headers[i]->revision != GPT_HEADER_REVISION) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } - return valid_headers; + return gpt->valid_headers; } /* A valid header size should be between MIN_SIZE_OF_HEADER and * MAX_SIZE_OF_HEADER. */ uint32_t CheckSize(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -104,14 +101,13 @@ uint32_t CheckSize(GptData *gpt) { for (i = PRIMARY; i <= SECONDARY; ++i) { if ((headers[i]->size < MIN_SIZE_OF_HEADER) || (headers[i]->size > MAX_SIZE_OF_HEADER)) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } - return valid_headers; + return gpt->valid_headers; } /* Reserved and padding fields should be zero. */ uint32_t CheckReservedFields(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -120,32 +116,30 @@ uint32_t CheckReservedFields(GptData *gpt) { for (i = PRIMARY; i <= SECONDARY; ++i) { if (headers[i]->reserved || headers[i]->padding) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } - return valid_headers; + return gpt->valid_headers; } /* my_lba field points to the header itself. * So that the my_lba of primary header should be 1 (right after PMBR). * The my_lba of secondary header should be the last secotr on drive. */ uint32_t CheckMyLba(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *primary_header, *secondary_header; primary_header = (GptHeader*)gpt->primary_header; secondary_header = (GptHeader*)gpt->secondary_header; if (primary_header->my_lba != GPT_PMBR_SECTOR) /* 2nd sector on drive */ - INVALIDATE_HEADER(valid_headers, PRIMARY); + INVALIDATE_HEADER(gpt->valid_headers, PRIMARY); if (secondary_header->my_lba != (gpt->drive_sectors - 1)) /* last sector */ - INVALIDATE_HEADER(valid_headers, SECONDARY); - return valid_headers; + INVALIDATE_HEADER(gpt->valid_headers, SECONDARY); + return gpt->valid_headers; } /* SizeOfPartitionEntry must be between MIN_SIZE_OF_ENTRY and * MAX_SIZE_OF_ENTRY, and a multiple of SIZE_OF_ENTRY_MULTIPLE. */ uint32_t CheckSizeOfPartitionEntry(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -157,16 +151,15 @@ uint32_t CheckSizeOfPartitionEntry(GptData *gpt) { if ((size_of_entry < MIN_SIZE_OF_ENTRY) || (size_of_entry > MAX_SIZE_OF_ENTRY) || (size_of_entry & (SIZE_OF_ENTRY_MULTIPLE - 1))) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } - return valid_headers; + return gpt->valid_headers; } /* number_of_entries must be between MIN_NUMBER_OF_ENTRIES and * MAX_NUMBER_OF_ENTRIES, and size_of_entry * number_of_entries must be * equal to TOTAL_ENTRIES_SIZE. */ uint32_t CheckNumberOfEntries(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -178,16 +171,15 @@ uint32_t CheckNumberOfEntries(GptData *gpt) { if ((number_of_entries < MIN_NUMBER_OF_ENTRIES) || (number_of_entries > MAX_NUMBER_OF_ENTRIES) || (number_of_entries * headers[i]->size_of_entry != TOTAL_ENTRIES_SIZE)) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } - return valid_headers; + return gpt->valid_headers; } /* Make sure entries_lba is correct. * 2 for primary entries * drive_sectors-1-GPT_ENTRIES_SECTORS for secondary entries. */ uint32_t CheckEntriesLba(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; GptHeader *primary_header, *secondary_header; primary_header = (GptHeader*)gpt->primary_header; @@ -196,20 +188,19 @@ uint32_t CheckEntriesLba(GptData *gpt) { /* We assume the primary partition entry table is located at the sector * right after primary partition header. */ if (primary_header->entries_lba != (GPT_PMBR_SECTOR + GPT_HEADER_SECTOR)) - INVALIDATE_HEADER(valid_headers, PRIMARY); + INVALIDATE_HEADER(gpt->valid_headers, PRIMARY); /* We assume the secondary partition entry table is the 32 sectors * right before the secondary partition header. */ if (secondary_header->entries_lba != (gpt->drive_sectors - 1 - GPT_ENTRIES_SECTORS)) - INVALIDATE_HEADER(valid_headers, SECONDARY); - return valid_headers; + INVALIDATE_HEADER(gpt->valid_headers, SECONDARY); + return gpt->valid_headers; } /* FirstUsableLBA must be after the end of the primary GPT table array. * LastUsableLBA must be before the start of the secondary GPT table array. * FirstUsableLBA <= LastUsableLBA. */ uint32_t CheckValidUsableLbas(GptData *gpt) { - uint32_t valid_headers = MASK_BOTH; uint64_t end_of_primary_entries; uint64_t start_of_secondary_entries; GptHeader *headers[] = { @@ -224,20 +215,20 @@ uint32_t CheckValidUsableLbas(GptData *gpt) { for (i = PRIMARY; i <= SECONDARY; ++i) { if (headers[i]->first_usable_lba < end_of_primary_entries) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); if (headers[i]->last_usable_lba >= start_of_secondary_entries) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); if (headers[i]->first_usable_lba > headers[i]->last_usable_lba) - INVALIDATE_HEADER(valid_headers, i); + INVALIDATE_HEADER(gpt->valid_headers, i); } if (headers[PRIMARY]->first_usable_lba - headers[PRIMARY]->entries_lba < GPT_ENTRIES_SECTORS) - INVALIDATE_HEADER(valid_headers, PRIMARY); + INVALIDATE_HEADER(gpt->valid_headers, PRIMARY); if (headers[SECONDARY]->last_usable_lba >= headers[SECONDARY]->entries_lba) - INVALIDATE_HEADER(valid_headers, SECONDARY); + INVALIDATE_HEADER(gpt->valid_headers, SECONDARY); - return valid_headers; + return gpt->valid_headers; } /* Checks header CRC */ @@ -289,7 +280,7 @@ uint32_t CheckEntriesCrc(GptData *gpt) { } /* Returns non-zero if the given GUID is non-zero. */ -static int NonZeroGuid(const Guid *guid) { +int NonZeroGuid(const Guid *guid) { static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; return Memcmp(&zero, guid, sizeof(zero)); } @@ -376,7 +367,6 @@ int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries) { /* Checks if any two partitions are overlapped in primary and secondary entries. */ uint32_t CheckOverlappedPartition(GptData *gpt) { - uint32_t valid_entries = MASK_BOTH; GptHeader *headers[] = { (GptHeader*)gpt->primary_header, (GptHeader*)gpt->secondary_header, @@ -395,9 +385,9 @@ uint32_t CheckOverlappedPartition(GptData *gpt) { for (i = PRIMARY; i <= SECONDARY; ++i) { if (OverlappedEntries(entries[i], number_of_entries)) - INVALIDATE_ENTRIES(valid_entries, i); + INVALIDATE_ENTRIES(gpt->valid_entries, i); } - return valid_entries; + return gpt->valid_entries; } /* Primary entries and secondary entries should be bitwise identical. diff --git a/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h b/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h index 25cbec4bc8..b4b63c535d 100644 --- a/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h +++ b/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h @@ -21,6 +21,7 @@ uint32_t CheckEntriesLba(GptData *gpt); uint32_t CheckValidUsableLbas(GptData *gpt); uint32_t CheckHeaderCrc(GptData *gpt); uint32_t CheckEntriesCrc(GptData *gpt); +int NonZeroGuid(const Guid *guid); uint32_t CheckValidEntries(GptData *gpt); typedef struct { uint64_t starting;