From e820054fd876faa7306d64957c71c9c5c29db78c Mon Sep 17 00:00:00 2001 From: Drake Dowsett Date: Mon, 6 Nov 2006 16:54:10 +0100 Subject: [MTD] UBI: rework of off-line UBI analysis tool The new version can create a gnuplot graph of the erase count statistics. It can also extract UBI volumes and single blocks with a preanalysis of the EC as well as the VID header. It has a manual page too ;-). Signed-off-by: Frank Haverkamp --- ubi-utils/src/unubi.c | 975 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 714 insertions(+), 261 deletions(-) (limited to 'ubi-utils/src/unubi.c') diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c index 6d877e7..4b9ddfd 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -14,13 +14,23 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Author: Frank Haverkamp - * - * An utility to decompose UBI images. Not yet finished ... */ -#include +/* + * Authors: Frank Haverkamp, haver@vnet.ibm.com + * Drake Dowsett, dowsett@de.ibm.com + */ + +/* + * unubi reads an image file containing blocks of UBI headers and data + * (such as produced from nand2bin) and rebuilds the volumes within. The + * default operation (when no flags are given) is to rebuild all valid + * volumes found in the image. unubi can also read straight from the + * onboard MTD device (ex. /dev/mtdblock/NAND). + */ + +/* TODO: consideration for dynamic vs. static volumes */ + #include #include #include @@ -34,359 +44,802 @@ #include #include #include +#include #include "crc32.h" -#include +#include "eb_chain.h" +#include "unubi_analyze.h" + +#define EXEC "unubi" +#define CONTACT "haver@vnet.ibm.com" +#define VERSION "0.9" + +static char doc[] = "\nVersion: " VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nAnalyze raw flash containing UBI data.\n"; + +#define ERR_MSG(fmt...) \ + fprintf(stderr, EXEC ": " fmt) -#define MAXPATH 1024 -#define MIN(x,y) ((x)<(y)?(x):(y)) +#define SPLIT_DATA 1 +#define SPLIT_RAW 2 + +#define DIR_FMT "unubi_%s" +#define KIB 1024 +#define MIB (KIB * KIB) +#define MAXPATH KIB + +/* filenames */ +#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */ +#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */ +#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */ +#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04u" /* split volume */ +#define FN_VOLWH "%s/volume%03u" /* whole volume */ static uint32_t crc32_table[256]; +/* struct args: + * bsize int, blocksize of image blocks + * analyze flag, when non-zero produce analysis + * eb_split flag, when non-zero output eb#### + * note: SPLIT_DATA vs. SPLIT_RAW + * vol_split flag, when non-zero output vol###_#### + * note: SPLIT_DATA vs. SPLIT_RAW + * odir_path string, directory to place volumes in + * img_path string, file to read as ubi image + * vols int array of size UBI_MAX_VOLUMES, where a 1 can be + * written for each --rebuild flag in the index specified + * then the array can be counted and collapsed using + * count_set() and collapse() + */ struct args { - const char *output_dir; - uint32_t hdr_offs; - uint32_t data_offs; - uint32_t blocksize; - - /* special stuff needed to get additional arguments */ - char *arg1; - char **options; /* [STRING...] */ + uint32_t bsize; + int analyze; + int eb_split; + int vol_split; + char *odir_path; + char *img_path; + uint32_t *vols; + + char **options; }; -static struct args myargs = { - .output_dir = "unubi", - .hdr_offs = 64, - .data_offs = 128, - .blocksize = 128 * 1024, - .arg1 = NULL, - .options = NULL, -}; +static error_t parse_opt(int key, char *arg, struct argp_state *state); -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = - "... uuuh, lets wait until it looks nicer"; - -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" - BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" - "\nWrite to UBI Volume.\n"; +const char *argp_program_version = VERSION; +const char *argp_program_bug_address = CONTACT; static struct argp_option options[] = { - { .name = "dir", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "output directory", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "blocksize", - .key = 'b', - .arg = "", - .flags = 0, - .doc = "blocksize", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "data-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "data offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "hdr-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "hdr offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, + { + name: NULL, key: 0, arg: NULL, + flags: 0, group: 0, doc: "OPTIONS", + }, + { + name: "rebuild", key: 'r', arg: "", + flags: 0, group: 1, doc: "Extract and rebuild volume", + }, + { + name: "dir", key: 'd', arg: "", + flags: 0, group: 2, doc: "Specify output directory", + }, + { + name: "analyze", key: 'a', arg: NULL, + flags: 0, group: 3, doc: "Analyze image", + }, + { + name: "blocksize", key: 'b', arg: "", + flags: 0, group: 4, doc: "Specify size of eraseblocks " + "in image in bytes (default " + "128KiB)", + }, + { + name: "eb-split", key: 'e', arg: NULL, + flags: 0, group: 5, doc: "Generate individual eraseblock " + "images (all eraseblocks)", + }, + { + name: "vol-split", key: 'v', arg: NULL, + flags: 0, group: 5, doc: "Generate individual eraseblock " + "images (valid eraseblocks only)", + }, + { + name: "vol-split!", key: 'V', arg: NULL, + flags: 0, group: 5, doc: "Raw split by eraseblock " + "(valid eraseblocks only)", + }, + { + name: NULL, key: 0, arg: NULL, + flags: 0, group: 0, doc: NULL, + }, }; static struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "image-file", - .doc = doc, + .doc = doc, .children = NULL, .help_filter = NULL, .argp_domain = NULL, }; -/* - * str_to_num - Convert string into number and cope with endings like - * k, K, kib, KiB for kilobyte - * m, M, mib, MiB for megabyte - */ -uint32_t str_to_num(char *str) + +/** + * parses out a numerical value from a string of numbers followed by: + * k, K, kib, KiB for kibibyte + * m, M, mib, MiB for mebibyte + **/ +static uint32_t +str_to_num(char *str) { - char *s = str; - ulong num = strtoul(s, &s, 0); + char *s; + ulong num; + + s = str; + num = strtoul(s, &s, 0); if (*s != '\0') { - if (strcmp(s, "KiB") == 0) - num *= 1024; - else if (strcmp(s, "MiB") == 0) - num *= 1024*1024; - else { - fprintf(stderr, "WARNING: Wrong number format " - "\"%s\", check your paramters!\n", str); - } + if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) || + (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0)) + num *= KIB; + if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) || + (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0)) + num *= MIB; + else + ERR_MSG("couldn't parse '%s', assuming %lu\n", + s, num); } return num; } -/* - * @brief Parse the arguments passed into the test case. - * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. - */ + +/** + * parses the arguments passed into the program + * get the input argument from argp_parse, which we know is a + * pointer to our arguments structure; + **/ static error_t parse_opt(int key, char *arg, struct argp_state *state) { + uint32_t i; struct args *args = state->input; switch (key) { - case 'b': /* --blocksize */ - args->blocksize = str_to_num(arg); + case 'a': + args->analyze = 1; break; - - case 'x': /* --data-offs= */ - args->data_offs = str_to_num(arg); + case 'b': + args->bsize = str_to_num(arg); break; - - case 'y': /* --hdr-offs= */ - args->hdr_offs = str_to_num(arg); + case 'd': + args->odir_path = arg; break; - - case 'd': /* --dir= */ - args->output_dir = arg; + case 'e': + args->eb_split = SPLIT_RAW; + break; + case 'r': + i = str_to_num(arg); + if (i < UBI_MAX_VOLUMES) + args->vols[str_to_num(arg)] = 1; + else { + ERR_MSG("volume-id out of bounds\n"); + return ARGP_ERR_UNKNOWN; + } + break; + case 'v': + if (args->vol_split != SPLIT_RAW) + args->vol_split = SPLIT_DATA; + break; + case 'V': + args->vol_split = SPLIT_RAW; break; - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ break; - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - + args->img_path = arg; args->options = &state->argv[state->next]; state->next = state->argc; break; - case ARGP_KEY_END: - /* argp_usage(state); */ break; - default: - return(ARGP_ERR_UNKNOWN); + return ARGP_ERR_UNKNOWN; } return 0; } -static inline void -hexdump(FILE *fp, const void *p, ssize_t size) + +/** + * counts the number of indicies which are flagged in full_array; + * full_array is an array of flags (1/0); + **/ +static size_t +count_set(uint32_t *full_array, size_t full_len) { - int k; - const uint8_t *buf = p; + size_t count, i; - for (k = 0; k < size; k++) { - fprintf(fp, "%02x ", buf[k]); - if ((k & 15) == 15) - fprintf(fp, "\n"); - } + if (full_array == NULL) + return 0; + + for (i = 0, count = 0; i < full_len; i++) + if (full_array[i] != 0) + count++; + + return count; } -/* - * This was put together in 1.5 hours and this is exactly how it looks - * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME! - */ -static int extract_volume(struct args *args, const uint8_t *buf, - int len, int volume, FILE *fp) + +/** + * generates coll_array from full_array; + * full_array is an array of flags (1/0); + * coll_array is an array of the indicies in full_array which are flagged (1); + **/ +static size_t +collapse(uint32_t *full_array, size_t full_len, + uint32_t *coll_array, size_t coll_len) { - int i, rc; - int nrblocks = len / args->blocksize; - int max_lnum = -1, lnum = 0; - const uint8_t *ptr; - uint8_t **vol_tab; - int *vol_len; + size_t i, j; - vol_tab = calloc(nrblocks, sizeof(uint8_t *)); - vol_len = calloc(nrblocks, sizeof(int)); + if ((full_array == NULL) || (coll_array == NULL)) + return 0; - if (!buf || !vol_tab || !vol_len) - exit(EXIT_FAILURE); + for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++) + if (full_array[i] != 0) { + coll_array[j] = i; + j++; + } - for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { - uint32_t crc; - struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; - struct ubi_vid_hdr *vid_hdr = NULL; - uint8_t *data; + return j; +} + + +/** + * header_crc: calculate the crc of EITHER a eb_hdr or vid_hdr + * one of the first to args MUST be NULL, the other is the header + * to caculate the crc on + * always returns 0 + **/ +static int +header_crc(struct ubi_ec_hdr *ebh, struct ubi_vid_hdr *vidh, uint32_t *ret_crc) +{ + uint32_t crc = UBI_CRC32_INIT; + + if (ret_crc == NULL) + return 0; + + if ((ebh != NULL) && (vidh == NULL)) + crc = clc_crc32(crc32_table, crc, ebh, UBI_EC_HDR_SIZE_CRC); + else if ((ebh == NULL) && (vidh != NULL)) + crc = clc_crc32(crc32_table, crc, vidh, UBI_VID_HDR_SIZE_CRC); + else + return 0; + + *ret_crc = crc; + return 0; +} - /* default */ - vol_len[lnum] = args->blocksize - (2 * 1024); - /* Check UBI EC header */ - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, - UBI_EC_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) - continue; +/** + * data_crc: save the FILE* position, calculate the crc over a span, + * reset the position + * returns non-zero when EOF encountered + **/ +static int +data_crc(FILE* fpin, size_t length, uint32_t *ret_crc) +{ + int rc; + size_t i; + char buf[length]; + uint32_t crc; + fpos_t start; + + rc = fgetpos(fpin, &start); + if (rc < 0) + return -1; + + for (i = 0; i < length; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF\n"); + return -1; + } + buf[i] = (char)c; + } - vid_hdr = (struct ubi_vid_hdr *) - (ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); - data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); + rc = fsetpos(fpin, &start); + if (rc < 0) + return -1; - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, - UBI_VID_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) - continue; + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length); + *ret_crc = crc; + return 0; +} + + +/** + * reads data of size len from fpin and writes it to path + **/ +static int +extract_data(FILE* fpin, size_t len, const char *path) +{ + int rc; + size_t i; + FILE* fpout; + + rc = 0; + fpout = NULL; + + fpout = fopen(path, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", path); + rc = -1; + goto err; + } - if (volume == (int)ubi32_to_cpu(vid_hdr->vol_id)) { + for (i = 0; i < len; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", path); + rc = -2; + goto err; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", path); + rc = -3; + goto err; + } + } - printf("****** block %4d volume %2d **********\n", - i, volume); + err: + if (fpout != NULL) + fclose(fpout); + return rc; +} - hexdump(stdout, ptr, 64); - printf("--- vid_hdr\n"); - hexdump(stdout, vid_hdr, 64); +/** + * using eb chain, tries to rebuild the data of volume at vol_id, or for all + * the known volumes, if vol_id is NULL; + **/ +static int +rebuild_volume(FILE* fpin, uint32_t *vol_id, eb_info_t *head, const char* path) +{ + char filename[MAXPATH]; + int rc; + uint32_t vol, num; + FILE* fpout; + eb_info_t cur; - printf("--- data\n"); - hexdump(stdout, data, 64); + rc = 0; - lnum = ubi32_to_cpu(vid_hdr->lnum); - vol_tab[lnum] = data; - if (max_lnum < lnum) - max_lnum = lnum; - if (vid_hdr->vol_type == UBI_VID_STATIC) - vol_len[lnum] = - ubi32_to_cpu(vid_hdr->data_size); + if ((fpin == NULL) || (head == NULL) || (*head == NULL)) + return 0; + /* when vol_id is null, then do all */ + if (vol_id == NULL) { + cur = *head; + vol = ubi32_to_cpu(cur->inner.vol_id); + } + else { + vol = *vol_id; + eb_chain_position(head, vol, NULL, &cur); + if (cur == NULL) { + ERR_MSG("no valid volume %d was found\n", vol); + return -1; } } - for (lnum = 0; lnum <= max_lnum; lnum++) { - if (vol_tab[lnum]) { - rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); - if (ferror(fp) || (vol_len[lnum] != rc)) { - perror("could not write file"); - exit(EXIT_FAILURE); + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", filename); + return -1; + } + + while (cur != NULL) { + size_t i; + + if (ubi32_to_cpu(cur->inner.vol_id) != vol) { + /* close out file */ + fclose(fpout); + + /* only stay around if that was the only volume */ + if (vol_id != NULL) + goto out; + + /* begin with next */ + vol = ubi32_to_cpu(cur->inner.vol_id); + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", + filename); + return -1; } - } else { - /* Fill up empty areas by 0xff, for static - * volumes this means they are broken! - */ - for (i = 0; i < vol_len[lnum]; i++) { - if (fputc(0xff, fp) == EOF) { - perror("could not write char"); - exit(EXIT_FAILURE); - } + } + + if (ubi32_to_cpu(cur->inner.lnum) != num) { + ERR_MSG("missing valid block %d for volume %d\n", + num, vol); + } + + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc < 0) + goto out; + fseek(fpin, ubi32_to_cpu(cur->outer.data_offset), SEEK_CUR); + + for (i = 0; i < ubi32_to_cpu(cur->inner.data_size); i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", + filename); + rc = -2; + goto out; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", filename); + rc = -3; + goto out; } } + + cur = cur->next; + num++; } - free(vol_tab); - free(vol_len); - return 0; + out: + if (vol_id == NULL) + fclose(fpout); + return rc; } -int -main(int argc, char *argv[]) + +/** + * traverses FILE* trying to load complete, valid and accurate header data + * into the eb chain; + **/ +static int +unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a) { - int len, rc; - FILE *fp; - struct stat file_info; - uint8_t *buf; - int i; + char filename[MAXPATH + 1]; + char reason[MAXPATH + 1]; + int rc; + size_t i, count; + /* relations: + * cur ~ head + * next ~ first */ + eb_info_t head, cur, first, next; + eb_info_t *next_ptr; + + rc = 0; + count = 0; + head = NULL; + first = NULL; + next = NULL; + cur = malloc(sizeof(*cur)); + if (cur == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(cur, 0, sizeof(*cur)); - init_crc32_table(crc32_table); + fgetpos(fpin, &(cur->eb_top)); + while (1) { + const char *raw_path; + uint32_t crc; + + memset(filename, 0, MAXPATH + 1); + memset(reason, 0, MAXPATH + 1); + + /* in case of an incomplete ec header */ + raw_path = FN_INVAL; + + /* read erasecounter header */ + rc = fread(&cur->outer, 1, sizeof(cur->outer), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->outer)) { + ERR_MSG("reading ec-hdr failed rc=%d\n", rc); + rc = -1; + goto err; + } + + /* check erasecounter header magic */ + if (ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.ec_magic"); + goto invalid; + } + + /* check erasecounter header crc */ + header_crc(&(cur->outer), NULL, &crc); + + if (ubi32_to_cpu(cur->outer.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc"); + goto invalid; + } + + /* read volume id header */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, ubi32_to_cpu(cur->outer.vid_hdr_offset), SEEK_CUR); + + /* read erasecounter header */ + rc = fread(&cur->inner, 1, sizeof(cur->inner), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->inner)) { + ERR_MSG("reading vid-hdr failed rc=%d\n", rc); + rc = -1; + goto err; + } + + /* empty? */ + if (ubi32_to_cpu(cur->inner.magic) == 0xffffffff) { + snprintf(reason, MAXPATH, ".empty"); + goto invalid; + } + + /* vol_id should be in bounds */ + if ((ubi32_to_cpu(cur->inner.vol_id) >= UBI_MAX_VOLUMES) && + (ubi32_to_cpu(cur->inner.vol_id) < + UBI_INTERNAL_VOL_START)) { + snprintf(reason, MAXPATH, ".invalid"); + goto invalid; + } else + raw_path = FN_NSURE; + + /* check volume id header magic */ + if (ubi32_to_cpu(cur->inner.magic) != UBI_VID_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.vid_magic"); + goto invalid; + } + + /* check volume id header crc */ + header_crc(NULL, &(cur->inner), &crc); + if (ubi32_to_cpu(cur->inner.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc"); + goto invalid; + } + + /* check data crc */ + rc = data_crc(fpin, ubi32_to_cpu(cur->inner.data_size), &crc); + if (rc < 0) + goto err; + if (ubi32_to_cpu(cur->inner.data_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.data_crc"); + goto invalid; + } - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + /* enlist this vol, it's valid */ + raw_path = FN_VALID; + cur->linear = count; + rc = eb_chain_insert(&head, cur); + if (rc < 0) { + if (rc == -ENOMEM) { + ERR_MSG("out of memory\n"); + goto err; + } + ERR_MSG("unknown and unexpected error, please contact " + CONTACT "\n"); + goto err; + } + + if (a->vol_split) { + size_t size = 0; - if (!myargs.arg1) { - fprintf(stderr, "Please specify input file!\n"); - exit(EXIT_FAILURE); + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + if (a->vol_split == SPLIT_DATA) { + /* write only data section */ + size = ubi32_to_cpu(cur->inner.data_size); + fseek(fpin, + ubi32_to_cpu(cur->outer.data_offset), + SEEK_CUR); + } + else if (a->vol_split == SPLIT_RAW) + /* write entire eraseblock */ + size = a->bsize; + + snprintf(filename, MAXPATH, FN_VOLSP, + a->odir_path, + ubi32_to_cpu(cur->inner.vol_id), + ubi32_to_cpu(cur->inner.lnum), + ubi32_to_cpu(cur->inner.leb_ver), count); + rc = extract_data(fpin, size, filename); + if (rc < 0) + goto err; + } + + invalid: + if (a->eb_split) { + /* jump to top of block */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + if (strcmp(raw_path, FN_INVAL) == 0) + snprintf(filename, MAXPATH, raw_path, + a->odir_path, count, reason); + else + snprintf(filename, MAXPATH, raw_path, + a->odir_path, + count, + ubi32_to_cpu(cur->inner.vol_id), + ubi32_to_cpu(cur->inner.lnum), + ubi32_to_cpu(cur->inner.leb_ver), + reason); + + rc = extract_data(fpin, a->bsize, filename); + if (rc < 0) + goto err; + } + + /* append to simple linked list */ + if (first == NULL) + next_ptr = &first; + else + next_ptr = &next->next; + + *next_ptr = malloc(sizeof(**next_ptr)); + if (*next_ptr == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(*next_ptr, 0, sizeof(**next_ptr)); + + next = *next_ptr; + memcpy(next, cur, sizeof(*next)); + next->next = NULL; + + count++; + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, a->bsize, SEEK_CUR); + memset(cur, 0, sizeof(*cur)); + + fgetpos(fpin, &(cur->eb_top)); } - fp = fopen(myargs.arg1, "r"); - if (!fp) { - perror("Cannot open file"); - exit(EXIT_FAILURE); + out: + for (i = 0; i < vc; i++) { + rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path); + if (rc < 0) + goto err; } - if (fstat(fileno(fp), &file_info) != 0) { - fprintf(stderr, "Cannot fetch file size " - "from input file.\n"); + + /* if there were no volumes specified, rebuild them all, + * UNLESS eb_ or vol_ split or analyze was specified */ + if ((vc == 0) && (!a->eb_split) && (!a->vol_split) && (!a->analyze)) { + rc = rebuild_volume(fpin, NULL, &head, a->odir_path); + if (rc < 0) + goto err; } - len = file_info.st_size; - buf = malloc(len); - if (!buf) { - perror("out of memory!"); - exit(EXIT_FAILURE); + + err: + free(cur); + + if (a->analyze) + unubi_analyze(&head, first, a->odir_path); + eb_chain_destroy(&head); + eb_chain_destroy(&first); + + return rc; +} + + +/** + * handles command line arguments, then calls unubi_volumes + **/ +int +main(int argc, char *argv[]) +{ + int rc, free_a_odir; + size_t vols_len; + uint32_t *vols; + FILE* fpin; + struct args a; + + rc = 0; + free_a_odir = 0; + vols_len = 0; + vols = NULL; + fpin = NULL; + init_crc32_table(crc32_table); + + /* setup struct args a */ + memset(&a, 0, sizeof(a)); + a.bsize = 128 * KIB; + a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES); + if (a.vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; } - rc = fread(buf, 1, len, fp); - if (ferror(fp) || (len != rc)) { - perror("could not read file"); - exit(EXIT_FAILURE); + memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); + + /* parse args and check for validity */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &a); + if (a.img_path == NULL) { + ERR_MSG("no image file specified\n"); + rc = EINVAL; + goto err; } - if (!myargs.output_dir) { - fprintf(stderr, "No output directory specified!\n"); - exit(EXIT_FAILURE); + else if (a.odir_path == NULL) { + char *ptr; + int len; + + ptr = strrchr(a.img_path, '/'); + if (ptr == NULL) + ptr = a.img_path; + else + ptr++; + + len = strlen(DIR_FMT) + strlen(ptr); + free_a_odir = 1; + a.odir_path = malloc(sizeof(*a.odir_path) * len); + if (a.odir_path == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + snprintf(a.odir_path, len, DIR_FMT, ptr); + } + + fpin = fopen(a.img_path, "rb"); + if (fpin == NULL) { + ERR_MSG("couldn't open file for reading: " + "%s\n", a.img_path); + rc = EINVAL; + goto err; } - rc = mkdir(myargs.output_dir, 0777); - if (rc && errno != EEXIST) { - perror("Cannot create output directory"); - exit(EXIT_FAILURE); + rc = mkdir(a.odir_path, 0777); + if ((rc < 0) && (errno != EEXIST)) { + ERR_MSG("couldn't create ouput directory: " + "%s\n", a.odir_path); + rc = -rc; + goto err; } - for (i = 0; i < 32; i++) { - char fname[1024]; - FILE *fpout; - - printf("######### VOLUME %d ############################\n", - i); - - sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); - fpout = fopen(fname, "w+"); - if (!fpout) { - perror("Cannot open file"); - exit(EXIT_FAILURE); + + /* fill in vols array */ + vols_len = count_set(a.vols, UBI_MAX_VOLUMES); + if (vols_len > 0) { + vols = malloc(sizeof(*vols) * vols_len); + if (vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; } - extract_volume(&myargs, buf, len, i, fpout); - fclose(fpout); + collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len); + } + + /* unubi volumes */ + rc = unubi_volumes(fpin, vols, vols_len, &a); + if (rc < 0) { + ERR_MSG("error encountered while working on image file: " + "%s\n", a.img_path); + rc = -rc; + goto err; } - fclose(fp); - free(buf); - exit(EXIT_SUCCESS); + err: + free(a.vols); + if (free_a_odir != 0) + free(a.odir_path); + if (fpin != NULL) + fclose(fpin); + if (vols_len > 0) + free(vols); + return rc; } -- cgit v1.2.3