From 078ecc5c4dfd6cff38e74aba8278143ebcac7e7c Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 10:53:54 -0700 Subject: [PATCH 01/19] Add flag for binary file --- frugen.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frugen.c b/frugen.c index a6b8fe0..62d03b5 100644 --- a/frugen.c +++ b/frugen.c @@ -264,6 +264,9 @@ int main(int argc, char *argv[]) /* Set input file format to JSON */ { .name = "json", .val = 'j', .has_arg = false }, + /* Set input file format to binary */ + { .name = "binaryformat", .val = 'x', .has_arg = false }, + /* Set file to load the data from */ { .name = "from", .val = 'z', .has_arg = true }, @@ -302,6 +305,7 @@ int main(int argc, char *argv[]) "\n\t\t" "There must be an even number of characters in a 'binary' argument", ['j'] = "Set input text file format to JSON (default). Specify before '--from'", + ['x'] = "Set input file format to binary. Specify before '--from'", ['z'] = "Load FRU information from a text file", /* Chassis info area related options */ ['t'] = "Set chassis type (hex). Defaults to 0x02 ('Unknown')", @@ -336,7 +340,8 @@ int main(int argc, char *argv[]) has_internal = false, has_multirec = false; - bool use_json = true; /* TODO: Add more input formats, consider libconfig */ + bool use_json = false; /* TODO: Add more input formats, consider libconfig */ + bool use_binary = false; do { fru_reclist_t **custom = NULL; @@ -376,6 +381,16 @@ int main(int argc, char *argv[]) case 'j': // json use_json = true; + if (use_binary) { + fatal("Can't specify --json and --binaryformat together"); + } + break; + + case 'x': // binary + use_binary = true; + if (use_json) { + fatal("Can't specify --json and --binaryformat together"); + } break; case 'z': // from @@ -460,6 +475,9 @@ int main(int argc, char *argv[]) fatal("JSON support was disabled at compile time"); #endif } + else if (use_binary) { + fatal("Binary support not yet added"); + } else { fatal("The requested input file format is not supported"); } From e9872807bcca6240e87212342a7b9ea82f866de6 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 12:17:35 -0700 Subject: [PATCH 02/19] Add support for ascii chassis data --- frugen.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/frugen.c b/frugen.c index 62d03b5..404f5f4 100644 --- a/frugen.c +++ b/frugen.c @@ -476,7 +476,118 @@ int main(int argc, char *argv[]) #endif } else if (use_binary) { - fatal("Binary support not yet added"); + int fd = open(optarg, O_RDONLY); + if (fd < 0) { + perror(""); + fatal("Failed to open file"); + } + + char common_header[8]; + ssize_t bytes_read = read(fd, common_header, 8); + if (bytes_read != 8) + fatal("Encountered error while reading header"); + + // Common Header Format Version = common_header[0] + // Internal Use Area Starting Offset = common_header[1] + uint16_t chassis_start_offset = 8 * common_header[2]; + uint16_t board_area_start_offset = 8 * common_header[3]; + uint16_t product_area_start_offset = 8 * common_header[4]; + // MultiRecord Area Starting Offset = 8 * common_header[5] + // Padding = common_header[6] = 0 + // Checksum = common_header[7] + + // For the above offsets, an offset of 0 is valid and implies + // that the field is not present. + + has_chassis = chassis_start_offset != 0; + // has_board = board_start_offset != 0; + // has_product = product_start_offset != 0; + + printf("Offsets: %u %u %u\n", + chassis_start_offset, + board_area_start_offset, + product_area_start_offset + ); + + if (has_chassis) { + uint8_t chassis_header[4]; + lseek(fd, chassis_start_offset, SEEK_SET); + bytes_read = read(fd, chassis_header, 4); + if (bytes_read != 4) + fatal("Error reading chassis"); + + if (chassis_header[0] != 1) + fatal("Unsupported Chassis Info Area Format Version"); + + uint16_t length = 8 * chassis_header[1]; + chassis.type = chassis_header[2]; + + { + uint8_t type_and_length = chassis_header[3]; + uint8_t type = (type_and_length & 0xc0) >> 6; + uint8_t length = type_and_length & ~0xc0; + // TODO check type, for now we assume it is 0b11 + if (type != 3) + fatal("Unsupported data type in chassis"); + + bytes_read = read(fd, chassis.pn, length); + if (bytes_read != length) + fatal("Error reading chassis"); + } + + { + uint8_t type_and_length; + bytes_read = read(fd, &type_and_length, 1); + if (bytes_read != 1) + fatal("Error reading chassis"); + + uint8_t type = (type_and_length & 0xc0) >> 6; + uint8_t length = type_and_length & ~0xc0; + // TODO check type, for now we assume it is 0b11 + if (type != 3) + fatal("Unsupported data type in chassis"); + + bytes_read = read(fd, chassis.serial, length); + if (bytes_read != length) + fatal("Error reading chassis"); + } + + while (true) { + uint8_t type_and_length; + bytes_read = read(fd, &type_and_length, 1); + if (bytes_read != 1) + fatal("Error reading chassis"); + + if (type_and_length == 0xc1) { + // throw away the next byte, it's an empty field + // indicating that we've reached the end + lseek(fd, 1, SEEK_CUR); + break; + } + + fru_reclist_t *custom_field = + add_reclist(&chassis.cust); + if (custom_field == NULL) + fatal("Error allocating custom field"); + + + uint8_t length = type_and_length & ~0xc0; + + uint8_t* data = malloc(length + 1); + if (data == NULL) + fatal("Error allocating custom field"); + + bytes_read = read(fd, data, length); + if (bytes_read != length) + fatal("Error reading chassis"); + // Add NUL byte + data[length] = 0; + + // TODO check type for binary data + custom_field->rec = fru_encode_data(LEN_AUTO, data); + free(data); + } + } } else { fatal("The requested input file format is not supported"); From 66c1062daac5bf52edd8906c4b92e755c3d7bd3f Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 13:14:46 -0700 Subject: [PATCH 03/19] Refactor chassis reading --- frugen.c | 131 ++++++++++++++++++++++++------------------------------- 1 file changed, 58 insertions(+), 73 deletions(-) diff --git a/frugen.c b/frugen.c index 404f5f4..92d15e7 100644 --- a/frugen.c +++ b/frugen.c @@ -220,6 +220,57 @@ bool json_fill_fru_area_custom(json_object *jso, fru_reclist_t **custom) } #endif /* __HAS_JSON__ */ +static void safe_read(int fd, void *buffer, size_t length) { + ssize_t bytes_read = read(fd, buffer, length); + if (bytes_read != length) + fatal("Error reading file"); +} + +static inline uint8_t get_type_from_typelen(uint8_t typelen) { + return (typelen & 0xc0) >> 6; +} + +static inline uint8_t get_length_from_typelen(uint8_t typelen) { + return typelen & ~0xc0; +} + +static void fd_read_field(int fd, uint8_t *out) { + uint8_t typelen; + safe_read(fd, &typelen, 1); + // TODO check type, for now we assume it is 0b11 + if (get_type_from_typelen(typelen) != 3) + fatal("Unsupported data type for binary format"); + safe_read(fd, out, get_length_from_typelen(typelen)); +} + +static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { + while (true) { + uint8_t typelen; + safe_read(fd, &typelen, 1); + if (typelen == 0xc1) { + // throw away the next byte, it's an empty field + // indicating that we've reached the end + lseek(fd, 1, SEEK_CUR); + break; + } + + fru_reclist_t *custom_field = add_reclist(reclist); + if (custom_field == NULL) + fatal("Error allocating custom field"); + + size_t length = get_length_from_typelen(typelen); + uint8_t* data = malloc(length + 1); + if (data == NULL) + fatal("Error allocating custom field"); + safe_read(fd, data, length); + // NUL terminate the date just in case its a string + data[length] = 0; + + custom_field->rec = fru_encode_data(LEN_AUTO, data); + free(data); + } +} + int main(int argc, char *argv[]) { int i; @@ -500,7 +551,7 @@ int main(int argc, char *argv[]) // that the field is not present. has_chassis = chassis_start_offset != 0; - // has_board = board_start_offset != 0; + has_board = board_start_offset != 0; // has_product = product_start_offset != 0; printf("Offsets: %u %u %u\n", @@ -510,83 +561,17 @@ int main(int argc, char *argv[]) ); if (has_chassis) { - uint8_t chassis_header[4]; lseek(fd, chassis_start_offset, SEEK_SET); - bytes_read = read(fd, chassis_header, 4); - if (bytes_read != 4) - fatal("Error reading chassis"); + uint8_t chassis_header[3]; + safe_read(fd, chassis_header, 3); if (chassis_header[0] != 1) fatal("Unsupported Chassis Info Area Format Version"); - - uint16_t length = 8 * chassis_header[1]; + // Chassis Info Area Length = 8 * chassis_header[1] chassis.type = chassis_header[2]; - - { - uint8_t type_and_length = chassis_header[3]; - uint8_t type = (type_and_length & 0xc0) >> 6; - uint8_t length = type_and_length & ~0xc0; - // TODO check type, for now we assume it is 0b11 - if (type != 3) - fatal("Unsupported data type in chassis"); - - bytes_read = read(fd, chassis.pn, length); - if (bytes_read != length) - fatal("Error reading chassis"); - } - - { - uint8_t type_and_length; - bytes_read = read(fd, &type_and_length, 1); - if (bytes_read != 1) - fatal("Error reading chassis"); - - uint8_t type = (type_and_length & 0xc0) >> 6; - uint8_t length = type_and_length & ~0xc0; - // TODO check type, for now we assume it is 0b11 - if (type != 3) - fatal("Unsupported data type in chassis"); - - bytes_read = read(fd, chassis.serial, length); - if (bytes_read != length) - fatal("Error reading chassis"); - } - - while (true) { - uint8_t type_and_length; - bytes_read = read(fd, &type_and_length, 1); - if (bytes_read != 1) - fatal("Error reading chassis"); - - if (type_and_length == 0xc1) { - // throw away the next byte, it's an empty field - // indicating that we've reached the end - lseek(fd, 1, SEEK_CUR); - break; - } - - fru_reclist_t *custom_field = - add_reclist(&chassis.cust); - if (custom_field == NULL) - fatal("Error allocating custom field"); - - - uint8_t length = type_and_length & ~0xc0; - - uint8_t* data = malloc(length + 1); - if (data == NULL) - fatal("Error allocating custom field"); - - bytes_read = read(fd, data, length); - if (bytes_read != length) - fatal("Error reading chassis"); - // Add NUL byte - data[length] = 0; - - // TODO check type for binary data - custom_field->rec = fru_encode_data(LEN_AUTO, data); - free(data); - } + fd_read_field(fd, chassis.pn); + fd_read_field(fd, chassis.serial); + fd_fill_custom_fields(fd, &chassis.cust); } } else { From 1ad43d11420d770455124db299866cb6f88ff0f6 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 13:55:01 -0700 Subject: [PATCH 04/19] Support for board fields --- frugen.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/frugen.c b/frugen.c index 92d15e7..89cbc4c 100644 --- a/frugen.c +++ b/frugen.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "fru.h" #include "smbios.h" @@ -93,6 +94,7 @@ bool datestr_to_tv(const char *datestr, struct timeval *tv) tm.tm_isdst = -1; // Use local timezone data in mktime time = mktime(&tm); // Here we have local time since local Epoch tv->tv_sec = time + timezone; // Convert to UTC + printf("Time written: %ld\n", tv->tv_sec); tv->tv_usec = 0; return true; } @@ -541,8 +543,8 @@ int main(int argc, char *argv[]) // Common Header Format Version = common_header[0] // Internal Use Area Starting Offset = common_header[1] uint16_t chassis_start_offset = 8 * common_header[2]; - uint16_t board_area_start_offset = 8 * common_header[3]; - uint16_t product_area_start_offset = 8 * common_header[4]; + uint16_t board_start_offset = 8 * common_header[3]; + uint16_t product_start_offset = 8 * common_header[4]; // MultiRecord Area Starting Offset = 8 * common_header[5] // Padding = common_header[6] = 0 // Checksum = common_header[7] @@ -550,17 +552,17 @@ int main(int argc, char *argv[]) // For the above offsets, an offset of 0 is valid and implies // that the field is not present. - has_chassis = chassis_start_offset != 0; - has_board = board_start_offset != 0; + bool data_has_chassis = chassis_start_offset != 0; + bool data_has_board = board_start_offset != 0; // has_product = product_start_offset != 0; printf("Offsets: %u %u %u\n", chassis_start_offset, - board_area_start_offset, - product_area_start_offset + board_start_offset, + product_start_offset ); - if (has_chassis) { + if (data_has_chassis) { lseek(fd, chassis_start_offset, SEEK_SET); uint8_t chassis_header[3]; @@ -572,6 +574,39 @@ int main(int argc, char *argv[]) fd_read_field(fd, chassis.pn); fd_read_field(fd, chassis.serial); fd_fill_custom_fields(fd, &chassis.cust); + + has_chassis = true; + } + if (data_has_board) { + lseek(fd, board_start_offset, SEEK_SET); + + uint8_t board_header[3]; + safe_read(fd, board_header, 3); + if (board_header[0] != 1) + fatal("Unsupported Board Info Area Format Version"); + // Board Info Area Length = 8 * board_header[1] + // TODO use the lang for text decoding?? + board.lang = board_header[2]; + + uint32_t min_since_epoch = 0; + safe_read(fd, &min_since_epoch, 3); + struct tm tm_1996 = { + .tm_year = 96, + .tm_mon = 0, + .tm_mday = 1 + }; + // The argument to mktime is zoneless + board.tv.tv_sec = mktime(&tm_1996) + 60 * min_since_epoch; + + fd_read_field(fd, board.mfg); + fd_read_field(fd, board.pname); + fd_read_field(fd, board.serial); + fd_read_field(fd, board.pn); + fd_read_field(fd, board.file); + fd_fill_custom_fields(fd, &board.cust); + + has_board = true; + has_bdate = true; } } else { From 4d2aa75b0473e49966c62479dd3ca02464e294b5 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 15:45:24 -0700 Subject: [PATCH 05/19] Use provided macros --- frugen.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/frugen.c b/frugen.c index 89cbc4c..84aafa5 100644 --- a/frugen.c +++ b/frugen.c @@ -228,21 +228,13 @@ static void safe_read(int fd, void *buffer, size_t length) { fatal("Error reading file"); } -static inline uint8_t get_type_from_typelen(uint8_t typelen) { - return (typelen & 0xc0) >> 6; -} - -static inline uint8_t get_length_from_typelen(uint8_t typelen) { - return typelen & ~0xc0; -} - static void fd_read_field(int fd, uint8_t *out) { uint8_t typelen; safe_read(fd, &typelen, 1); // TODO check type, for now we assume it is 0b11 - if (get_type_from_typelen(typelen) != 3) + if (!FRU_ISTYPE(typelen, TEXT)) fatal("Unsupported data type for binary format"); - safe_read(fd, out, get_length_from_typelen(typelen)); + safe_read(fd, out, FRU_FIELDDATALEN(typelen)); } static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { @@ -260,7 +252,7 @@ static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { if (custom_field == NULL) fatal("Error allocating custom field"); - size_t length = get_length_from_typelen(typelen); + size_t length = FRU_FIELDDATALEN(typelen); uint8_t* data = malloc(length + 1); if (data == NULL) fatal("Error allocating custom field"); @@ -554,13 +546,7 @@ int main(int argc, char *argv[]) bool data_has_chassis = chassis_start_offset != 0; bool data_has_board = board_start_offset != 0; - // has_product = product_start_offset != 0; - - printf("Offsets: %u %u %u\n", - chassis_start_offset, - board_start_offset, - product_start_offset - ); + // bool data_has_product = product_start_offset != 0; if (data_has_chassis) { lseek(fd, chassis_start_offset, SEEK_SET); @@ -585,7 +571,6 @@ int main(int argc, char *argv[]) if (board_header[0] != 1) fatal("Unsupported Board Info Area Format Version"); // Board Info Area Length = 8 * board_header[1] - // TODO use the lang for text decoding?? board.lang = board_header[2]; uint32_t min_since_epoch = 0; From f68994541f11fb1797d0f93572c8440f3b0956ab Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 16:17:47 -0700 Subject: [PATCH 06/19] Add support for product and for all data types --- fru.c | 1 - fru.h | 1 + frugen.c | 44 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/fru.c b/fru.c index bb32c08..0020465 100644 --- a/fru.c +++ b/fru.c @@ -285,7 +285,6 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data) * For binary data use FRU_FIELDDATALEN(field->typelen) to find * out the size of the returned buffer. */ -static unsigned char * fru_decode_data(const fru_field_t *field) { unsigned char * out; diff --git a/fru.h b/fru.h index c7441ba..bf696a5 100644 --- a/fru.h +++ b/fru.h @@ -230,6 +230,7 @@ fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis); fru_board_area_t * fru_board_info(const fru_exploded_board_t *board); fru_product_area_t * fru_product_info(const fru_exploded_product_t *product); fru_field_t * fru_encode_data(int len, const uint8_t *data); +unsigned char * fru_decode_data(const fru_field_t *field); fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); #endif // __FRULIB_FRU_H__ diff --git a/frugen.c b/frugen.c index 84aafa5..385ec67 100644 --- a/frugen.c +++ b/frugen.c @@ -232,9 +232,23 @@ static void fd_read_field(int fd, uint8_t *out) { uint8_t typelen; safe_read(fd, &typelen, 1); // TODO check type, for now we assume it is 0b11 - if (!FRU_ISTYPE(typelen, TEXT)) - fatal("Unsupported data type for binary format"); - safe_read(fd, out, FRU_FIELDDATALEN(typelen)); + //if (!FRU_ISTYPE(typelen, TEXT)) + // fatal("Unsupported data type for binary format"); + size_t length = FRU_FIELDDATALEN(typelen); + fru_field_t *field = calloc(1, FRU_FIELDSIZE(typelen)); + if (field == NULL) + fatal("Could not allocate field"); + + field->typelen = typelen; + safe_read(fd, &(field->data), length); + + char *data = fru_decode_data(field); + if (data == NULL) + fatal("Could not decode field"); + + memcpy(out, data, strlen(data) + 1); + free(data); + free(field); } static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { @@ -257,7 +271,6 @@ static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { if (data == NULL) fatal("Error allocating custom field"); safe_read(fd, data, length); - // NUL terminate the date just in case its a string data[length] = 0; custom_field->rec = fru_encode_data(LEN_AUTO, data); @@ -546,7 +559,7 @@ int main(int argc, char *argv[]) bool data_has_chassis = chassis_start_offset != 0; bool data_has_board = board_start_offset != 0; - // bool data_has_product = product_start_offset != 0; + bool data_has_product = product_start_offset != 0; if (data_has_chassis) { lseek(fd, chassis_start_offset, SEEK_SET); @@ -593,6 +606,27 @@ int main(int argc, char *argv[]) has_board = true; has_bdate = true; } + if (data_has_product) { + lseek(fd, product_start_offset, SEEK_SET); + + uint8_t product_header[3]; + safe_read(fd, product_header, 3); + if (product_header[0] != 1) + fatal("Unsupported Board Info Area Format Version"); + // Product Info Area Length = 8 * product_header[1] + product.lang = product_header[2]; + + fd_read_field(fd, product.mfg); + fd_read_field(fd, product.pname); + fd_read_field(fd, product.pn); + fd_read_field(fd, product.ver); + fd_read_field(fd, product.serial); + fd_read_field(fd, product.atag); + fd_read_field(fd, product.file); + fd_fill_custom_fields(fd, &product.cust); + + has_product = true; + } } else { fatal("The requested input file format is not supported"); From 2a93c936a7d562f7d07b0d11deceda3de3819d5d Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 16:32:38 -0700 Subject: [PATCH 07/19] Cleanup and re-tab --- frugen.c | 291 +++++++++++++++++++++++++++---------------------------- 1 file changed, 141 insertions(+), 150 deletions(-) diff --git a/frugen.c b/frugen.c index 385ec67..96747fd 100644 --- a/frugen.c +++ b/frugen.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "fru.h" #include "smbios.h" @@ -94,7 +93,6 @@ bool datestr_to_tv(const char *datestr, struct timeval *tv) tm.tm_isdst = -1; // Use local timezone data in mktime time = mktime(&tm); // Here we have local time since local Epoch tv->tv_sec = time + timezone; // Convert to UTC - printf("Time written: %ld\n", tv->tv_sec); tv->tv_usec = 0; return true; } @@ -223,59 +221,52 @@ bool json_fill_fru_area_custom(json_object *jso, fru_reclist_t **custom) #endif /* __HAS_JSON__ */ static void safe_read(int fd, void *buffer, size_t length) { - ssize_t bytes_read = read(fd, buffer, length); - if (bytes_read != length) - fatal("Error reading file"); + ssize_t bytes_read = read(fd, buffer, length); + if (bytes_read != length) + fatal("Error reading file"); } static void fd_read_field(int fd, uint8_t *out) { - uint8_t typelen; - safe_read(fd, &typelen, 1); - // TODO check type, for now we assume it is 0b11 - //if (!FRU_ISTYPE(typelen, TEXT)) - // fatal("Unsupported data type for binary format"); - size_t length = FRU_FIELDDATALEN(typelen); - fru_field_t *field = calloc(1, FRU_FIELDSIZE(typelen)); - if (field == NULL) - fatal("Could not allocate field"); - - field->typelen = typelen; - safe_read(fd, &(field->data), length); - - char *data = fru_decode_data(field); - if (data == NULL) - fatal("Could not decode field"); - - memcpy(out, data, strlen(data) + 1); - free(data); - free(field); + uint8_t typelen; + safe_read(fd, &typelen, 1); + + size_t length = FRU_FIELDDATALEN(typelen); + fru_field_t *field = calloc(1, FRU_FIELDSIZE(typelen)); + if (field == NULL) + fatal("Could not allocate field"); + field->typelen = typelen; + safe_read(fd, &(field->data), length); + + char *data = fru_decode_data(field); + if (data == NULL) + fatal("Could not decode field"); + + memcpy(out, data, strlen(data) + 1); + free(data); + free(field); } static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { - while (true) { - uint8_t typelen; - safe_read(fd, &typelen, 1); - if (typelen == 0xc1) { - // throw away the next byte, it's an empty field - // indicating that we've reached the end - lseek(fd, 1, SEEK_CUR); - break; - } - - fru_reclist_t *custom_field = add_reclist(reclist); - if (custom_field == NULL) - fatal("Error allocating custom field"); - - size_t length = FRU_FIELDDATALEN(typelen); - uint8_t* data = malloc(length + 1); - if (data == NULL) - fatal("Error allocating custom field"); - safe_read(fd, data, length); - data[length] = 0; - - custom_field->rec = fru_encode_data(LEN_AUTO, data); - free(data); - } + while (true) { + uint8_t typelen; + safe_read(fd, &typelen, 1); + if (typelen == 0xc1) + break; + + fru_reclist_t *custom_field = add_reclist(reclist); + if (custom_field == NULL) + fatal("Error allocating custom field"); + + size_t length = FRU_FIELDDATALEN(typelen); + uint8_t *data = malloc(length + 1); + if (data == NULL) + fatal("Error allocating custom field"); + safe_read(fd, data, length); + data[length] = 0; + + custom_field->rec = fru_encode_data(LEN_AUTO, data); + free(data); + } } int main(int argc, char *argv[]) @@ -439,16 +430,16 @@ int main(int argc, char *argv[]) case 'j': // json use_json = true; - if (use_binary) { - fatal("Can't specify --json and --binaryformat together"); - } + if (use_binary) { + fatal("Can't specify --json and --binaryformat together"); + } break; case 'x': // binary use_binary = true; - if (use_json) { - fatal("Can't specify --json and --binaryformat together"); - } + if (use_json) { + fatal("Can't specify --json and --binaryformat together"); + } break; case 'z': // from @@ -533,101 +524,101 @@ int main(int argc, char *argv[]) fatal("JSON support was disabled at compile time"); #endif } - else if (use_binary) { - int fd = open(optarg, O_RDONLY); - if (fd < 0) { - perror(""); - fatal("Failed to open file"); - } - - char common_header[8]; - ssize_t bytes_read = read(fd, common_header, 8); - if (bytes_read != 8) - fatal("Encountered error while reading header"); - - // Common Header Format Version = common_header[0] - // Internal Use Area Starting Offset = common_header[1] - uint16_t chassis_start_offset = 8 * common_header[2]; - uint16_t board_start_offset = 8 * common_header[3]; - uint16_t product_start_offset = 8 * common_header[4]; - // MultiRecord Area Starting Offset = 8 * common_header[5] - // Padding = common_header[6] = 0 - // Checksum = common_header[7] - - // For the above offsets, an offset of 0 is valid and implies - // that the field is not present. - - bool data_has_chassis = chassis_start_offset != 0; - bool data_has_board = board_start_offset != 0; - bool data_has_product = product_start_offset != 0; - - if (data_has_chassis) { - lseek(fd, chassis_start_offset, SEEK_SET); - - uint8_t chassis_header[3]; - safe_read(fd, chassis_header, 3); - if (chassis_header[0] != 1) - fatal("Unsupported Chassis Info Area Format Version"); - // Chassis Info Area Length = 8 * chassis_header[1] - chassis.type = chassis_header[2]; - fd_read_field(fd, chassis.pn); - fd_read_field(fd, chassis.serial); - fd_fill_custom_fields(fd, &chassis.cust); - - has_chassis = true; - } - if (data_has_board) { - lseek(fd, board_start_offset, SEEK_SET); - - uint8_t board_header[3]; - safe_read(fd, board_header, 3); - if (board_header[0] != 1) - fatal("Unsupported Board Info Area Format Version"); - // Board Info Area Length = 8 * board_header[1] - board.lang = board_header[2]; - - uint32_t min_since_epoch = 0; - safe_read(fd, &min_since_epoch, 3); - struct tm tm_1996 = { - .tm_year = 96, - .tm_mon = 0, - .tm_mday = 1 - }; - // The argument to mktime is zoneless - board.tv.tv_sec = mktime(&tm_1996) + 60 * min_since_epoch; - - fd_read_field(fd, board.mfg); - fd_read_field(fd, board.pname); - fd_read_field(fd, board.serial); - fd_read_field(fd, board.pn); - fd_read_field(fd, board.file); - fd_fill_custom_fields(fd, &board.cust); - - has_board = true; - has_bdate = true; - } - if (data_has_product) { - lseek(fd, product_start_offset, SEEK_SET); - - uint8_t product_header[3]; - safe_read(fd, product_header, 3); - if (product_header[0] != 1) - fatal("Unsupported Board Info Area Format Version"); - // Product Info Area Length = 8 * product_header[1] - product.lang = product_header[2]; - - fd_read_field(fd, product.mfg); - fd_read_field(fd, product.pname); - fd_read_field(fd, product.pn); - fd_read_field(fd, product.ver); - fd_read_field(fd, product.serial); - fd_read_field(fd, product.atag); - fd_read_field(fd, product.file); - fd_fill_custom_fields(fd, &product.cust); - - has_product = true; - } - } + else if (use_binary) { + int fd = open(optarg, O_RDONLY); + if (fd < 0) { + perror(""); + fatal("Failed to open file"); + } + + char common_header[8]; + ssize_t bytes_read = read(fd, common_header, 8); + if (bytes_read != 8) + fatal("Encountered error while reading header"); + + // Common Header Format Version = common_header[0] + // Internal Use Area Starting Offset = common_header[1] + uint16_t chassis_start_offset = 8 * common_header[2]; + uint16_t board_start_offset = 8 * common_header[3]; + uint16_t product_start_offset = 8 * common_header[4]; + // MultiRecord Area Starting Offset = 8 * common_header[5] + // Padding = common_header[6] = 0 + // Checksum = common_header[7] + + // For the above offsets, an offset of 0 is valid and implies + // that the field is not present. + + bool data_has_chassis = chassis_start_offset != 0; + bool data_has_board = board_start_offset != 0; + bool data_has_product = product_start_offset != 0; + + if (data_has_chassis) { + lseek(fd, chassis_start_offset, SEEK_SET); + + uint8_t chassis_header[3]; + safe_read(fd, chassis_header, 3); + if (chassis_header[0] != 1) + fatal("Unsupported Chassis Info Area Format Version"); + // Chassis Info Area Length = 8 * chassis_header[1] + chassis.type = chassis_header[2]; + fd_read_field(fd, chassis.pn); + fd_read_field(fd, chassis.serial); + fd_fill_custom_fields(fd, &chassis.cust); + + has_chassis = true; + } + if (data_has_board) { + lseek(fd, board_start_offset, SEEK_SET); + + uint8_t board_header[3]; + safe_read(fd, board_header, 3); + if (board_header[0] != 1) + fatal("Unsupported Board Info Area Format Version"); + // Board Info Area Length = 8 * board_header[1] + board.lang = board_header[2]; + + uint32_t min_since_1996 = 0; + safe_read(fd, &min_since_1996, 3); + struct tm tm_1996 = { + .tm_year = 96, + .tm_mon = 0, + .tm_mday = 1 + }; + // The argument to mktime is zoneless + board.tv.tv_sec = mktime(&tm_1996) + 60 * min_since_1996; + + fd_read_field(fd, board.mfg); + fd_read_field(fd, board.pname); + fd_read_field(fd, board.serial); + fd_read_field(fd, board.pn); + fd_read_field(fd, board.file); + fd_fill_custom_fields(fd, &board.cust); + + has_board = true; + has_bdate = true; + } + if (data_has_product) { + lseek(fd, product_start_offset, SEEK_SET); + + uint8_t product_header[3]; + safe_read(fd, product_header, 3); + if (product_header[0] != 1) + fatal("Unsupported Board Info Area Format Version"); + // Product Info Area Length = 8 * product_header[1] + product.lang = product_header[2]; + + fd_read_field(fd, product.mfg); + fd_read_field(fd, product.pname); + fd_read_field(fd, product.pn); + fd_read_field(fd, product.ver); + fd_read_field(fd, product.serial); + fd_read_field(fd, product.atag); + fd_read_field(fd, product.file); + fd_fill_custom_fields(fd, &product.cust); + + has_product = true; + } + } else { fatal("The requested input file format is not supported"); } From ebf5bf3d074fd297f98c04531907920b0eee328f Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 16:42:43 -0700 Subject: [PATCH 08/19] close the file after reading it --- frugen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frugen.c b/frugen.c index 96747fd..9e77caf 100644 --- a/frugen.c +++ b/frugen.c @@ -618,6 +618,8 @@ int main(int argc, char *argv[]) has_product = true; } + + close(fd); } else { fatal("The requested input file format is not supported"); From 8e38de17f652079d21b1841c10b6ce926dfe9a95 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Mon, 20 Jul 2020 16:59:22 -0700 Subject: [PATCH 09/19] Improve safety as suggested by the quality review --- frugen.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frugen.c b/frugen.c index 9e77caf..8eb057b 100644 --- a/frugen.c +++ b/frugen.c @@ -221,9 +221,15 @@ bool json_fill_fru_area_custom(json_object *jso, fru_reclist_t **custom) #endif /* __HAS_JSON__ */ static void safe_read(int fd, void *buffer, size_t length) { - ssize_t bytes_read = read(fd, buffer, length); - if (bytes_read != length) - fatal("Error reading file"); + size_t total_bytes_read = 0; + while (total_bytes_read != length) { + ssize_t bytes_read = read( + fd, buffer + total_bytes_read, length - total_bytes_read); + if (bytes_read == -1) + fatal("Error reading file"); + + total_bytes_read += bytes_read; + } } static void fd_read_field(int fd, uint8_t *out) { @@ -241,7 +247,7 @@ static void fd_read_field(int fd, uint8_t *out) { if (data == NULL) fatal("Could not decode field"); - memcpy(out, data, strlen(data) + 1); + memcpy(out, data, strnlen(data, 2 * length) + 1); free(data); free(field); } @@ -532,9 +538,7 @@ int main(int argc, char *argv[]) } char common_header[8]; - ssize_t bytes_read = read(fd, common_header, 8); - if (bytes_read != 8) - fatal("Encountered error while reading header"); + safe_read(fd, common_header, 8); // Common Header Format Version = common_header[0] // Internal Use Area Starting Offset = common_header[1] From 632f5f3bc98e5aa9f0119b889cec60a9fa034b84 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 10:17:17 -0700 Subject: [PATCH 10/19] Rename binaryformat to raw --- frugen.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frugen.c b/frugen.c index 8eb057b..b1ed2c7 100644 --- a/frugen.c +++ b/frugen.c @@ -319,8 +319,8 @@ int main(int argc, char *argv[]) /* Set input file format to JSON */ { .name = "json", .val = 'j', .has_arg = false }, - /* Set input file format to binary */ - { .name = "binaryformat", .val = 'x', .has_arg = false }, + /* Set input file format to raw binary */ + { .name = "raw", .val = 'r', .has_arg = false }, /* Set file to load the data from */ { .name = "from", .val = 'z', .has_arg = true }, @@ -360,7 +360,7 @@ int main(int argc, char *argv[]) "\n\t\t" "There must be an even number of characters in a 'binary' argument", ['j'] = "Set input text file format to JSON (default). Specify before '--from'", - ['x'] = "Set input file format to binary. Specify before '--from'", + ['r'] = "Set input file format to raw binary. Specify before '--from'", ['z'] = "Load FRU information from a text file", /* Chassis info area related options */ ['t'] = "Set chassis type (hex). Defaults to 0x02 ('Unknown')", @@ -437,14 +437,14 @@ int main(int argc, char *argv[]) case 'j': // json use_json = true; if (use_binary) { - fatal("Can't specify --json and --binaryformat together"); + fatal("Can't specify --json and --raw together"); } break; - case 'x': // binary + case 'r': // binary use_binary = true; if (use_json) { - fatal("Can't specify --json and --binaryformat together"); + fatal("Can't specify --json and --raw together"); } break; From d30800d0bc19eff662d5735cffa9adae1c4944eb Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 10:48:43 -0700 Subject: [PATCH 11/19] Change error message --- frugen.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frugen.c b/frugen.c index b1ed2c7..c824153 100644 --- a/frugen.c +++ b/frugen.c @@ -533,8 +533,7 @@ int main(int argc, char *argv[]) else if (use_binary) { int fd = open(optarg, O_RDONLY); if (fd < 0) { - perror(""); - fatal("Failed to open file"); + fatal("Failed to open file: %s", strerror(errno)); } char common_header[8]; From efa60c0f823a0e4960b7e16f410dd34b4fc682fe Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Wed, 22 Jul 2020 16:01:11 -0700 Subject: [PATCH 12/19] Rename encoder functions --- fru.c | 6 +++--- fru.h | 6 +++--- frugen.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fru.c b/fru.c index 0020465..264ae91 100644 --- a/fru.c +++ b/fru.c @@ -537,7 +537,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis) ///< [in] Exploded chassis info area +fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis) ///< [in] Exploded chassis info area { int i; @@ -584,7 +584,7 @@ fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis) /// * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_board_area_t * fru_board_info(const fru_exploded_board_t *board) ///< [in] Exploded board information area +fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< [in] Exploded board information area { int i; @@ -629,7 +629,7 @@ fru_board_area_t * fru_board_info(const fru_exploded_board_t *board) ///< [in] E * @returns fru_info_area_t *area A newly allocated buffer containing the created area * */ -fru_product_area_t * fru_product_info(const fru_exploded_product_t *product) ///< [in] Exploded product information area +fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product) ///< [in] Exploded product information area { int i; diff --git a/fru.h b/fru.h index bf696a5..847fbe0 100644 --- a/fru.h +++ b/fru.h @@ -226,9 +226,9 @@ typedef struct { #define fru_loadfield(eafield, value) strncpy(eafield, value, FRU_FIELDMAXLEN) -fru_chassis_area_t * fru_chassis_info(const fru_exploded_chassis_t *chassis); -fru_board_area_t * fru_board_info(const fru_exploded_board_t *board); -fru_product_area_t * fru_product_info(const fru_exploded_product_t *product); +fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis); +fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); +fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); fru_field_t * fru_encode_data(int len, const uint8_t *data); unsigned char * fru_decode_data(const fru_field_t *field); fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); diff --git a/frugen.c b/frugen.c index c824153..b5e43e9 100644 --- a/frugen.c +++ b/frugen.c @@ -757,7 +757,7 @@ int main(int argc, char *argv[]) fru_chassis_area_t *ci = NULL; debug(1, "FRU file will have a chassis information area"); debug(3, "Chassis information area's custom field list is %p", chassis.cust); - ci = fru_chassis_info(&chassis); + ci = fru_encode_chassis_info(&chassis); e = errno; free_reclist(chassis.cust); @@ -781,7 +781,7 @@ int main(int argc, char *argv[]) board.tv = (struct timeval){0}; } - bi = fru_board_info(&board); + bi = fru_encode_board_info(&board); e = errno; free_reclist(board.cust); @@ -798,7 +798,7 @@ int main(int argc, char *argv[]) fru_product_area_t *pi = NULL; debug(1, "FRU file will have a product information area"); debug(3, "Product information area's custom field list is %p", product.cust); - pi = fru_product_info(&product); + pi = fru_encode_product_info(&product); e = errno; free_reclist(product.cust); From 06c2eb1b4d75721a4f045c23564b190069f6e19e Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Wed, 22 Jul 2020 16:01:39 -0700 Subject: [PATCH 13/19] Change decoder functions to expect pre-allocated buffers In the next patch this will allow avoiding some memcpys. --- fru.c | 40 ++++++++++++++++++++++------------------ fru.h | 3 ++- frugen.c | 4 +++- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/fru.c b/fru.c index 264ae91..7493694 100644 --- a/fru.c +++ b/fru.c @@ -170,11 +170,14 @@ static fru_field_t *fru_encode_6bit(const unsigned char *s /**< [in] Input strin } /** - * Allocate a buffer and decode a 6-bit ASCII string from it + * Decode a 6-bit ASCII string + * + * Return false if there were errors during decoding and true otherwise. */ -static unsigned char *fru_decode_6bit(const fru_field_t *field) +static bool fru_decode_6bit(const fru_field_t *field, + uint8_t *out, //< [out] buffer to decode into + size_t out_len) //< [in] length of output buffer { - unsigned char *out = NULL; const unsigned char *s6; int len, len6bit; int i, i6; @@ -185,8 +188,8 @@ static unsigned char *fru_decode_6bit(const fru_field_t *field) s6 = field->data; len = FRU_6BIT_FULLLENGTH(len6bit); - if (!(out = malloc(len + 1))) { - return out; + if (out_len < (len + 1)) { + return false; } DEBUG("Allocated a destination buffer at %p\n", out); memset(out, 0, len + 1); @@ -224,7 +227,7 @@ static unsigned char *fru_decode_6bit(const fru_field_t *field) // string that was a byte shorter than a multiple of 4. cut_tail(out); - return out; + return true; } /** @@ -280,23 +283,26 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data) } /** - * Allocate a buffer and decode the data from it. + * Decode data from a buffer into another buffer. * * For binary data use FRU_FIELDDATALEN(field->typelen) to find - * out the size of the returned buffer. + * out the size of valid bytes in the returned buffer. + * + * Return false if there were errors during decoding and true otherwise. */ -unsigned char * fru_decode_data(const fru_field_t *field) +bool fru_decode_data(const fru_field_t *field, + uint8_t *out, //< [out] buffer to decode into + size_t out_len) // <[in] length of output buffer { - unsigned char * out; if (!field) return NULL; if (FRU_ISTYPE(field->typelen, ASCII_6BIT)) { - out = fru_decode_6bit(field); + return fru_decode_6bit(field, out, out_len); } else { - out = malloc(FRU_FIELDDATALEN(field->typelen) + 1); - if (!out) return NULL; + if (out_len < (FRU_FIELDDATALEN(field->typelen) + 1)) + return false; if (FRU_ISTYPE(field->typelen, BCDPLUS)) { int i; @@ -329,7 +335,7 @@ unsigned char * fru_decode_data(const fru_field_t *field) } } - return out; + return true; } #if 0 @@ -788,7 +794,7 @@ void test_encodings(void) for(i = 0; i < ARRAY_SZ(test_strings); i++) { fru_field_t *field; - const unsigned char *out; + const unsigned char out[FRU_FIELDMAXARRAY]; printf("Data set %d.\n", i); printf("Original data "); @@ -824,8 +830,7 @@ void test_encodings(void) dump(FRU_FIELDSIZE(field->typelen), (uint8_t *)field); printf("Decoding... "); - out = fru_decode_data(field); - if (!out) { + if (!fru_decode_data(field->typelen, out, FRU_FIELDMAXARRAY)) { printf("FAIL!"); goto next; } @@ -848,7 +853,6 @@ void test_encodings(void) printf("FAIL!"); } - free((void *)out); next: free((void *)field); printf("\n\n"); diff --git a/fru.h b/fru.h index 847fbe0..1149d77 100644 --- a/fru.h +++ b/fru.h @@ -5,6 +5,7 @@ #ifndef __FRULIB_FRU_H__ #define __FRULIB_FRU_H__ +#include #include #include #include @@ -230,7 +231,7 @@ fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chass fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); fru_field_t * fru_encode_data(int len, const uint8_t *data); -unsigned char * fru_decode_data(const fru_field_t *field); +bool fru_decode_data(const fru_field_t *field, uint8_t *out, size_t out_len); fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); #endif // __FRULIB_FRU_H__ diff --git a/frugen.c b/frugen.c index b5e43e9..f925d14 100644 --- a/frugen.c +++ b/frugen.c @@ -243,8 +243,10 @@ static void fd_read_field(int fd, uint8_t *out) { field->typelen = typelen; safe_read(fd, &(field->data), length); - char *data = fru_decode_data(field); + char *data = malloc(2 * length + 1); if (data == NULL) + fatal("Could not allocate memory"); + if(!fru_decode_data(field, data, 2 * length + 1)) fatal("Could not decode field"); memcpy(out, data, strnlen(data, 2 * length) + 1); From 6bae19cad2717914cd4fdd9118f74e4f8cb30208 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Wed, 22 Jul 2020 16:02:43 -0700 Subject: [PATCH 14/19] Move logic into library and create new file for reading functions --- CMakeLists.txt | 4 ++- fatal.h | 7 ++++ fru.c | 62 ++++++++++++++++++++++++++++++++--- fru.h | 3 +- fru_reader.c | 68 ++++++++++++++++++++++++++++++++++++++ fru_reader.h | 5 +++ frugen.c | 88 ++++++++------------------------------------------ 7 files changed, 155 insertions(+), 82 deletions(-) create mode 100644 fatal.h create mode 100644 fru_reader.c create mode 100644 fru_reader.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c1b91c..5746228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,12 +27,13 @@ string(REPLACE "-" "." gitver "${gitver}") add_definitions(-DVERSION="${gitver}") add_executable(frugen frugen.c) +add_library(fru_reader STATIC fru_reader.c) add_library(fru-static STATIC fru.c) add_library(fru-shared SHARED fru.c) find_library(JSON_LIB json-c) SET_TARGET_PROPERTIES(fru-static PROPERTIES OUTPUT_NAME fru CLEAN_DIRECT_OUTPUT 1) SET_TARGET_PROPERTIES(fru-shared PROPERTIES OUTPUT_NAME fru CLEAN_DIRECT_OUTPUT 1) -target_link_libraries(frugen fru-static) +target_link_libraries(frugen fru-static fru_reader) if (JSON_LIB) message (STATUS "Using JSON Library found at " ${JSON_LIB}) add_definitions(-D__HAS_JSON__) @@ -42,6 +43,7 @@ else (JSON_LIB) endif (JSON_LIB) # To make frugen 32-bit, uncomment the following lines or use an external toolchain file +# set_target_properties(fru_reader-static PROPERTIES OUTPUT_NAME fru CLEAN_DIRECT_OUTPUT 1) #set_target_properties(frugen PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") #set_target_properties(fru-static PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") #set_target_properties(fru-shared PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") diff --git a/fatal.h b/fatal.h new file mode 100644 index 0000000..1930e62 --- /dev/null +++ b/fatal.h @@ -0,0 +1,7 @@ +#pragma once + +#define fatal(fmt, args...) do { \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "\n"); \ + exit(1); \ +} while(0) diff --git a/fru.c b/fru.c index 7493694..7814f51 100644 --- a/fru.c +++ b/fru.c @@ -182,7 +182,7 @@ static bool fru_decode_6bit(const fru_field_t *field, int len, len6bit; int i, i6; - if (!field) return out; + if (!field) return false; len6bit = FRU_FIELDDATALEN(field->typelen); s6 = field->data; @@ -290,12 +290,11 @@ fru_field_t * fru_encode_data(int len, const uint8_t *data) * * Return false if there were errors during decoding and true otherwise. */ -bool fru_decode_data(const fru_field_t *field, +bool fru_decode_data(fru_field_t *field, uint8_t *out, //< [out] buffer to decode into size_t out_len) // <[in] length of output buffer { - - if (!field) return NULL; + if (!field) return false; if (FRU_ISTYPE(field->typelen, ASCII_6BIT)) { return fru_decode_6bit(field, out, out_len); @@ -525,6 +524,33 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t return out; } +static bool fru_decode_custom_fields(const uint8_t *data, fru_reclist_t **reclist) { + fru_field_t *field = NULL; + + while (true) { + field = data; + if (field->typelen == 0xc1) + break; + + fru_reclist_t *custom_field = add_reclist(reclist); + if (custom_field == NULL) + return false; + + // Create a NUL terminated version of the data for encoding + // TODO pass the length into fru_encode_data instead + size_t length = FRU_FIELDDATALEN(field->typelen); + uint8_t *buff = malloc(length + 1); + if (buff == NULL) + return false; + memcpy(buff, field->data, length); + buff[length] = 0; + custom_field->rec = fru_encode_data(LEN_AUTO, buff); + data += FRU_FIELDSIZE(field->typelen); + } + + return true; +} + /** * Allocate and build a Chassis Information Area block. * @@ -572,6 +598,31 @@ fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chass return out; } +bool fru_decode_chassis_info( + const fru_chassis_area_t *area, //< [in] encoded chassis + fru_exploded_chassis_t *chassis_out //< [out] +) +{ + chassis_out->type = area->langtype; + const uint8_t *data = area->data; + fru_field_t *field = (fru_field_t*)data; + + if(!fru_decode_data(field, chassis_out->pn, + sizeof(chassis_out->pn))) + return false; + + data += FRU_FIELDSIZE(field->typelen); + field = data; + if (!fru_decode_data(field, chassis_out->serial, + sizeof(chassis_out->serial))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &chassis_out->cust); + + return true; +} + /** * Allocate and build a Board Information Area block. * @@ -830,7 +881,8 @@ void test_encodings(void) dump(FRU_FIELDSIZE(field->typelen), (uint8_t *)field); printf("Decoding... "); - if (!fru_decode_data(field->typelen, out, FRU_FIELDMAXARRAY)) { + if (!fru_decode_data(field->typelen, &field->data, out, + FRU_FIELDMAXARRAY)) { printf("FAIL!"); goto next; } diff --git a/fru.h b/fru.h index 1149d77..e26d11f 100644 --- a/fru.h +++ b/fru.h @@ -228,10 +228,11 @@ typedef struct { #define fru_loadfield(eafield, value) strncpy(eafield, value, FRU_FIELDMAXLEN) fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis); +bool fru_decode_chassis_info(const fru_chassis_area_t *area, fru_exploded_chassis_t *chassis_out); fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); fru_field_t * fru_encode_data(int len, const uint8_t *data); -bool fru_decode_data(const fru_field_t *field, uint8_t *out, size_t out_len); +bool fru_decode_data(fru_field_t *field, uint8_t *out, size_t out_len); fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); #endif // __FRULIB_FRU_H__ diff --git a/fru_reader.c b/fru_reader.c new file mode 100644 index 0000000..a01fb13 --- /dev/null +++ b/fru_reader.c @@ -0,0 +1,68 @@ +#include +#include +#include "fru.h" +#include "fru_reader.h" +#include "fatal.h" + +void safe_read(int fd, void *buffer, size_t length) { + size_t total_bytes_read = 0; + while (total_bytes_read != length) { + ssize_t bytes_read = read( + fd, buffer + total_bytes_read, length - total_bytes_read); + if (bytes_read == -1) + fatal("Error reading file"); + + total_bytes_read += bytes_read; + } +} + +void fd_read_field(int fd, uint8_t *out) { + fru_field_t *field = malloc(FRU_FIELDMAXARRAY); + if (!field) + fatal("Failed to allocate field"); + safe_read(fd, &field->typelen, 1); + + size_t length = FRU_FIELDDATALEN(field->typelen); + safe_read(fd, &field->data, length); + + if(!fru_decode_data(field, out, FRU_FIELDMAXARRAY)) + fatal("Could not decode field"); +} + +void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { + while (true) { + uint8_t typelen; + safe_read(fd, &typelen, 1); + if (typelen == 0xc1) + break; + + fru_reclist_t *custom_field = add_reclist(reclist); + if (custom_field == NULL) + fatal("Error allocating custom field"); + + size_t length = FRU_FIELDDATALEN(typelen); + uint8_t *data = malloc(length + 1); + if (data == NULL) + fatal("Error allocating custom field"); + safe_read(fd, data, length); + data[length] = 0; + + custom_field->rec = fru_encode_data(LEN_AUTO, data); + free(data); + } +} + + +/** + * Allocate and read a fru_chassis_area_t from a file descriptor + */ +fru_chassis_area_t *read_fru_chassis_area(int fd) { + size_t base_len = sizeof(fru_chassis_area_t); + fru_chassis_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; +} diff --git a/fru_reader.h b/fru_reader.h new file mode 100644 index 0000000..750b3e5 --- /dev/null +++ b/fru_reader.h @@ -0,0 +1,5 @@ +#include "fru.h" +fru_chassis_area_t *read_fru_chassis_area(int fd); +void safe_read(int fd, void *buffer, size_t length); +void fd_read_field(int fd, uint8_t *out); +void fd_fill_custom_fields(int fd, fru_reclist_t **reclist); diff --git a/frugen.c b/frugen.c index f925d14..970e9f7 100644 --- a/frugen.c +++ b/frugen.c @@ -15,18 +15,14 @@ #include #include #include "fru.h" +#include "fru_reader.h" #include "smbios.h" +#include "fatal.h" #ifdef __HAS_JSON__ #include #endif -#define fatal(fmt, args...) do { \ - fprintf(stderr, fmt, ##args); \ - fprintf(stderr, "\n"); \ - exit(1); \ -} while(0) - volatile int debug_level = 0; #define debug(level, fmt, args...) do { \ int e = errno; \ @@ -39,7 +35,6 @@ volatile int debug_level = 0; } \ } while(0) - /** * Convert 2 bytes of hex string into a binary byte */ @@ -220,63 +215,6 @@ bool json_fill_fru_area_custom(json_object *jso, fru_reclist_t **custom) } #endif /* __HAS_JSON__ */ -static void safe_read(int fd, void *buffer, size_t length) { - size_t total_bytes_read = 0; - while (total_bytes_read != length) { - ssize_t bytes_read = read( - fd, buffer + total_bytes_read, length - total_bytes_read); - if (bytes_read == -1) - fatal("Error reading file"); - - total_bytes_read += bytes_read; - } -} - -static void fd_read_field(int fd, uint8_t *out) { - uint8_t typelen; - safe_read(fd, &typelen, 1); - - size_t length = FRU_FIELDDATALEN(typelen); - fru_field_t *field = calloc(1, FRU_FIELDSIZE(typelen)); - if (field == NULL) - fatal("Could not allocate field"); - field->typelen = typelen; - safe_read(fd, &(field->data), length); - - char *data = malloc(2 * length + 1); - if (data == NULL) - fatal("Could not allocate memory"); - if(!fru_decode_data(field, data, 2 * length + 1)) - fatal("Could not decode field"); - - memcpy(out, data, strnlen(data, 2 * length) + 1); - free(data); - free(field); -} - -static void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { - while (true) { - uint8_t typelen; - safe_read(fd, &typelen, 1); - if (typelen == 0xc1) - break; - - fru_reclist_t *custom_field = add_reclist(reclist); - if (custom_field == NULL) - fatal("Error allocating custom field"); - - size_t length = FRU_FIELDDATALEN(typelen); - uint8_t *data = malloc(length + 1); - if (data == NULL) - fatal("Error allocating custom field"); - safe_read(fd, data, length); - data[length] = 0; - - custom_field->rec = fru_encode_data(LEN_AUTO, data); - free(data); - } -} - int main(int argc, char *argv[]) { int i; @@ -558,17 +496,17 @@ int main(int argc, char *argv[]) bool data_has_product = product_start_offset != 0; if (data_has_chassis) { - lseek(fd, chassis_start_offset, SEEK_SET); - - uint8_t chassis_header[3]; - safe_read(fd, chassis_header, 3); - if (chassis_header[0] != 1) - fatal("Unsupported Chassis Info Area Format Version"); - // Chassis Info Area Length = 8 * chassis_header[1] - chassis.type = chassis_header[2]; - fd_read_field(fd, chassis.pn); - fd_read_field(fd, chassis.serial); - fd_fill_custom_fields(fd, &chassis.cust); + lseek(fd, chassis_start_offset, SEEK_SET); + fru_chassis_area_t *chassis_raw = + read_fru_chassis_area(fd); + bool success = fru_decode_chassis_info( + chassis_raw, &chassis); + if (!success) + fatal("Failed to decode chassis!"); + free(chassis_raw); + + printf("Loaded chassis serial '%s'\n", chassis.serial); + printf("Loaded chassis pn '%s'\n", chassis.pn); has_chassis = true; } From 2dd511d184423ff1f5b9e50dbbf3c9f361c16b58 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 15:20:40 -0700 Subject: [PATCH 15/19] Update board decoding --- fru.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- fru.h | 3 ++- fru_reader.c | 14 +++++++++++++ fru_reader.h | 4 +++- frugen.c | 25 ++-------------------- 5 files changed, 77 insertions(+), 27 deletions(-) diff --git a/fru.c b/fru.c index 7814f51..6f11b0f 100644 --- a/fru.c +++ b/fru.c @@ -528,7 +528,7 @@ static bool fru_decode_custom_fields(const uint8_t *data, fru_reclist_t **reclis fru_field_t *field = NULL; while (true) { - field = data; + field = (fru_field_t*)data; if (field->typelen == 0xc1) break; @@ -612,7 +612,7 @@ bool fru_decode_chassis_info( return false; data += FRU_FIELDSIZE(field->typelen); - field = data; + field = (fru_field_t*)data; if (!fru_decode_data(field, chassis_out->serial, sizeof(chassis_out->serial))) return false; @@ -668,6 +668,60 @@ fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< return out; } +bool fru_decode_board_info( + const fru_board_area_t *area, //< [in] encoded chassis + fru_exploded_board_t *board_out //< [out] +) +{ + fru_field_t *field; + const uint8_t *data = area->data; + + board_out->lang = area->langtype; + + uint32_t *min_since_1996 = (uint32_t*)&(area->mfgdate); + struct tm tm_1996 = { + .tm_year = 96, + .tm_mon = 0, + .tm_mday = 1 + }; + // The argument to mktime is zoneless + board_out->tv.tv_sec = mktime(&tm_1996) + 60 * (*min_since_1996); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, board_out->mfg, + sizeof(board_out->mfg))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, board_out->pname, + sizeof(board_out->pname))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, board_out->serial, + sizeof(board_out->serial))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, board_out->pn, + sizeof(board_out->pn))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, board_out->file, + sizeof(board_out->file))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &board_out->cust); + + return true; +} + /** * Allocate and build a Product Information Area block. * diff --git a/fru.h b/fru.h index e26d11f..2cbce7a 100644 --- a/fru.h +++ b/fru.h @@ -153,7 +153,7 @@ typedef fru_info_area_t fru_chassis_area_t; typedef struct fru_board_area_s { FRU_INFO_AREA_HEADER; - uint8_t mfgdate[3]; ///< Manufacturing date/time in seconds since 1996/1/1 0:00 + uint8_t mfgdate[3]; ///< Manufacturing date/time in minutes since 1996/1/1 0:00 uint8_t data[]; ///< Variable size (multiple of 8 bytes) data with tail padding and checksum } fru_board_area_t; @@ -230,6 +230,7 @@ typedef struct { fru_chassis_area_t * fru_encode_chassis_info(const fru_exploded_chassis_t *chassis); bool fru_decode_chassis_info(const fru_chassis_area_t *area, fru_exploded_chassis_t *chassis_out); fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); +bool fru_decode_board_info(const fru_board_area_t *area, fru_exploded_board_t *board_out); fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); fru_field_t * fru_encode_data(int len, const uint8_t *data); bool fru_decode_data(fru_field_t *field, uint8_t *out, size_t out_len); diff --git a/fru_reader.c b/fru_reader.c index a01fb13..28b71d3 100644 --- a/fru_reader.c +++ b/fru_reader.c @@ -66,3 +66,17 @@ fru_chassis_area_t *read_fru_chassis_area(int fd) { return area; } + +/** + * Allocate and read a fru_board_area_t from a file descriptor + */ +fru_board_area_t *read_fru_board_area(int fd) { + size_t base_len = sizeof(fru_board_area_t); + fru_board_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; +} diff --git a/fru_reader.h b/fru_reader.h index 750b3e5..65c602f 100644 --- a/fru_reader.h +++ b/fru_reader.h @@ -1,5 +1,7 @@ #include "fru.h" -fru_chassis_area_t *read_fru_chassis_area(int fd); void safe_read(int fd, void *buffer, size_t length); void fd_read_field(int fd, uint8_t *out); void fd_fill_custom_fields(int fd, fru_reclist_t **reclist); + +fru_chassis_area_t *read_fru_chassis_area(int fd); +fru_board_area_t *read_fru_board_area(int fd); diff --git a/frugen.c b/frugen.c index 970e9f7..7c1cfd4 100644 --- a/frugen.c +++ b/frugen.c @@ -513,29 +513,8 @@ int main(int argc, char *argv[]) if (data_has_board) { lseek(fd, board_start_offset, SEEK_SET); - uint8_t board_header[3]; - safe_read(fd, board_header, 3); - if (board_header[0] != 1) - fatal("Unsupported Board Info Area Format Version"); - // Board Info Area Length = 8 * board_header[1] - board.lang = board_header[2]; - - uint32_t min_since_1996 = 0; - safe_read(fd, &min_since_1996, 3); - struct tm tm_1996 = { - .tm_year = 96, - .tm_mon = 0, - .tm_mday = 1 - }; - // The argument to mktime is zoneless - board.tv.tv_sec = mktime(&tm_1996) + 60 * min_since_1996; - - fd_read_field(fd, board.mfg); - fd_read_field(fd, board.pname); - fd_read_field(fd, board.serial); - fd_read_field(fd, board.pn); - fd_read_field(fd, board.file); - fd_fill_custom_fields(fd, &board.cust); + fru_board_area_t *board_raw = read_fru_board_area(fd); + bool success = fru_decode_board_info(board_raw, &board); has_board = true; has_bdate = true; From f2b7f22078370b3703ce4a25c61bd73472b080e2 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 15:24:52 -0700 Subject: [PATCH 16/19] Update product decoding --- fru.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-- fru.h | 1 + fru_reader.c | 51 ++++++++++++------------------------------ fru_reader.h | 4 +--- frugen.c | 19 ++++------------ 5 files changed, 80 insertions(+), 57 deletions(-) diff --git a/fru.c b/fru.c index 6f11b0f..6a779f3 100644 --- a/fru.c +++ b/fru.c @@ -496,7 +496,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t // Now fill the output buffer. First copy the header. memcpy(outp, &header, headerlen); outp += headerlen; - + DEBUG("area size is %d (%d) bytes\n", totalsize, FRU_BYTES(header.blocks)); DEBUG("area size in header is (%d) bytes\n", FRU_BYTES(((fru_info_area_t *)out)->blocks)); @@ -669,7 +669,7 @@ fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board) ///< } bool fru_decode_board_info( - const fru_board_area_t *area, //< [in] encoded chassis + const fru_board_area_t *area, //< [in] encoded board fru_exploded_board_t *board_out //< [out] ) { @@ -771,6 +771,64 @@ fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *produ return out; } + +bool fru_decode_product_info( + const fru_product_area_t *area, //< [in] encoded product + fru_exploded_product_t *product_out //< [out] +) +{ + fru_field_t *field; + const uint8_t *data = area->data; + + product_out->lang = area->langtype; + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->mfg, + sizeof(product_out->mfg))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->pname, + sizeof(product_out->pname))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->pn, + sizeof(product_out->pn))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->ver, + sizeof(product_out->ver))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->serial, + sizeof(product_out->serial))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->atag, + sizeof(product_out->atag))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + field = (fru_field_t*)data; + if (!fru_decode_data(field, product_out->file, + sizeof(product_out->file))) + return false; + data += FRU_FIELDSIZE(field->typelen); + + fru_decode_custom_fields(data, &product_out->cust); + + return true; +} + /** * Create a FRU information file. * diff --git a/fru.h b/fru.h index 2cbce7a..ef353be 100644 --- a/fru.h +++ b/fru.h @@ -232,6 +232,7 @@ bool fru_decode_chassis_info(const fru_chassis_area_t *area, fru_exploded_chassi fru_board_area_t * fru_encode_board_info(const fru_exploded_board_t *board); bool fru_decode_board_info(const fru_board_area_t *area, fru_exploded_board_t *board_out); fru_product_area_t * fru_encode_product_info(const fru_exploded_product_t *product); +bool fru_decode_product_info(const fru_product_area_t *area, fru_exploded_product_t *product_out); fru_field_t * fru_encode_data(int len, const uint8_t *data); bool fru_decode_data(fru_field_t *field, uint8_t *out, size_t out_len); fru_t * fru_create(fru_area_t area[FRU_MAX_AREAS], size_t *size); diff --git a/fru_reader.c b/fru_reader.c index 28b71d3..fa8c54f 100644 --- a/fru_reader.c +++ b/fru_reader.c @@ -16,43 +16,6 @@ void safe_read(int fd, void *buffer, size_t length) { } } -void fd_read_field(int fd, uint8_t *out) { - fru_field_t *field = malloc(FRU_FIELDMAXARRAY); - if (!field) - fatal("Failed to allocate field"); - safe_read(fd, &field->typelen, 1); - - size_t length = FRU_FIELDDATALEN(field->typelen); - safe_read(fd, &field->data, length); - - if(!fru_decode_data(field, out, FRU_FIELDMAXARRAY)) - fatal("Could not decode field"); -} - -void fd_fill_custom_fields(int fd, fru_reclist_t **reclist) { - while (true) { - uint8_t typelen; - safe_read(fd, &typelen, 1); - if (typelen == 0xc1) - break; - - fru_reclist_t *custom_field = add_reclist(reclist); - if (custom_field == NULL) - fatal("Error allocating custom field"); - - size_t length = FRU_FIELDDATALEN(typelen); - uint8_t *data = malloc(length + 1); - if (data == NULL) - fatal("Error allocating custom field"); - safe_read(fd, data, length); - data[length] = 0; - - custom_field->rec = fru_encode_data(LEN_AUTO, data); - free(data); - } -} - - /** * Allocate and read a fru_chassis_area_t from a file descriptor */ @@ -80,3 +43,17 @@ fru_board_area_t *read_fru_board_area(int fd) { return area; } + +/** + * Allocate and read a fru_product_area_t from a file descriptor + */ +fru_product_area_t *read_fru_product_area(int fd) { + size_t base_len = sizeof(fru_product_area_t); + fru_product_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; +} diff --git a/fru_reader.h b/fru_reader.h index 65c602f..e5bbf8e 100644 --- a/fru_reader.h +++ b/fru_reader.h @@ -1,7 +1,5 @@ #include "fru.h" void safe_read(int fd, void *buffer, size_t length); -void fd_read_field(int fd, uint8_t *out); -void fd_fill_custom_fields(int fd, fru_reclist_t **reclist); - fru_chassis_area_t *read_fru_chassis_area(int fd); fru_board_area_t *read_fru_board_area(int fd); +fru_product_area_t *read_fru_product_area(int fd); diff --git a/frugen.c b/frugen.c index 7c1cfd4..2cf79c7 100644 --- a/frugen.c +++ b/frugen.c @@ -522,21 +522,10 @@ int main(int argc, char *argv[]) if (data_has_product) { lseek(fd, product_start_offset, SEEK_SET); - uint8_t product_header[3]; - safe_read(fd, product_header, 3); - if (product_header[0] != 1) - fatal("Unsupported Board Info Area Format Version"); - // Product Info Area Length = 8 * product_header[1] - product.lang = product_header[2]; - - fd_read_field(fd, product.mfg); - fd_read_field(fd, product.pname); - fd_read_field(fd, product.pn); - fd_read_field(fd, product.ver); - fd_read_field(fd, product.serial); - fd_read_field(fd, product.atag); - fd_read_field(fd, product.file); - fd_fill_custom_fields(fd, &product.cust); + fru_product_area_t *product_raw = + read_fru_product_area(fd); + bool success = + fru_decode_product_info(product_raw, &product); has_product = true; } From 57a0f33bb20753911a36800df74796deb52dc57f Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 15:25:30 -0700 Subject: [PATCH 17/19] Retab --- fru_reader.c | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/fru_reader.c b/fru_reader.c index fa8c54f..7d56a83 100644 --- a/fru_reader.c +++ b/fru_reader.c @@ -5,55 +5,55 @@ #include "fatal.h" void safe_read(int fd, void *buffer, size_t length) { - size_t total_bytes_read = 0; - while (total_bytes_read != length) { - ssize_t bytes_read = read( + size_t total_bytes_read = 0; + while (total_bytes_read != length) { + ssize_t bytes_read = read( fd, buffer + total_bytes_read, length - total_bytes_read); - if (bytes_read == -1) - fatal("Error reading file"); + if (bytes_read == -1) + fatal("Error reading file"); - total_bytes_read += bytes_read; - } + total_bytes_read += bytes_read; + } } /** * Allocate and read a fru_chassis_area_t from a file descriptor */ fru_chassis_area_t *read_fru_chassis_area(int fd) { - size_t base_len = sizeof(fru_chassis_area_t); - fru_chassis_area_t *area = malloc(base_len); - safe_read(fd, area, base_len); - size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); - - return area; + size_t base_len = sizeof(fru_chassis_area_t); + fru_chassis_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; } /** * Allocate and read a fru_board_area_t from a file descriptor */ fru_board_area_t *read_fru_board_area(int fd) { - size_t base_len = sizeof(fru_board_area_t); - fru_board_area_t *area = malloc(base_len); - safe_read(fd, area, base_len); - size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); - - return area; + size_t base_len = sizeof(fru_board_area_t); + fru_board_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; } /** * Allocate and read a fru_product_area_t from a file descriptor */ fru_product_area_t *read_fru_product_area(int fd) { - size_t base_len = sizeof(fru_product_area_t); - fru_product_area_t *area = malloc(base_len); - safe_read(fd, area, base_len); - size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); - - return area; + size_t base_len = sizeof(fru_product_area_t); + fru_product_area_t *area = malloc(base_len); + safe_read(fd, area, base_len); + size_t data_len = 8 * area->blocks; + area = realloc(area, base_len + data_len); + safe_read(fd, &area->data, data_len); + + return area; } From dea002c5e5ff499a961a8c30e2bb4aaf16a821c2 Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Tue, 21 Jul 2020 15:52:27 -0700 Subject: [PATCH 18/19] Read fru header into struct and check for errors --- fru_reader.c | 36 +++++++++++++++++++++++------ fru_reader.h | 2 +- frugen.c | 65 +++++++++++++++++++++++----------------------------- 3 files changed, 59 insertions(+), 44 deletions(-) diff --git a/fru_reader.c b/fru_reader.c index 7d56a83..699ec09 100644 --- a/fru_reader.c +++ b/fru_reader.c @@ -4,28 +4,42 @@ #include "fru_reader.h" #include "fatal.h" -void safe_read(int fd, void *buffer, size_t length) { +static void safe_read(int fd, void *buffer, size_t length) { size_t total_bytes_read = 0; while (total_bytes_read != length) { ssize_t bytes_read = read( fd, buffer + total_bytes_read, length - total_bytes_read); if (bytes_read == -1) fatal("Error reading file"); + if (bytes_read == 0) + fatal("Reached end of file"); total_bytes_read += bytes_read; } } +fru_t *read_fru_header(int fd) { + fru_t *fru = malloc(sizeof(fru_t)); + if (!fru) + return NULL; + safe_read(fd, fru, sizeof(fru_t)); + return fru; +} + /** * Allocate and read a fru_chassis_area_t from a file descriptor */ fru_chassis_area_t *read_fru_chassis_area(int fd) { size_t base_len = sizeof(fru_chassis_area_t); fru_chassis_area_t *area = malloc(base_len); + if (!area) + return NULL; safe_read(fd, area, base_len); size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); + area = realloc(area, data_len); + if (!area) + return NULL; + safe_read(fd, &area->data, data_len - base_len); return area; } @@ -36,10 +50,14 @@ fru_chassis_area_t *read_fru_chassis_area(int fd) { fru_board_area_t *read_fru_board_area(int fd) { size_t base_len = sizeof(fru_board_area_t); fru_board_area_t *area = malloc(base_len); + if (!area) + return NULL; safe_read(fd, area, base_len); size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); + area = realloc(area, data_len); + if (!area) + return NULL; + safe_read(fd, &area->data, data_len - base_len); return area; } @@ -50,10 +68,14 @@ fru_board_area_t *read_fru_board_area(int fd) { fru_product_area_t *read_fru_product_area(int fd) { size_t base_len = sizeof(fru_product_area_t); fru_product_area_t *area = malloc(base_len); + if (!area) + return NULL; safe_read(fd, area, base_len); size_t data_len = 8 * area->blocks; - area = realloc(area, base_len + data_len); - safe_read(fd, &area->data, data_len); + area = realloc(area, data_len); + if (!area) + return NULL; + safe_read(fd, &area->data, data_len - base_len); return area; } diff --git a/fru_reader.h b/fru_reader.h index e5bbf8e..26adf1d 100644 --- a/fru_reader.h +++ b/fru_reader.h @@ -1,5 +1,5 @@ #include "fru.h" -void safe_read(int fd, void *buffer, size_t length); +fru_t *read_fru_header(int fd); fru_chassis_area_t *read_fru_chassis_area(int fd); fru_board_area_t *read_fru_board_area(int fd); fru_product_area_t *read_fru_product_area(int fd); diff --git a/frugen.c b/frugen.c index 2cf79c7..e220992 100644 --- a/frugen.c +++ b/frugen.c @@ -476,60 +476,53 @@ int main(int argc, char *argv[]) fatal("Failed to open file: %s", strerror(errno)); } - char common_header[8]; - safe_read(fd, common_header, 8); - - // Common Header Format Version = common_header[0] - // Internal Use Area Starting Offset = common_header[1] - uint16_t chassis_start_offset = 8 * common_header[2]; - uint16_t board_start_offset = 8 * common_header[3]; - uint16_t product_start_offset = 8 * common_header[4]; - // MultiRecord Area Starting Offset = 8 * common_header[5] - // Padding = common_header[6] = 0 - // Checksum = common_header[7] - - // For the above offsets, an offset of 0 is valid and implies - // that the field is not present. - - bool data_has_chassis = chassis_start_offset != 0; - bool data_has_board = board_start_offset != 0; - bool data_has_product = product_start_offset != 0; - - if (data_has_chassis) { - lseek(fd, chassis_start_offset, SEEK_SET); - fru_chassis_area_t *chassis_raw = - read_fru_chassis_area(fd); - bool success = fru_decode_chassis_info( - chassis_raw, &chassis); - if (!success) - fatal("Failed to decode chassis!"); - free(chassis_raw); - - printf("Loaded chassis serial '%s'\n", chassis.serial); - printf("Loaded chassis pn '%s'\n", chassis.pn); - + fru_t *raw_fru = read_fru_header(fd); + if (!raw_fru) + fatal("Failed to read fru header"); + + if (raw_fru->chassis != 0) { + if (lseek(fd, 8 * raw_fru->chassis, SEEK_SET) < 0) + fatal("Failed to seek"); + + fru_chassis_area_t *chassis_raw = + read_fru_chassis_area(fd); + bool success = fru_decode_chassis_info( + chassis_raw, &chassis); + if (!success) + fatal("Failed to decode chassis"); + + free(chassis_raw); has_chassis = true; } - if (data_has_board) { - lseek(fd, board_start_offset, SEEK_SET); + if (raw_fru->board != 0) { + if(lseek(fd, 8 * raw_fru->board, SEEK_SET) < 0) + fatal("Failed to seek"); fru_board_area_t *board_raw = read_fru_board_area(fd); bool success = fru_decode_board_info(board_raw, &board); + if (!success) + fatal("Failed to decode board"); + free(board_raw); has_board = true; has_bdate = true; } - if (data_has_product) { - lseek(fd, product_start_offset, SEEK_SET); + if (raw_fru->product != 0) { + if (lseek(fd, 8 * raw_fru->product, SEEK_SET) < 0) + fatal("Failed to seek"); fru_product_area_t *product_raw = read_fru_product_area(fd); bool success = fru_decode_product_info(product_raw, &product); + if (!success) + fatal("Failed to decode product"); + free(product_raw); has_product = true; } + free(raw_fru); close(fd); } else { From 4c8b4e42313089f5140d5c0b235fcbd1fcdc8cbe Mon Sep 17 00:00:00 2001 From: Aneesh Durg Date: Wed, 22 Jul 2020 15:31:36 -0700 Subject: [PATCH 19/19] Make suggested changes --- fatal.h | 1 - fru.c | 63 +++++++++++++++++++++++++++------------------------- fru_reader.c | 19 +++++++++------- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/fatal.h b/fatal.h index 1930e62..423f7dd 100644 --- a/fatal.h +++ b/fatal.h @@ -1,5 +1,4 @@ #pragma once - #define fatal(fmt, args...) do { \ fprintf(stderr, fmt, ##args); \ fprintf(stderr, "\n"); \ diff --git a/fru.c b/fru.c index 6a779f3..6e2fd04 100644 --- a/fru.c +++ b/fru.c @@ -29,6 +29,17 @@ #define DEBUG(f, args...) #endif +static time_t epoch_seconds_1996() { + struct tm tm_1996 = { + .tm_year = 96, + .tm_mon = 0, + .tm_mday = 1 + }; + // The argument to mktime is zoneless + return mktime(&tm_1996); +} + + /** * Strip trailing spaces */ @@ -434,13 +445,7 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t if (FRU_AREA_HAS_DATE(atype)) { uint32_t fru_time; - struct tm tm_1996 = { - .tm_year = 96, - .tm_mon = 0, - .tm_mday = 1 - }; const struct timeval tv_unspecified = { 0 }; - struct timeval tv_1996 = { 0 }; if (!tv) { errno = EFAULT; @@ -455,10 +460,8 @@ fru_info_area_t *fru_create_info_area(fru_area_type_t atype, ///< [in] Area t printf("Using FRU_DATE_UNSPECIFIED\n"); fru_time = FRU_DATE_UNSPECIFIED; } else { - // The argument to mktime is zoneless - tv_1996.tv_sec = mktime(&tm_1996); // FRU time is in minutes and we don't care about microseconds - fru_time = (tv->tv_sec - tv_1996.tv_sec) / 60; + fru_time = (tv->tv_sec - epoch_seconds_1996()) / 60; } header.mfgdate[0] = fru_time & 0xFF; header.mfgdate[1] = (fru_time >> 8) & 0xFF; @@ -534,7 +537,7 @@ static bool fru_decode_custom_fields(const uint8_t *data, fru_reclist_t **reclis fru_reclist_t *custom_field = add_reclist(reclist); if (custom_field == NULL) - return false; + return false; // Create a NUL terminated version of the data for encoding // TODO pass the length into fru_encode_data instead @@ -548,7 +551,7 @@ static bool fru_decode_custom_fields(const uint8_t *data, fru_reclist_t **reclis data += FRU_FIELDSIZE(field->typelen); } - return true; + return true; } /** @@ -673,53 +676,53 @@ bool fru_decode_board_info( fru_exploded_board_t *board_out //< [out] ) { - fru_field_t *field; + fru_field_t *field; const uint8_t *data = area->data; - board_out->lang = area->langtype; + board_out->lang = area->langtype; - uint32_t *min_since_1996 = (uint32_t*)&(area->mfgdate); - struct tm tm_1996 = { + uint32_t *min_since_1996 = (uint32_t*)&(area->mfgdate); + struct tm tm_1996 = { .tm_year = 96, .tm_mon = 0, .tm_mday = 1 - }; - // The argument to mktime is zoneless - board_out->tv.tv_sec = mktime(&tm_1996) + 60 * (*min_since_1996); + }; + // The argument to mktime is zoneless + board_out->tv.tv_sec = mktime(&tm_1996) + 60 * (*min_since_1996); field = (fru_field_t*)data; - if (!fru_decode_data(field, board_out->mfg, + if (!fru_decode_data(field, board_out->mfg, sizeof(board_out->mfg))) - return false; + return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; - if (!fru_decode_data(field, board_out->pname, + if (!fru_decode_data(field, board_out->pname, sizeof(board_out->pname))) - return false; + return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; - if (!fru_decode_data(field, board_out->serial, + if (!fru_decode_data(field, board_out->serial, sizeof(board_out->serial))) - return false; + return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; - if (!fru_decode_data(field, board_out->pn, + if (!fru_decode_data(field, board_out->pn, sizeof(board_out->pn))) - return false; + return false; data += FRU_FIELDSIZE(field->typelen); field = (fru_field_t*)data; - if (!fru_decode_data(field, board_out->file, + if (!fru_decode_data(field, board_out->file, sizeof(board_out->file))) - return false; + return false; data += FRU_FIELDSIZE(field->typelen); - fru_decode_custom_fields(data, &board_out->cust); + fru_decode_custom_fields(data, &board_out->cust); - return true; + return true; } /** diff --git a/fru_reader.c b/fru_reader.c index 699ec09..9acb559 100644 --- a/fru_reader.c +++ b/fru_reader.c @@ -4,7 +4,10 @@ #include "fru_reader.h" #include "fatal.h" -static void safe_read(int fd, void *buffer, size_t length) { +static void safe_read(int fd, uint8_t *buffer, size_t length) { + if (!buffer) + fatal("Cannot read into NULL buffer"); + size_t total_bytes_read = 0; while (total_bytes_read != length) { ssize_t bytes_read = read( @@ -22,7 +25,7 @@ fru_t *read_fru_header(int fd) { fru_t *fru = malloc(sizeof(fru_t)); if (!fru) return NULL; - safe_read(fd, fru, sizeof(fru_t)); + safe_read(fd, (uint8_t*)fru, sizeof(fru_t)); return fru; } @@ -34,12 +37,12 @@ fru_chassis_area_t *read_fru_chassis_area(int fd) { fru_chassis_area_t *area = malloc(base_len); if (!area) return NULL; - safe_read(fd, area, base_len); + safe_read(fd, (uint8_t*)area, base_len); size_t data_len = 8 * area->blocks; area = realloc(area, data_len); if (!area) return NULL; - safe_read(fd, &area->data, data_len - base_len); + safe_read(fd, (uint8_t*)&area->data, data_len - base_len); return area; } @@ -52,12 +55,12 @@ fru_board_area_t *read_fru_board_area(int fd) { fru_board_area_t *area = malloc(base_len); if (!area) return NULL; - safe_read(fd, area, base_len); + safe_read(fd, (uint8_t*)area, base_len); size_t data_len = 8 * area->blocks; area = realloc(area, data_len); if (!area) return NULL; - safe_read(fd, &area->data, data_len - base_len); + safe_read(fd, (uint8_t*)&area->data, data_len - base_len); return area; } @@ -70,12 +73,12 @@ fru_product_area_t *read_fru_product_area(int fd) { fru_product_area_t *area = malloc(base_len); if (!area) return NULL; - safe_read(fd, area, base_len); + safe_read(fd, (uint8_t*)area, base_len); size_t data_len = 8 * area->blocks; area = realloc(area, data_len); if (!area) return NULL; - safe_read(fd, &area->data, data_len - base_len); + safe_read(fd, (uint8_t*)&area->data, data_len - base_len); return area; }