From 21ef1a3678854cf1a0ff63a6548305a3df2ecec3 Mon Sep 17 00:00:00 2001 From: Hung-Te Lin Date: Wed, 2 Jun 2010 18:13:47 -0700 Subject: Refine gbb_utility for better maintainance * now -g (get mode) becomes default * properties are now managed in a more generalized way so adding new property would be more easy Review URL: http://codereview.chromium.org/2549001 --- utility/gbb_utility.cc | 318 ++++++++++++++++++++++++++++-------------- utility/include/gbb_utility.h | 52 ++++--- 2 files changed, 240 insertions(+), 130 deletions(-) diff --git a/utility/gbb_utility.cc b/utility/gbb_utility.cc index b0aab56c..8b10787b 100644 --- a/utility/gbb_utility.cc +++ b/utility/gbb_utility.cc @@ -155,6 +155,13 @@ int GoogleBinaryBlockUtil::search_header_signatures(const string &image, static bool check_property_range(uint32_t off, uint32_t sz, uint32_t hdr_sz, uint32_t max_sz, const char *prop_name, bool verbose) { + // for backward compatibility, we allow zero entry here. + if (off == 0 && sz == 0) { + if (verbose) + fprintf(stderr, " warning: property %s is EMPTY.\n", prop_name); + return true; + } + if (off + sz > max_sz) { if (verbose) fprintf(stderr, " error: property %s exceed GBB.\n", prop_name); @@ -205,14 +212,20 @@ bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, return false; } - // verify location of properties - if (!check_property_range(h.hwid_offset, h.hwid_size, - h.header_size, block_size, "hwid", verbose) || - !check_property_range(h.rootkey_offset, h.rootkey_size, - h.header_size, block_size, "rootkey", verbose) || - !check_property_range(h.bmpfv_offset, h.bmpfv_size, - h.header_size, block_size, "bmpfv", verbose)) { - return false; + // verify properties + for (int i = 0; i < PROP_RANGE; i++) { + uint32_t off, size; + const char *name; + + if (!find_property(static_cast(i), + &off, &size, &name)) { + assert(!"invalid property."); + return false; + } + + if (!check_property_range(off, size, + h.header_size, block_size, name, verbose)) + return false; } return true; @@ -220,21 +233,28 @@ bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, uint32_t *poffset, - uint32_t *psize) const { + uint32_t *psize, + const char** pname) const { switch (i) { case PROP_HWID: *poffset = header_.hwid_offset; *psize = header_.hwid_size; + if (pname) + *pname = "hardware_id"; break; case PROP_ROOTKEY: *poffset = header_.rootkey_offset; *psize = header_.rootkey_size; + if (pname) + *pname = "root_key"; break; case PROP_BMPFV: *poffset = header_.bmpfv_offset; *psize = header_.bmpfv_size; + if (pname) + *pname = "bmp_fv"; break; default: @@ -248,10 +268,11 @@ bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { uint32_t prop_size; uint32_t prop_offset; + const char *prop_name; assert(is_valid_gbb); - if (!find_property(i, &prop_offset, &prop_size)) { + if (!find_property(i, &prop_offset, &prop_size, &prop_name)) { if (verbose) fprintf(stderr, " internal error: unknown property (%d).\n", static_cast(i)); @@ -260,16 +281,16 @@ bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { if (prop_size < value.size()) { if (verbose) - fprintf(stderr, " error: value size (%zu) exceed capacity (%u).\n", - value.size(), prop_size); + fprintf(stderr, " error: value size (%zu) exceed property capacity " + "(%u): %s\n", value.size(), prop_size, prop_name); return false; } if (i == PROP_HWID && prop_size == value.size()) { // special case: this is NUL-terminated so it's better to keep one more \0 if (verbose) - fprintf(stderr, "error: NUL-terminated string exceed capacity (%d)\n", - prop_size); + fprintf(stderr, "error: NUL-terminated string exceed capacity (%d): %s\n", + prop_size, prop_name); return false; } @@ -283,21 +304,45 @@ bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { string GoogleBinaryBlockUtil::get_property(PROPINDEX i) const { uint32_t prop_size; uint32_t prop_offset; + const char *prop_name; assert(is_valid_gbb); - if (!find_property(i, &prop_offset, &prop_size)) { + if (!find_property(i, &prop_offset, &prop_size, &prop_name)) { if (verbose) fprintf(stderr, " internal error: unknown property (%d).\n", static_cast(i)); return ""; } + // check range again to allow empty value (for compatbility) + if (prop_offset == 0 && prop_size == 0) { + if (verbose) + fprintf(stderr, " warning: empty property (%d): %s.\n", + static_cast(i), prop_name); + return ""; + } + string::const_iterator dest = file_content_.begin() + header_offset_ + prop_offset; return string(dest, dest + prop_size); } +string GoogleBinaryBlockUtil::get_property_name(PROPINDEX i) const { + uint32_t unused_off, unused_size; + const char *prop_name; + + if (!find_property(i, &unused_off, &unused_size, &prop_name)) { + if (verbose) + fprintf(stderr, " internal error: unknown property (%d).\n", + static_cast(i)); + assert(!"invalid property index."); + return ""; + } + + return prop_name; +} + bool GoogleBinaryBlockUtil::set_hwid(const char *hwid) { return set_property(PROP_HWID, hwid); } @@ -310,31 +355,39 @@ bool GoogleBinaryBlockUtil::set_bmpfv(const string &value) { return set_property(PROP_BMPFV, value); } -} // namespace vboot_reference +} // namespace vboot_reference #ifdef WITH_UTIL_MAIN /////////////////////////////////////////////////////////////////////// // command line utilities +#include + +using vboot_reference::GoogleBinaryBlockUtil; + // utility function: provide usage of this utility and exit. static void usagehelp_exit(const char *prog_name) { printf( "Utility to manage Google Binary Block (GBB)\n" - "Usage: %s [-g|-s] [OPTIONS] bios_file [output_file]\n\n" - "-g, --get \tGet (read) from bios_file, " + "Usage: %s [-g|-s] [OPTIONS] bios_file [output_file]\n" + "\n" + "GET MODE:\n" + "-g, --get (default)\tGet (read) from bios_file, " "with following options:\n" " --hwid \tReport hardware id (default).\n" " -k, --rootkey=FILE \tFile name to export Root Key.\n" " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n" "\n" + "SET MODE:\n" "-s, --set \tSet (write) to bios_file, " "with following options:\n" + " -o, --output=FILE \tNew file name for ouptput.\n" " -i, --hwid=HWID \tThe new hardware id to be changed.\n" " -k, --rootkey=FILE \tFile name of new Root Key.\n" - " -b, --bmpfv=FILE \tFile name of new Bitmap FV\n" + " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n" "\n" - " SAMPLE:\n" + "SAMPLE:\n" " %s -g bios.bin\n" " %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n" , prog_name, prog_name, prog_name); @@ -342,43 +395,63 @@ static void usagehelp_exit(const char *prog_name) { } // utility function: export a property from GBB to given file. +// if filename was empty, export to console (screen). // return true on success, otherwise false. -static bool export_property_to_file(const string &filename, - const char *name, const string &value) { - assert(!filename.empty()); - const char *fn = filename.c_str(); +static bool export_property(GoogleBinaryBlockUtil::PROPINDEX idx, + const string &filename, + const GoogleBinaryBlockUtil &util) { + string prop_name = util.get_property_name(idx), + value = util.get_property(idx); + const char *name = prop_name.c_str(); + + if (filename.empty()) { + // write to console + printf("%s: %s\n", name, value.c_str()); + } else { + const char *fn = filename.c_str(); - if (!write_nonempty_file(fn, value)) { - fprintf(stderr, "error: failed to export %s to file: %s\n", name, fn); - return false; + if (!write_nonempty_file(fn, value)) { + fprintf(stderr, "error: failed to export %s to file: %s\n", name, fn); + return false; + } + printf(" - exported %s to file: %s\n", name, fn); } - printf(" - exported %s to file: %s\n", name, fn); return true; } -// utility function: import a property to GBB by given file. +// utility function: import a property to GBB by given source (file or string). // return true on success, otherwise false. // is succesfully imported into GBB. -static bool import_property_from_file( - const string &filename, const char *name, - bool (vboot_reference::GoogleBinaryBlockUtil::*setter)(const string &value), - vboot_reference::GoogleBinaryBlockUtil *putil) { - assert(!filename.empty()); - - printf(" - import %s from %s: ", name, filename.c_str()); - string v = read_nonempty_file(filename.c_str()); - if (v.empty()) { - printf("invalid file.\n"); - return false; - } - - if (!(putil->*setter)(v)) { - printf("invalid content.\n"); - return false; +static bool import_property( + GoogleBinaryBlockUtil::PROPINDEX idx, const string &source, + bool source_as_file, GoogleBinaryBlockUtil *putil) { + assert(!source.empty()); + string prop_name = putil->get_property_name(idx); + + if (source_as_file) { + printf(" - import %s from %s: ", prop_name.c_str(), source.c_str()); + string v = read_nonempty_file(source.c_str()); + if (v.empty()) { + printf("invalid file.\n"); + return false; + } + if (!putil->set_property(idx, v)) { + printf("invalid content.\n"); + return false; + } + printf("success.\n"); + } else { + // source as string + string old_value = putil->get_property(idx); + bool result = putil->set_property(idx, source); + printf(" - %s changed from '%s' to '%s': %s\n", + prop_name.c_str(), old_value.c_str(), source.c_str(), + result ? "success" : "failed"); + if (!result) + return false; } - printf("success.\n"); return true; } @@ -389,29 +462,39 @@ int main(int argc, char *argv[]) { const char *myname = argv[0]; int err_stage = 0; // an indicator for error exits + // small parameter helper class + class OptPropertyMap: public + std::map { + public: + bool set_new_value(GoogleBinaryBlockUtil::PROPINDEX id, const string &v) { + if (find(id) != end()) + return false; + (*this)[id] = v; + return true; + } + }; + OptPropertyMap opt_props; + struct GBBUtilOptions { bool get_mode, set_mode; - bool use_hwid, use_rootkey, use_bmpfv; - string hwid, rootkey_fn, bmpfv_fn; + string input_fn, output_fn; } myopts; - myopts.get_mode = myopts.set_mode = false; - myopts.use_hwid = myopts.use_rootkey = myopts.use_bmpfv = false; // snippets for getopt_long int option_index, opt; static struct option long_options[] = { - {"get", 0, NULL, 'g' }, - {"set", 0, NULL, 's' }, - {"hwid", 2, NULL, 'i' }, + {"get", 0, NULL, 'g' }, + {"set", 0, NULL, 's' }, + {"output", 1, NULL, 'o' }, + {"hwid", 2, NULL, 'i' }, {"rootkey", 1, NULL, 'k' }, - {"bmpfv", 1, NULL, 'b' }, - { NULL, 0, NULL, 0 }, + {"bmpfv", 1, NULL, 'b' }, + { NULL, 0, NULL, 0 }, }; - int opt_props = 0; // number of assigned properties. // parse command line options - while ((opt = getopt_long(argc, argv, "gsi:k:b:", + while ((opt = getopt_long(argc, argv, "gso:i:k:b:", long_options, &option_index)) >= 0) { switch (opt) { case 'g': @@ -422,23 +505,26 @@ int main(int argc, char *argv[]) { myopts.set_mode = true; break; + case 'o': + myopts.output_fn = optarg; + break; + case 'i': - opt_props++; - myopts.use_hwid = true; - if (optarg) - myopts.hwid = optarg; + if (!opt_props.set_new_value( + GoogleBinaryBlockUtil::PROP_HWID, optarg ? optarg : "")) + usagehelp_exit(myname); break; case 'k': - opt_props++; - myopts.use_rootkey = true; - myopts.rootkey_fn = optarg; + if (!opt_props.set_new_value( + GoogleBinaryBlockUtil::PROP_ROOTKEY, optarg)) + usagehelp_exit(myname); break; case 'b': - opt_props++; - myopts.use_bmpfv = true; - myopts.bmpfv_fn = optarg; + if (!opt_props.set_new_value( + GoogleBinaryBlockUtil::PROP_BMPFV, optarg)) + usagehelp_exit(myname); break; default: @@ -450,25 +536,42 @@ int main(int argc, char *argv[]) { argc -= optind; argv += optind; - // check parameters configuration - if (!(argc == 1 || (myopts.set_mode && argc == 2))) + // adjust non-dashed parameters + if (myopts.output_fn.empty() && argc == 2) { + myopts.output_fn = argv[1]; + argc--; + } + + // currently, the only parameter is 'input file'. + if (argc == 1) { + myopts.input_fn = argv[0]; + } else { usagehelp_exit(myname); + } - // stage: parameter parsing + // stage: complete parameter parsing and checking err_stage++; if (myopts.get_mode == myopts.set_mode) { - printf("error: please assign either get or set mode.\n"); + if (myopts.get_mode) { + printf("error: please assign either get or set mode.\n"); + return err_stage; + } else { + // enter 'get' mode by default, if not assigned. + myopts.get_mode = true; + } + } + if (myopts.get_mode && !myopts.output_fn.empty()) { + printf("error: get-mode does not create output files.\n"); return err_stage; } // stage: load image files err_stage++; - vboot_reference::GoogleBinaryBlockUtil util; - const char *input_filename = argv[0], - *output_filename= (argc > 1) ? argv[1] : argv[0]; + GoogleBinaryBlockUtil util; - if (!util.load_from_file(input_filename)) { - printf("error: cannot load valid BIOS file: %s\n", input_filename); + assert(!myopts.input_fn.empty()); + if (!util.load_from_file(myopts.input_fn.c_str())) { + printf("error: cannot load valid BIOS file: %s\n", myopts.input_fn.c_str()); return err_stage; } @@ -476,53 +579,52 @@ int main(int argc, char *argv[]) { err_stage++; if (myopts.get_mode) { // get mode - if (opt_props < 1) // enable hwid by default - myopts.use_hwid = true; - - if (myopts.use_hwid) - printf("Hardware ID: %s\n", util.get_hwid().c_str()); - if (myopts.use_rootkey) - export_property_to_file(myopts.rootkey_fn, "rootkey", util.get_rootkey()); - if (myopts.use_bmpfv) - export_property_to_file(myopts.bmpfv_fn, "bmpfv", util.get_bmpfv()); + if (opt_props.empty()) // enable hwid by default + opt_props.set_new_value(GoogleBinaryBlockUtil::PROP_HWID, ""); + + for (OptPropertyMap::const_iterator i = opt_props.begin(); + i != opt_props.end(); + i++) { + export_property(i->first, i->second, util); + } + } else { // set mode assert(myopts.set_mode); - if (opt_props < 1) { + + if (opt_props.empty()) { printf("nothing to change. abort.\n"); return err_stage; } - // HWID does not come from file, so update it direcly here. - if (myopts.use_hwid) { - string old_hwid = util.get_hwid(); - if (!util.set_hwid(myopts.hwid.c_str())) { - printf("error: inproper hardware id: %s\n", - myopts.hwid.c_str()); + for (OptPropertyMap::const_iterator i = opt_props.begin(); + i != opt_props.end(); + i++) { + bool source_as_file = true; + + // the hwid command line parameter was a simple string. + if (i->first == GoogleBinaryBlockUtil::PROP_HWID) + source_as_file = false; + + if (!import_property(i->first, i->second, source_as_file, &util)) { + printf("error: cannot set properties. abort.\n"); return err_stage; } - printf(" - Hardware id changed: %s -> %s.\n", - old_hwid.c_str(), util.get_hwid().c_str()); - } - - // import other properties from file - if ((myopts.use_rootkey && - !import_property_from_file(myopts.rootkey_fn, "rootkey", - &vboot_reference::GoogleBinaryBlockUtil::set_rootkey, &util)) || - (myopts.use_bmpfv && - !import_property_from_file(myopts.bmpfv_fn, "bmpfv", - &vboot_reference::GoogleBinaryBlockUtil::set_bmpfv, &util))) { - printf("error: cannot set new properties. abort.\n"); - return err_stage; } // stage: write output err_stage++; - if (!util.save_to_file(output_filename)) { - printf("error: cannot save to file: %s\n", output_filename); + + // use input filename (overwrite) by default + if (myopts.output_fn.empty()) + myopts.output_fn = myopts.input_fn; + + assert(!myopts.output_fn.empty()); + if (!util.save_to_file(myopts.output_fn.c_str())) { + printf("error: cannot save to file: %s\n", myopts.output_fn.c_str()); return err_stage; } else { - printf("successfully saved new image to: %s\n", output_filename); + printf("successfully saved new image to: %s\n", myopts.output_fn.c_str()); } } diff --git a/utility/include/gbb_utility.h b/utility/include/gbb_utility.h index 70a45275..021eb80f 100644 --- a/utility/include/gbb_utility.h +++ b/utility/include/gbb_utility.h @@ -13,6 +13,14 @@ namespace vboot_reference { class GoogleBinaryBlockUtil { public: + // enumerate of available data fields + enum PROPINDEX { + PROP_HWID, // hardware id + PROP_ROOTKEY, // root key + PROP_BMPFV, // bitmap FV + PROP_RANGE, // indicator of valid property range + }; + GoogleBinaryBlockUtil(); ~GoogleBinaryBlockUtil(); @@ -24,22 +32,27 @@ class GoogleBinaryBlockUtil { // return true on success. bool save_to_file(const char *filename); - // getters and setters of properties in GBB - bool set_hwid(const char *hwid); // hwid is NUL-terminated. + // retrieve the value of a property from GBB data. + // return the property value. + std::string get_property(PROPINDEX i) const; + + // overwrite a property in GBB data. + // return true on success. + bool set_property(PROPINDEX i, const std::string &value); + + // get a readable name by a property index. + // return the name for valid properties, otherwise unexpected empty string. + std::string get_property_name(PROPINDEX i) const; + + // quick getters and setters of known properties in GBB + bool set_hwid(const char *hwid); // NOTE: hwid is NUL-terminated. bool set_rootkey(const std::string &value); bool set_bmpfv(const std::string &value); - std::string get_hwid() const { return get_property(PROP_HWID); } + std::string get_hwid() const { return get_property(PROP_HWID); } std::string get_rootkey() const { return get_property(PROP_ROOTKEY); } - std::string get_bmpfv() const { return get_property(PROP_BMPFV); } + std::string get_bmpfv() const { return get_property(PROP_BMPFV); } private: - // enumerate of available data fields - enum PROPINDEX { - PROP_HWID, // hardware id - PROP_ROOTKEY, // root key - PROP_BMPFV, // bitmap FV - }; - // clear all cached data and initialize to original state void initialize(); @@ -52,17 +65,12 @@ class GoogleBinaryBlockUtil { bool load_gbb_header(const std::string &image, long offset, GoogleBinaryBlockHeader *phdr) const; - // retrieve a property from GBB data. - // return the property value. - std::string get_property(PROPINDEX i) const; - - // overwrite a property in GBB data. - // return true on success. - bool set_property(PROPINDEX i, const std::string &value); - - // find the size and offset information for given property - // return true if the offset and size are assign to *poffset and *psize. - bool find_property(PROPINDEX i, uint32_t *poffset, uint32_t *psize) const; + // find the size, offset, and name information for given property. + // return true if the offset and size are assign to *poffset and *psize; + // if pname is not NULL, *pname will hold a pointer to a readable name. + // return false if the property index is invalid. + bool find_property(PROPINDEX i, uint32_t *poffset, uint32_t *psize, + const char **pname) const; GoogleBinaryBlockHeader header_; // copy of GBB header from image std::string file_content_; // complete image file content -- cgit v1.2.1