summaryrefslogtreecommitdiff
path: root/ubi-utils/old-utils/src/unubi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/old-utils/src/unubi.c')
-rw-r--r--ubi-utils/old-utils/src/unubi.c1024
1 files changed, 1024 insertions, 0 deletions
diff --git a/ubi-utils/old-utils/src/unubi.c b/ubi-utils/old-utils/src/unubi.c
new file mode 100644
index 0000000..0f72b18
--- /dev/null
+++ b/ubi-utils/old-utils/src/unubi.c
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ * Frank Haverkamp, haver@vnet.ibm.com
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups.
+ * 1.4 Meanwhile Drake had done a lot of changes, syncing those.
+ * 1.5 Bugfixes, simplifications
+ */
+
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <mtd/ubi-media.h>
+#include <mtd_swab.h>
+
+#include "crc32.h"
+#include "unubi_analyze.h"
+
+#define EXEC "unubi"
+#define CONTACT "haver@vnet.ibm.com"
+#define VERSION "1.5"
+
+static char doc[] = "\nVersion: " VERSION "\n";
+static int debug = 0;
+
+static const char *optionsstr =
+"Extract volumes and/or analysis information from an UBI data file.\n"
+"When no parameters are flagged or given, the default operation is\n"
+"to rebuild all valid complete UBI volumes found within the image.\n"
+"\n"
+" OPERATIONS\n"
+" -a, --analyze Analyze image and create gnuplot graphs\n"
+" -i, --info-table Extract volume information tables\n"
+" -r, --rebuild=<volume-id> Extract and rebuild volume\n"
+"\n"
+" OPTIONS\n"
+" -b, --blocksize=<block-size> Specify size of eraseblocks in image in bytes\n"
+" (default 128KiB)\n"
+" -d, --dir=<output-dir> Specify output directory\n"
+" -D, --debug Enable debug output\n"
+" -s, --headersize=<header-size> Specify size reserved for metadata in eraseblock\n"
+ " in bytes (default 2048 Byte)\n"
+ /* the -s option might be insufficient when using different vid
+ offset than what we used when writing this tool ... Better would
+ probably be --vid-hdr-offset or alike */
+"\n"
+" ADVANCED\n"
+" -e, --eb-split Generate individual eraseblock images (all\n"
+" eraseblocks)\n"
+" -v, --vol-split Generate individual eraseblock images (valid\n"
+" eraseblocks only)\n"
+" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" --version Print program version\n"
+"\n";
+
+static const char *usage =
+"Usage: unubi [-aievV?] [-r <volume-id>] [-b <block-size>] [-d <output-dir>]\n"
+" [-s <header-size>] [--analyze] [--info-table]\n"
+" [--rebuild=<volume-id>] [--blocksize=<block-size>]\n"
+" [--dir=<output-dir>] [--headersize=<header-size>] [--eb-split]\n"
+" [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n"
+" image-file\n";
+
+#define ERR_MSG(fmt...) \
+ fprintf(stderr, EXEC ": " fmt)
+
+#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_%04zu" /* split volume */
+#define FN_VOLWH "%s/volume%03u" /* whole volume */
+#define FN_VITBL "%s/vol_info_table%zu" /* vol info table */
+
+static uint32_t crc32_table[256];
+
+/* struct args:
+ * bsize int, blocksize of image blocks
+ * hsize int, eraseblock header size
+ * 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 {
+ int analyze;
+ int itable;
+ uint32_t *vols;
+
+ size_t vid_hdr_offset;
+ size_t data_offset;
+ size_t bsize; /* FIXME replace by vid_hdr/data offs? */
+ size_t hsize;
+
+ char *odir_path;
+ int eb_split;
+ int vol_split;
+ char *img_path;
+
+ char **options;
+};
+
+struct option long_options[] = {
+ { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' },
+ { NULL, 0, NULL, 0}
+};
+
+/**
+ * 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;
+ ulong num;
+
+ s = str;
+ num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) ||
+ (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0))
+ num *= KIB;
+ else 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;
+}
+
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ uint32_t i;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a': /* --analyze */
+ args->analyze = 1;
+ break;
+ case 'b': /* --block-size=<block-size> */
+ args->bsize = str_to_num(optarg);
+ break;
+ case 's': /* --header-size=<header-size> */
+ args->hsize = str_to_num(optarg);
+ break;
+ case 'd': /* --dir=<output-dir> */
+ args->odir_path = optarg;
+ break;
+ case 'D': /* --debug */
+ /* I wanted to use -v but that was already
+ used ... */
+ debug = 1;
+ break;
+ case 'e': /* --eb-split */
+ args->eb_split = SPLIT_RAW;
+ break;
+ case 'i': /* --info-table */
+ args->itable = 1;
+ break;
+ case 'r': /* --rebuild=<volume-id> */
+ i = str_to_num(optarg);
+ if (i < UBI_MAX_VOLUMES)
+ args->vols[str_to_num(optarg)] = 1;
+ else {
+ ERR_MSG("volume-id out of bounds\n");
+ return -1;
+ }
+ break;
+ case 'v': /* --vol-split */
+ if (args->vol_split != SPLIT_RAW)
+ args->vol_split = SPLIT_DATA;
+ break;
+ case 'V': /* --vol-split! */
+ args->vol_split = SPLIT_RAW;
+ break;
+ case '?': /* help */
+ fprintf(stderr, "Usage: unubi [OPTION...] "
+ "image-file\n%s%s\nReport bugs to %s\n",
+ doc, optionsstr, CONTACT);
+ exit(0);
+ break;
+ case 'J':
+ fprintf(stderr, "%s\n", VERSION);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ /* FIXME I suppose hsize should be replaced! */
+ args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE;
+ args->data_offset = args->hsize;
+
+ if (optind < argc)
+ args->img_path = argv[optind++];
+ return 0;
+}
+
+
+/**
+ * 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)
+{
+ size_t count, i;
+
+ if (full_array == NULL)
+ return 0;
+
+ for (i = 0, count = 0; i < full_len; i++)
+ if (full_array[i] != 0)
+ count++;
+
+ return count;
+}
+
+
+/**
+ * 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)
+{
+ size_t i, j;
+
+ if ((full_array == NULL) || (coll_array == NULL))
+ return 0;
+
+ for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++)
+ if (full_array[i] != 0) {
+ coll_array[j] = i;
+ j++;
+ }
+
+ return j;
+}
+
+/**
+ * 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;
+ }
+
+ rc = fsetpos(fpin, &start);
+ if (rc < 0)
+ return -1;
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ err:
+ if (fpout != NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * extract volume information table from block. saves and reloads fpin
+ * position
+ * returns -1 when a fpos set or get fails, otherwise <= -2 on other
+ * failure and 0 on success
+ **/
+static int
+extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num,
+ const char *path)
+{
+ char filename[MAXPATH + 1];
+ int rc;
+ size_t i, max;
+ fpos_t temp;
+ FILE* fpout = NULL;
+ struct ubi_vtbl_record rec;
+
+ if (fpin == NULL || cur == NULL || path == NULL)
+ return -2;
+
+ /* remember position */
+ rc = fgetpos(fpin, &temp);
+ if (rc < 0)
+ return -1;
+
+ /* jump to top of eraseblock, skip to data section */
+ fsetpos(fpin, &cur->eb_top);
+ if (rc < 0)
+ return -1;
+ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+ /* prepare output file */
+ if (be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOLUME_ID)
+ return -2;
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, FN_VITBL, path, num);
+ fpout = fopen(filename, "w");
+ if (fpout == NULL)
+ return -2;
+
+ /* loop through entries */
+ fprintf(fpout,
+ "index\trpebs\talign\ttype\tcrc\t\tname\n");
+ max = bsize - be32_to_cpu(cur->ec.data_offset);
+ for (i = 0; i < (max / sizeof(rec)); i++) {
+ int blank = 1;
+ char *ptr, *base;
+ char name[UBI_VOL_NAME_MAX + 1];
+ const char *type = "unknown\0";
+ uint32_t crc;
+
+ /* read record */
+ rc = fread(&rec, 1, sizeof(rec), fpin);
+ if (rc == 0)
+ break;
+ if (rc != sizeof(rec)) {
+ ERR_MSG("reading volume information "
+ "table record failed\n");
+ rc = -3;
+ goto exit;
+ }
+
+ /* check crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec,
+ UBI_VTBL_RECORD_SIZE_CRC);
+ if (crc != be32_to_cpu(rec.crc))
+ continue;
+
+ /* check for empty */
+ base = (char *)&rec;
+ ptr = base;
+ while (blank &&
+ ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) {
+ if (*ptr != 0)
+ blank = 0;
+ ptr++;
+ }
+
+ if (blank)
+ continue;
+
+ /* prep type string */
+ if (rec.vol_type == UBI_VID_DYNAMIC)
+ type = "dynamic\0";
+ else if (rec.vol_type == UBI_VID_STATIC)
+ type = "static\0";
+
+ /* prep name string */
+ rec.name[be16_to_cpu(rec.name_len)] = '\0';
+ sprintf(name, "%s", rec.name);
+
+ /* print record line to fpout */
+ fprintf(fpout, "%zu\t%u\t%u\t%s\t0x%08x\t%s\n",
+ i,
+ be32_to_cpu(rec.reserved_pebs),
+ be32_to_cpu(rec.alignment),
+ type,
+ be32_to_cpu(rec.crc),
+ name);
+ }
+
+ exit:
+ /* reset position */
+ if (fsetpos(fpin, &temp) < 0)
+ rc = -1;
+
+ if (fpout != NULL)
+ fclose(fpout);
+
+ return rc;
+}
+
+
+/**
+ * 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, struct eb_info **head,
+ const char *path, size_t block_size, size_t header_size)
+{
+ char filename[MAXPATH];
+ int rc;
+ uint32_t vol, num, data_size;
+ FILE* fpout;
+ struct eb_info *cur;
+
+ rc = 0;
+
+ if ((fpin == NULL) || (head == NULL) || (*head == NULL))
+ return 0;
+
+ /* when vol_id is null, then do all */
+ if (vol_id == NULL) {
+ cur = *head;
+ vol = be32_to_cpu(cur->vid.vol_id);
+ } else {
+ vol = *vol_id;
+ eb_chain_position(head, vol, NULL, &cur);
+ if (cur == NULL) {
+ if (debug)
+ ERR_MSG("no valid volume %d was found\n", vol);
+ return -1;
+ }
+ }
+
+ 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 (be32_to_cpu(cur->vid.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 = be32_to_cpu(cur->vid.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;
+ }
+ }
+
+ while (num < be32_to_cpu(cur->vid.lnum)) {
+ /* FIXME haver: I hope an empty block is
+ written out so that the binary has no holes
+ ... */
+ if (debug)
+ ERR_MSG("missing valid block %d for volume %d\n",
+ num, vol);
+ num++;
+ }
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc < 0)
+ goto out;
+ fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+ if (cur->vid.vol_type == UBI_VID_DYNAMIC)
+ /* FIXME It might be that alignment has influence */
+ data_size = block_size - header_size;
+ else
+ data_size = be32_to_cpu(cur->vid.data_size);
+
+ for (i = 0; i < 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++;
+ }
+
+ out:
+ if (vol_id == NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * 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)
+{
+ char filename[MAXPATH + 1];
+ char reason[MAXPATH + 1];
+ int rc;
+ size_t i, count, itable_num;
+ /* relations:
+ * cur ~ head
+ * next ~ first */
+ struct eb_info *head, *cur, *first, *next;
+ struct eb_info **next_ptr;
+
+ rc = 0;
+ count = 0;
+ itable_num = 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));
+
+ fgetpos(fpin, &(cur->eb_top));
+ while (1) {
+ const char *raw_path;
+ uint32_t crc;
+
+ cur->phys_addr = ftell(fpin);
+ cur->phys_block = cur->phys_addr / a->bsize;
+ cur->data_crc_ok = 0;
+ cur->ec_crc_ok = 0;
+ cur->vid_crc_ok = 0;
+
+ 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->ec, 1, sizeof(cur->ec), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->ec)) {
+ ERR_MSG("reading ec-hdr failed\n");
+ rc = -1;
+ goto err;
+ }
+
+ /* check erasecounter header magic */
+ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.ec_magic");
+ goto invalid;
+ }
+
+ /* check erasecounter header crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec),
+ UBI_EC_HDR_SIZE_CRC);
+ if (be32_to_cpu(cur->ec.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, be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR);
+ rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->vid)) {
+ ERR_MSG("reading vid-hdr failed\n");
+ rc = -1;
+ goto err;
+ }
+
+ /* if the magic number is 0xFFFFFFFF, then it's very likely
+ * that the volume is empty */
+ if (be32_to_cpu(cur->vid.magic) == 0xffffffff) {
+ snprintf(reason, MAXPATH, ".empty");
+ goto invalid;
+ }
+
+ /* vol_id should be in bounds */
+ if ((be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) &&
+ (be32_to_cpu(cur->vid.vol_id) <
+ UBI_INTERNAL_VOL_START)) {
+ snprintf(reason, MAXPATH, ".invalid");
+ goto invalid;
+ } else
+ raw_path = FN_NSURE;
+
+ /* check volume id header magic */
+ if (be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.vid_magic");
+ goto invalid;
+ }
+ cur->ec_crc_ok = 1;
+
+ /* check volume id header crc */
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid),
+ UBI_VID_HDR_SIZE_CRC);
+ if (be32_to_cpu(cur->vid.hdr_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc");
+ goto invalid;
+ }
+ cur->vid_crc_ok = 1;
+
+ /* check data crc, but only for a static volume */
+ if (cur->vid.vol_type == UBI_VID_STATIC) {
+ rc = data_crc(fpin, be32_to_cpu(cur->vid.data_size),
+ &crc);
+ if (rc < 0)
+ goto err;
+ if (be32_to_cpu(cur->vid.data_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.data_crc");
+ goto invalid;
+ }
+ cur->data_crc_ok = 1;
+ }
+
+ /* 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;
+ }
+
+ /* extract info-table */
+ if (a->itable &&
+ (be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOLUME_ID)) {
+ extract_itable(fpin, cur, a->bsize,
+ itable_num, a->odir_path);
+ itable_num++;
+ }
+
+ /* split volumes */
+ if (a->vol_split) {
+ size_t size = 0;
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+
+ /*
+ * FIXME For dynamic UBI volumes we must write
+ * the maximum available data. The
+ * vid.data_size field is not used in this
+ * case. The dynamic volume user is
+ * responsible for the content.
+ */
+ if (a->vol_split == SPLIT_DATA) {
+ /* Write only data section */
+ if (cur->vid.vol_type == UBI_VID_DYNAMIC) {
+ /* FIXME Formular is not
+ always right ... */
+ size = a->bsize - a->hsize;
+ } else
+ size = be32_to_cpu(cur->vid.data_size);
+
+ fseek(fpin,
+ be32_to_cpu(cur->ec.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,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.leb_ver), count);
+ rc = extract_data(fpin, size, filename);
+ if (rc < 0)
+ goto err;
+ }
+
+ invalid:
+ /* split eraseblocks */
+ 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,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.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));
+ }
+
+ out:
+ for (i = 0; i < vc; i++) {
+ rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path,
+ a->bsize, a->hsize);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* 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) && (!a->itable)) {
+ rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize,
+ a->hsize);
+ if (rc < 0)
+ goto err;
+ }
+
+ err:
+ free(cur);
+
+ if (a->analyze) {
+ char fname[PATH_MAX + 1];
+ FILE *fp;
+
+ unubi_analyze(&head, first, a->odir_path);
+
+ /* prepare output files */
+ memset(fname, 0, PATH_MAX + 1);
+ snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT);
+ fp = fopen(fname, "w");
+ if (fp != NULL) {
+ eb_chain_print(fp, head);
+ fclose(fp);
+ }
+ }
+ 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.hsize = 2 * KIB;
+ a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES);
+ if (a.vols == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES);
+
+ /* parse args and check for validity */
+ parse_opt(argc, argv, &a);
+ if (a.img_path == NULL) {
+ ERR_MSG("no image file specified\n");
+ rc = EINVAL;
+ goto err;
+ }
+ 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(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;
+ }
+
+ /* 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;
+ }
+ 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;
+ }
+
+ 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;
+}