summaryrefslogtreecommitdiff
path: root/ubi-utils/src
diff options
context:
space:
mode:
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>2008-02-19 14:54:17 +0200
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>2008-02-19 16:41:16 +0200
commit86dd9696d3a1acb036d65909d93d73455b8aed3a (patch)
tree9e8bb6797b3bebd0bf83ae1fab2151a6dfeafe6a /ubi-utils/src
parentef644090223e5983ca2492850354c1390e3368d1 (diff)
Revert ubi-tools changes
Restore ubi tools to their "original" state, which means to the state they were before I stareted cleaning them up. Instead, create a "new-utils" subdirectory and move my work there. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'ubi-utils/src')
-rw-r--r--ubi-utils/src/bin2nand.c344
-rw-r--r--ubi-utils/src/bootenv.c1032
-rw-r--r--ubi-utils/src/bootenv.h434
-rw-r--r--ubi-utils/src/common.c176
-rw-r--r--ubi-utils/src/common.h71
-rw-r--r--ubi-utils/src/config.h28
-rw-r--r--ubi-utils/src/crc32.c156
-rw-r--r--ubi-utils/src/crc32.h47
-rw-r--r--ubi-utils/src/dictionary.c405
-rw-r--r--ubi-utils/src/dictionary.h174
-rw-r--r--ubi-utils/src/eb_chain.c281
-rw-r--r--ubi-utils/src/error.c240
-rw-r--r--ubi-utils/src/error.h84
-rw-r--r--ubi-utils/src/example_ubi.h28
-rw-r--r--ubi-utils/src/hashmap.c412
-rw-r--r--ubi-utils/src/hashmap.h49
-rw-r--r--ubi-utils/src/libiniparser.c646
-rw-r--r--ubi-utils/src/libpfiflash.c1325
-rw-r--r--ubi-utils/src/libubi.c1318
-rw-r--r--ubi-utils/src/libubi_int.h100
-rw-r--r--ubi-utils/src/libubigen.c660
-rw-r--r--ubi-utils/src/libubimirror.c237
-rw-r--r--ubi-utils/src/list.c149
-rw-r--r--ubi-utils/src/list.h56
-rw-r--r--ubi-utils/src/mkbootenv.c168
-rw-r--r--ubi-utils/src/nand2bin.c493
-rw-r--r--ubi-utils/src/nandcorr.c95
-rw-r--r--ubi-utils/src/nandecc.c159
-rw-r--r--ubi-utils/src/nandecc.h29
-rw-r--r--ubi-utils/src/pddcustomize.c509
-rw-r--r--ubi-utils/src/peb.c116
-rw-r--r--ubi-utils/src/peb.h41
-rw-r--r--ubi-utils/src/pfi.c458
-rw-r--r--ubi-utils/src/pfi.h244
-rw-r--r--ubi-utils/src/pfi2bin.c682
-rw-r--r--ubi-utils/src/pfiflash.c264
-rw-r--r--ubi-utils/src/pfiflash.h76
-rw-r--r--ubi-utils/src/pfiflash_error.h75
-rw-r--r--ubi-utils/src/reader.c482
-rw-r--r--ubi-utils/src/reader.h87
-rw-r--r--ubi-utils/src/ubiattach.c205
-rw-r--r--ubi-utils/src/ubicrc32.c166
-rw-r--r--ubi-utils/src/ubidetach.c181
-rw-r--r--ubi-utils/src/ubigen.c359
-rw-r--r--ubi-utils/src/ubigen.h149
-rw-r--r--ubi-utils/src/ubimirror.c213
-rw-r--r--ubi-utils/src/ubimirror.h66
-rw-r--r--ubi-utils/src/ubimkvol.c424
-rw-r--r--ubi-utils/src/ubinfo.c409
-rw-r--r--ubi-utils/src/ubinize.c549
-rw-r--r--ubi-utils/src/ubirmvol.c223
-rw-r--r--ubi-utils/src/ubiupdate.c295
-rw-r--r--ubi-utils/src/ubiupdatevol.c336
-rw-r--r--ubi-utils/src/unubi.c1024
-rw-r--r--ubi-utils/src/unubi_analyze.c463
-rw-r--r--ubi-utils/src/unubi_analyze.h87
56 files changed, 12954 insertions, 4625 deletions
diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c
new file mode 100644
index 0000000..c7c7ccc
--- /dev/null
+++ b/ubi-utils/src/bin2nand.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) International Business Machines Corp., 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+/*
+ * Create a flashable NAND image from a binary image
+ *
+ * History:
+ * 1.0 Initial release (tglx)
+ * 1.1 Understands hex and dec input parameters (tglx)
+ * 1.2 Generates separated OOB data, if needed. (oloh)
+ * 1.3 Padds data/oob to a given size. (oloh)
+ * 1.4 Removed argp because we want to use uClibc.
+ * 1.5 Minor cleanup
+ * 1.6 written variable not initialized (-j did not work) (haver)
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "error.h"
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+} action_t;
+
+#define PAGESIZE 2048
+#define PADDING 0 /* 0 means, do not adjust anything */
+#define BUFSIZE 4096
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "bin2nand - a tool for adding OOB information to a "
+ "binary input file.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -j, --padding=<num> Padding in Byte/Mi/ki. Default = no padding\n"
+" -p, --pagesize=<num> Pagesize in Byte/Mi/ki. Default = 2048\n"
+" -o, --output=<fname> Output filename. Interleaved Data/OOB if\n"
+" output-oob not specified.\n"
+" -q, --output-oob=<fname> Write OOB data in separate file.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: bin2nand [-c?V] [-j <num>] [-p <num>] [-o <fname>] [-q <fname>]\n"
+" [--copyright] [--padding=<num>] [--pagesize=<num>]\n"
+" [--output=<fname>] [--output-oob=<fname>] [--help] [--usage]\n"
+" [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp. 2006";
+
+typedef struct myargs {
+ action_t action;
+
+ size_t pagesize;
+ size_t padding;
+
+ FILE* fp_in;
+ char *file_out_data; /* Either: Data and OOB interleaved
+ or plain data */
+ char *file_out_oob; /* OOB Data only. */
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoull(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long long res = strtoull(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ res *= 1024;
+ case 'M':
+ res *= 1024;
+ case 'k':
+ case 'K':
+ res *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return res;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* pagesize */
+ args->pagesize = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("p", endp);
+ break;
+ case 'j': /* padding */
+ args->padding = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("j", endp);
+ break;
+ case 'o': /* output */
+ args->file_out_data = optarg;
+ break;
+ case 'q': /* output oob */
+ args->file_out_oob = optarg;
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ case 'c':
+ printf("%s\n", copyright);
+ exit(0);
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ err_quit("Cannot open file %s for input\n",
+ argv[optind++]);
+ }
+ }
+
+ return 0;
+}
+
+static int
+process_page(uint8_t* buf, size_t pagesize,
+ FILE *fp_data, FILE* fp_oob, size_t* written)
+{
+ int eccpoi, oobsize;
+ size_t i;
+ uint8_t oobbuf[64];
+
+ memset(oobbuf, 0xff, sizeof(oobbuf));
+
+ switch(pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ err_msg("Unsupported page size: %d\n", pagesize);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+
+ /* write data */
+ *written += fwrite(buf, 1, pagesize, fp_data);
+
+ /* either separate oob or interleave with data */
+ if (fp_oob) {
+ i = fwrite(oobbuf, 1, oobsize, fp_oob);
+ if (ferror(fp_oob)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+ else {
+ i = fwrite(oobbuf, 1, oobsize, fp_data);
+ if (ferror(fp_data)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = -1;
+ int res = 0;
+ size_t written = 0, read;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .pagesize = PAGESIZE,
+ .padding = PADDING,
+ .fp_in = NULL,
+ .file_out_data = NULL,
+ .file_out_oob = NULL,
+ };
+
+ FILE* fp_out_data = stdout;
+ FILE* fp_out_oob = NULL;
+
+ parse_opt(argc, argv, &args);
+
+ uint8_t* buf = calloc(1, BUFSIZE);
+ if (!buf) {
+ err_quit("Cannot allocate page buffer.\n");
+ }
+
+ if (!args.fp_in) {
+ err_msg("No input image specified!\n");
+ goto err;
+ }
+
+ if (args.file_out_data) {
+ fp_out_data = fopen(args.file_out_data, "wb");
+ if (fp_out_data == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_data);
+ goto err;
+ }
+ }
+
+ if (args.file_out_oob) {
+ fp_out_oob = fopen(args.file_out_oob, "wb");
+ if (fp_out_oob == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_oob);
+ goto err;
+ }
+ }
+
+
+ while(1) {
+ read = fread(buf, 1, args.pagesize, args.fp_in);
+ if (feof(args.fp_in) && read == 0)
+ break;
+
+ if (read < args.pagesize) {
+ err_msg("Image not page aligned\n");
+ goto err;
+ }
+
+ if (ferror(args.fp_in)) {
+ err_msg("Read error\n");
+ goto err;
+ }
+
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ while (written < args.padding) {
+ memset(buf, 0xff, args.pagesize);
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ rc = 0;
+err:
+ free(buf);
+
+ if (args.fp_in)
+ fclose(args.fp_in);
+
+ if (fp_out_oob)
+ fclose(fp_out_oob);
+
+ if (fp_out_data && fp_out_data != stdout)
+ fclose(fp_out_data);
+
+ if (rc != 0) {
+ err_msg("Error during conversion. rc: %d\n", rc);
+ if (args.file_out_data)
+ remove(args.file_out_data);
+ if (args.file_out_oob)
+ remove(args.file_out_oob);
+ }
+ return rc;
+}
diff --git a/ubi-utils/src/bootenv.c b/ubi-utils/src/bootenv.c
new file mode 100644
index 0000000..a6dd4de
--- /dev/null
+++ b/ubi-utils/src/bootenv.c
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <bootenv.h>
+
+#include "hashmap.h"
+#include "error.h"
+
+#include <mtd/ubi-header.h>
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */
+
+/* Structures */
+struct bootenv {
+ hashmap_t map; ///< Pointer to hashmap which holds data structure.
+};
+
+struct bootenv_list {
+ hashmap_t head; ///< Pointer to list which holds the data structure.
+};
+
+/**
+ * @brief Remove the '\n' from a given line.
+ * @param line Input/Output line.
+ * @param size Size of the line.
+ * @param fp File Pointer.
+ * @return 0
+ * @return or error
+ */
+static int
+remove_lf(char *line, size_t size, FILE* fp)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (line[i] == '\n') {
+ line[i] = '\0';
+ return 0;
+ }
+ }
+
+ if (!feof(fp)) {
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Determine if a line contains only WS.
+ * @param line The line to process.
+ * @param size Size of input line.
+ * @return 1 Yes, only WS.
+ * @return 0 No, contains data.
+ */
+static int
+is_ws(const char *line, size_t size)
+{
+ size_t i = 0;
+
+ while (i < size) {
+ switch (line[i]) {
+ case '\n':
+ return 1;
+ case '#':
+ return 1;
+ case ' ':
+ i++;
+ continue;
+ case '\t':
+ i++;
+ continue;
+ default: /* any other char -> no cmnt */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief Build a list from a comma seperated value string.
+ * @param list Pointer to hashmap structure which shall store
+ * the list.
+ * @param value Comma seperated value string.
+ * @return 0
+ * @return or error.
+ */
+static int
+build_list_definition(hashmap_t list, const char *value)
+{
+ int rc = 0;
+ char *str = NULL;
+ char *ptr = NULL;
+ size_t len, i, j;
+
+ /* str: val1,val2 , val4,...,valN */
+ len = strlen(value);
+ str = (char*) malloc((len+1) * sizeof(char));
+
+ /* 1. reformat string: remove spaces */
+ for (i = 0, j = 0; i < len; i++) {
+ if (value[i] == ' ')
+ continue;
+
+ str[j] = value[i];
+ j++;
+ }
+ str[j] = '\0';
+
+ /* str: val1,val2,val4,...,valN\0*/
+ /* 2. replace ',' seperator with '\0' */
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (str[i] == ',') {
+ str[i] = '\0';
+ }
+ }
+
+ /* str: val1\0val2\0val4\0...\0valN\0*/
+ /* 3. insert definitions into a hash map, using it like a list */
+ i = j = 0;
+ ptr = str;
+ while (((i = strlen(ptr)) > 0) && (j < len)) {
+ rc = hashmap_add(list, ptr, "");
+ if (rc != 0) {
+ free(str);
+ return rc;
+ }
+ j += i+1;
+ if (j < len)
+ ptr += i+1;
+ }
+
+ free(str);
+ return rc;
+}
+
+/**
+ * @brief Extract a key value pair and add it to a hashmap
+ * @param str Input string which contains a key value pair.
+ * @param env The updated handle which contains the new pair.
+ * @return 0
+ * @return or error
+ * @note The input string format is: "key=value"
+ */
+static int
+extract_pair(const char *str, bootenv_t env)
+{
+ int rc = 0;
+ char *key = NULL;
+ char *val = NULL;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ rc = BOOTENV_EBADENTRY;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+
+ rc = bootenv_set(env, key, val);
+
+ err:
+ free(key);
+ return rc;
+}
+
+int
+bootenv_destroy(bootenv_t* env)
+{
+ int rc = 0;
+
+ if (env == NULL || *env == NULL)
+ return -EINVAL;
+
+ bootenv_t tmp = *env;
+
+ rc = hashmap_free(tmp->map);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ return rc;
+}
+
+int
+bootenv_create(bootenv_t* env)
+{
+ bootenv_t res;
+ res = (bootenv_t) calloc(1, sizeof(struct bootenv));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->map = hashmap_new();
+
+ if (res->map == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *env = res;
+
+ return 0;
+}
+
+
+/**
+ * @brief Read a formatted buffer and scan it for valid bootenv
+ * key/value pairs. Add those pairs into a hashmap.
+ * @param env Hashmap which shall be used to hold the data.
+ * @param buf Formatted buffer.
+ * @param size Size of the buffer.
+ * @return 0
+ * @return or error
+ */
+static int
+rd_buffer(bootenv_t env, const char *buf, size_t size)
+{
+ const char *curr = buf; /* ptr to current key/value pair */
+ uint32_t i, j; /* current length, chars processed */
+
+ if (buf[size - 1] != '\0') /* must end in '\0' */
+ return BOOTENV_EFMT;
+
+ for (j = 0; j < size; j += i, curr += i) {
+ /* strlen returns the size of the string upto
+ but not including the null terminator;
+ adding 1 to account for '\0' */
+ i = strlen(curr) + 1;
+
+ if (i == 1)
+ return 0; /* no string found */
+
+ if (extract_pair(curr, env) != 0)
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+
+int
+bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc)
+{
+ int rc;
+ char *buf = NULL;
+ size_t i = 0;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ /* allocate temp buffer */
+ buf = (char*) calloc(1, size * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* FIXME Andreas, please review this I removed size-1 and
+ * replaced it by just size, I saw the kernel image starting
+ * with a 0x0060.... and not with the 0x60.... what it should
+ * be. Is this a tools problem or is it a problem here where
+ * fp is moved not to the right place due to the former size-1
+ * here.
+ */
+ while((i < size) && (!feof(fp))) {
+ int c = fgetc(fp);
+ if (c == EOF) {
+ /* FIXME isn't this dangerous, to update
+ the boot envs with incomplete data? */
+ buf[i++] = '\0';
+ break; /* we have enough */
+ }
+ if (ferror(fp)) {
+ rc = -EIO;
+ goto err;
+ }
+
+ buf[i++] = (char)c;
+ }
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ /* transfer to hashmap */
+ rc = rd_buffer(env, buf, size);
+
+err:
+ free(buf);
+ return rc;
+}
+
+
+/**
+ * If we have a single file containing the boot-parameter size should
+ * be specified either as the size of the file or as BOOTENV_MAXSIZE.
+ * If the bootparameter are in the middle of a file we need the exact
+ * length of the data.
+ */
+int
+bootenv_read(FILE* fp, bootenv_t env, size_t size)
+{
+ return bootenv_read_crc(fp, env, size, NULL);
+}
+
+
+int
+bootenv_read_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ char *buf = NULL;
+ char *line = NULL;
+ char *lstart = NULL;
+ char *curr = NULL;
+ size_t len;
+ size_t size;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ size = BOOTENV_MAXSIZE;
+
+ /* allocate temp buffers */
+ buf = (char*) calloc(1, size * sizeof(char));
+ lstart = line = (char*) calloc(1, size * sizeof(char));
+ if ((buf == NULL) || (line == NULL)) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ curr = buf;
+ while ((line = fgets(line, size, fp)) != NULL) {
+ if (is_ws(line, size)) {
+ continue;
+ }
+ rc = remove_lf(line, BOOTENV_MAXSIZE, fp);
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* copy new line to binary buffer */
+ len = strlen(line);
+ if (len > size) {
+ rc = -EFBIG;
+ goto err;
+ }
+ size -= len; /* track remaining space */
+
+ memcpy(curr, line, len);
+ curr += len + 1; /* for \0 seperator */
+ }
+
+ rc = rd_buffer(env, buf, BOOTENV_MAXSIZE);
+err:
+ if (buf != NULL)
+ free(buf);
+ if (lstart != NULL)
+ free(lstart);
+ return rc;
+}
+
+static int
+fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused,
+ size_t *written)
+{
+ int rc = 0;
+ size_t keys_size, i;
+ size_t wr = 0;
+ const char **keys = NULL;
+ const char *val = NULL;
+
+ rc = bootenv_get_key_vector(env, &keys_size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < keys_size; i++) {
+ if (wr > BOOTENV_MAXSIZE) {
+ rc = -ENOSPC;
+ goto err;
+ }
+
+ rc = bootenv_get(env, keys[i], &val);
+ if (rc != 0)
+ goto err;
+
+ wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr,
+ "%s=%s", keys[i], val);
+ wr++; /* for \0 */
+ }
+
+ *written = wr;
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+int
+bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc)
+{
+ int rc = 0;
+ size_t size = 0;
+ char *buf = NULL;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size);
+ if (rc != 0)
+ goto err;
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ if (fwrite(buf, size, 1, fp) != 1) {
+ rc = -EIO;
+ goto err;
+ }
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write(FILE* fp, bootenv_t env)
+{
+ return bootenv_write_crc(fp, env, NULL);
+}
+
+int
+bootenv_compare(bootenv_t first, bootenv_t second)
+{
+ int rc;
+ size_t written_first, written_second;
+ char *buf_first, *buf_second;
+
+ if (first == NULL || second == NULL)
+ return -EINVAL;
+
+ buf_first = malloc(BOOTENV_MAXSIZE);
+ if (!buf_first)
+ return -ENOMEM;
+ buf_second = malloc(BOOTENV_MAXSIZE);
+ if (!buf_second) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE,
+ &written_first);
+ if (rc < 0)
+ goto err;
+ rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE,
+ &written_second);
+ if (rc < 0)
+ goto err;
+
+ if (written_first != written_second) {
+ rc = 1;
+ goto err;
+ }
+
+ rc = memcmp(buf_first, buf_second, written_first);
+ if (rc != 0) {
+ rc = 2;
+ goto err;
+ }
+
+err:
+ if (buf_first)
+ free(buf_first);
+ if (buf_second)
+ free(buf_second);
+
+ return rc;
+}
+
+int
+bootenv_size(bootenv_t env, size_t *size)
+{
+ int rc = 0;
+ char *buf = NULL;
+
+ if (env == NULL)
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size);
+ if (rc != 0)
+ goto err;
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, wr, i;
+ const char **keys = NULL;
+ const char *key = NULL;
+ const char *val = NULL;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ rc = bootenv_get_key_vector(env, &size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ key = keys[i];
+ rc = bootenv_get(env, key, &val);
+ if (rc != 0)
+ goto err;
+
+ wr = fprintf(fp, "%s=%s\n", key, val);
+ if (wr != strlen(key) + strlen(val) + 2) {
+ rc = -EIO;
+ goto err;
+ }
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+ return rc;
+}
+
+int
+bootenv_valid(bootenv_t env ubi_unused)
+{
+ /* @FIXME No sanity check implemented. */
+ return 0;
+}
+
+int
+bootenv_copy_bootenv(bootenv_t in, bootenv_t *out)
+{
+ int rc = 0;
+ const char *tmp = NULL;
+ const char **keys = NULL;
+ size_t vec_size, i;
+
+ if ((in == NULL) || (out == NULL))
+ return -EINVAL;
+
+ /* purge output var for sure... */
+ rc = bootenv_destroy(out);
+ if (rc != 0)
+ return rc;
+
+ /* create the new map */
+ rc = bootenv_create(out);
+ if (rc != 0)
+ goto err;
+
+ /* get the key list from the input map */
+ rc = bootenv_get_key_vector(in, &vec_size, 0, &keys);
+ if (rc != 0)
+ goto err;
+
+ if (vec_size != hashmap_size(in->map)) {
+ rc = BOOTENV_ECOPY;
+ goto err;
+ }
+
+ /* make a deep copy of the hashmap */
+ for (i = 0; i < vec_size; i++) {
+ rc = bootenv_get(in, keys[i], &tmp);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_set(*out, keys[i], tmp);
+ if (rc != 0)
+ goto err;
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+int
+bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings, char *err_buf ubi_unused,
+ size_t err_buf_size ubi_unused)
+{
+ bootenv_list_t l_old = NULL;
+ bootenv_list_t l_new = NULL;
+ const char *pdd_old = NULL;
+ const char *pdd_new = NULL;
+ const char *tmp = NULL;
+ const char **vec_old = NULL;
+ const char **vec_new = NULL;
+ const char **pdd_up_vec = NULL;
+ size_t vec_old_size, vec_new_size, pdd_up_vec_size, i;
+ int rc = 0;
+
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ /* get the pdd strings, e.g.:
+ * pdd_old=a,b,c
+ * pdd_new=a,c,d,e */
+ rc = bootenv_get(env_old, "pdd", &pdd_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_get(env_new, "pdd", &pdd_new);
+ if (rc != 0)
+ goto err;
+
+ /* put it into a list and then convert it to an vector */
+ rc = bootenv_list_create(&l_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_list_create(&l_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_old, pdd_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_new, pdd_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_copy_bootenv(env_new, env_res);
+ if (rc != 0)
+ goto err;
+
+ /* calculate the update vector between the old and new pdd */
+ pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size,
+ vec_new, vec_new_size, &pdd_up_vec_size);
+
+ if (pdd_up_vec == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (pdd_up_vec_size != 0) {
+ /* need to warn the user about the unset of
+ * some pdd/bootenv values */
+ *warnings = BOOTENV_WPDD_STRING_DIFFERS;
+
+ /* remove all entries in the new bootenv load */
+ for (i = 0; i < pdd_up_vec_size; i++) {
+ bootenv_unset(*env_res, pdd_up_vec[i]);
+ }
+ }
+
+ /* generate the keep array and copy old pdd values to new bootenv */
+ for (i = 0; i < vec_old_size; i++) {
+ rc = bootenv_get(env_old, vec_old[i], &tmp);
+ if (rc != 0) {
+ rc = BOOTENV_EPDDINVAL;
+ goto err;
+ }
+ rc = bootenv_set(*env_res, vec_old[i], tmp);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+ /* put the old pdd string into the result map */
+ rc = bootenv_set(*env_res, "pdd", pdd_old);
+ if (rc != 0) {
+ goto err;
+ }
+
+
+err:
+ if (vec_old != NULL)
+ free(vec_old);
+ if (vec_new != NULL)
+ free(vec_new);
+ if (pdd_up_vec != NULL)
+ free(pdd_up_vec);
+
+ bootenv_list_destroy(&l_old);
+ bootenv_list_destroy(&l_new);
+ return rc;
+}
+
+
+int
+bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings ubi_unused,
+ char *err_buf ubi_unused, size_t err_buf_size ubi_unused)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ return bootenv_copy_bootenv(env_new, env_res);
+}
+
+int
+bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings ubi_unused, char *err_buf, size_t err_buf_size)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ snprintf(err_buf, err_buf_size, "The PDD merge operation is not "
+ "implemented. Contact: <oliloh@de.ibm.com>");
+
+ return BOOTENV_ENOTIMPL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+bootenv_get(bootenv_t env, const char *key, const char **value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ *value = hashmap_lookup(env->map, key);
+ if (*value == NULL)
+ return BOOTENV_ENOTFOUND;
+
+ return 0;
+}
+
+int
+bootenv_get_num(bootenv_t env, const char *key, uint32_t *value)
+{
+ char *endptr = NULL;
+ const char *str;
+
+ if (env == NULL)
+ return 0;
+
+ str = hashmap_lookup(env->map, key);
+ if (!str)
+ return -EINVAL;
+
+ *value = strtoul(str, &endptr, 0);
+
+ if (*endptr == '\0') {
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int
+bootenv_set(bootenv_t env, const char *key, const char *value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_add(env->map, key, value);
+}
+
+int
+bootenv_unset(bootenv_t env, const char *key)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_remove(env->map, key);
+}
+
+int
+bootenv_get_key_vector(bootenv_t env, size_t* size, int sort,
+ const char ***vector)
+{
+ if ((env == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(env->map, size, sort);
+
+ if (*vector == NULL)
+ return -EINVAL;
+
+ return 0;
+}
+
+int
+bootenv_dump(bootenv_t env)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_dump(env->map);
+}
+
+int
+bootenv_list_create(bootenv_list_t *list)
+{
+ bootenv_list_t res;
+ res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->head = hashmap_new();
+
+ if (res->head == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *list = res;
+ return 0;
+}
+
+int
+bootenv_list_destroy(bootenv_list_t *list)
+{
+ int rc = 0;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bootenv_list_t tmp = *list;
+ if (tmp == 0)
+ return 0;
+
+ rc = hashmap_free(tmp->head);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ *list = NULL;
+ return 0;
+}
+
+int
+bootenv_list_import(bootenv_list_t list, const char *str)
+{
+ if (list == NULL)
+ return -EINVAL;
+
+ return build_list_definition(list->head, str);
+}
+
+int
+bootenv_list_export(bootenv_list_t list, char **string)
+{
+ size_t size, i, j, bufsize, tmp, rc = 0;
+ const char **items;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bufsize = BOOTENV_MAXLINE;
+ char *res = (char*) malloc(bufsize * sizeof(char));
+ if (res == NULL)
+ return -ENOMEM;
+
+ rc = bootenv_list_to_vector(list, &size, &items);
+ if (rc != 0) {
+ goto err;
+ }
+
+ j = 0;
+ for (i = 0; i < size; i++) {
+ tmp = strlen(items[i]);
+ if (j >= bufsize) {
+ bufsize += BOOTENV_MAXLINE;
+ res = (char*) realloc(res, bufsize * sizeof(char));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+ memcpy(res + j, items[i], tmp);
+ j += tmp;
+ if (i < (size - 1)) {
+ res[j] = ',';
+ j++;
+ }
+ }
+ j++;
+ res[j] = '\0';
+ free(items);
+ *string = res;
+ return 0;
+err:
+ free(items);
+ return rc;
+}
+
+int
+bootenv_list_add(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_add(list->head, item, "");
+}
+
+int
+bootenv_list_remove(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_remove(list->head, item);
+}
+
+int
+bootenv_list_is_in(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_lookup(list->head, item) != NULL ? 1 : 0;
+}
+
+int
+bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector)
+{
+ if ((list == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(list->head, size, 1);
+ if (*vector == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int
+bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector)
+{
+ int rc = 0;
+ size_t i;
+ uint32_t* res = NULL;
+ char *endptr = NULL;
+ const char **a = NULL;
+
+ rc = bootenv_list_to_vector(list, size, &a);
+ if (rc != 0)
+ goto err;
+
+ res = (uint32_t*) malloc (*size * sizeof(uint32_t));
+ if (!res)
+ goto err;
+
+ for (i = 0; i < *size; i++) {
+ res[i] = strtoul(a[i], &endptr, 0);
+ if (*endptr != '\0')
+ goto err;
+ }
+
+ if (a)
+ free(a);
+ *vector = res;
+ return 0;
+
+err:
+ if (a)
+ free(a);
+ if (res)
+ free(res);
+ return rc;
+}
diff --git a/ubi-utils/src/bootenv.h b/ubi-utils/src/bootenv.h
new file mode 100644
index 0000000..8fecdbf
--- /dev/null
+++ b/ubi-utils/src/bootenv.h
@@ -0,0 +1,434 @@
+#ifndef __BOOTENV_H__
+#define __BOOTENV_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <pfiflash.h>
+
+/* DOXYGEN DOCUMENTATION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file bootenv.h
+ * @author oliloh@de.ibm.com
+ * @version 1.3
+ *
+ * 1.3 Some renaming
+ */
+
+/**
+ * @mainpage Usage
+ *
+ * @section intro Introduction
+ * This library provides all functionality to handle with the so-called
+ * platform description data (PDD) and the bootparameters defined in
+ * U-Boot. It is able to apply the defined PDD operations in PDD update
+ * scenarios. For more information about the PDD and bootparameter
+ * environment "bootenv" confer the PDD documentation.
+ *
+ * @section ret Return codes
+ * This library defines some return codes which will be delivered classified
+ * as warnings or errors. See the "Defines" section for details and numeric
+ * values.
+ *
+ * @section benv Bootenv format description
+ * There are two different input formats:
+ * - text files
+ * - binary files
+ *
+ * @subsection txt Text Files
+ * Text files have to be specified like:
+ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim
+ *
+ * @subsection bin Binary files
+ * Binary files have to be specified like:
+ * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim
+ * You can confer the U-Boot documentation for more details.
+ *
+ * @section benvlists Bootenv lists format description.
+ * Values referenced in the preceeding subsection can be
+ * defined like lists:
+ * @verbatim value1,value2,value3 @endverbatim
+ * There are some situation where a conversion of a comma
+ * seperated list can be useful, e.g. to get a list
+ * of defined PDD entries.
+ */
+
+#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */
+
+/**
+ * @def BOOTENV_ECRC
+ * @brief Given binary file is to large.
+ * @def BOOTENV_EFMT
+ * @brief Given bootenv section has an invalid format
+ * @def BOOTENV_EBADENTRY
+ * @brief Bad entry in the bootenv section.
+ * @def BOOTENV_EINVAL
+ * @brief Invalid bootenv defintion.
+ * @def BOOTENV_ENOPDD
+ * @brief Given bootenv sectoin has no PDD defintion string (pdd=...).
+ * @def BOOTENV_EPDDINVAL
+ * @brief Given bootenv section has an invalid PDD defintion.
+ * @def BOOTENV_ENOTIMPL
+ * @brief Functionality not implemented.
+ * @def BOOTENV_ECOPY
+ * @brief Bootenv memory copy error
+ * @def BOOTENV_ENOTFOUND
+ * @brief Given key has has no value.
+ * @def BOOTENV_EMAX
+ * @brief Highest error value.
+ */
+#define BOOTENV_ETOOBIG 1
+#define BOOTENV_EFMT 2
+#define BOOTENV_EBADENTRY 3
+#define BOOTENV_EINVAL 4
+#define BOOTENV_ENOPDD 5
+#define BOOTENV_EPDDINVAL 6
+#define BOOTENV_ENOTIMPL 7
+#define BOOTENV_ECOPY 8
+#define BOOTENV_ENOTFOUND 9
+#define BOOTENV_EMAX 10
+
+/**
+ * @def BOOTENV_W
+ * @brief A warning which is handled internally as an error
+ * but can be recovered by manual effort.
+ * @def BOOTENV_WPDD_STRING_DIFFERS
+ * @brief The PDD strings of old and new PDD differ and
+ * can cause update problems, because new PDD values
+ * are removed from the bootenv section completely.
+ */
+#define BOOTENV_W 20
+#define BOOTENV_WPDD_STRING_DIFFERS 21
+#define BOOTENV_WMAX 22 /* highest warning value */
+
+
+typedef struct bootenv *bootenv_t;
+ /**< A bootenv library handle. */
+
+typedef struct bootenv_list *bootenv_list_t;
+ /**< A handle for a value list. */
+
+typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*,
+ int*, char*, size_t);
+
+
+/**
+ * @brief Get a new handle.
+ * @return 0
+ * @return or error
+ * */
+int bootenv_create(bootenv_t *env);
+
+/**
+ * @brief Cleanup structure.
+ * @param env Bootenv structure which shall be destroyed.
+ * @return 0
+ * @return or error
+ */
+int bootenv_destroy(bootenv_t *env);
+
+/**
+ * @brief Copy a bootenv handle.
+ * @param in The input bootenv.
+ * @param out The copied output bootenv. Discards old data.
+ * @return 0
+ * @return or error
+ */
+int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out);
+
+/**
+ * @brief Looks for a value inside the bootenv data.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get(bootenv_t env, const char *key, const char **value);
+
+
+/**
+ * @brief Looks for a value inside the bootenv data and converts it to num.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @param value A pointer to the resulting numerical value
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value);
+
+/**
+ * @brief Set a bootenv value by key.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @param value Value to set.
+ * @return 0
+ * @return or error
+ */
+int bootenv_set(bootenv_t env, const char *key, const char *value);
+
+/**
+ * @brief Remove the given key (and its value) from a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @return 0
+ * @return or error
+ */
+int bootenv_unset(bootenv_t env, const char *key);
+
+
+/**
+ * @brief Get a vector of all keys which are currently set
+ * within a bootenv handle.
+ * @param env Handle to a bootenv structure.
+ * @param size The size of the allocated array structure.
+ * @param sort Flag, if set the vector is sorted ascending.
+ * @return NULL on error.
+ * @return !NULL a pointer to the first element the allocated vector.
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort,
+ const char ***vector);
+
+/**
+ * @brief Calculate the size in bytes which are necessary to write the
+ * current bootenv section in a *binary file.
+ * @param env bootenv handle.
+ * @param size The size in bytes of the bootenv handle.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_size(bootenv_t env, size_t *size);
+
+/**
+ * @brief Read a binary bootenv file.
+ * @param fp File pointer to input stream.
+ * @param env bootenv handle.
+ * @param size maximum data size.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read(FILE* fp, bootenv_t env, size_t size);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc);
+
+/**
+ * @brief Read bootenv data from an text/ascii file.
+ * @param fp File pointer to ascii PDD file.
+ * @param env bootenv handle
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Write a bootenv structure to the given location (binary).
+ * @param fp Filepointer to binary file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write(FILE* fp, bootenv_t env);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
+
+/**
+ * @brief Write a bootenv structure to the given location (text).
+ * @param fp Filepointer to text file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Compare bootenvs using memcmp().
+ * @param first First bootenv.
+ * @param second Second bootenv.
+ * @return 0 if bootenvs are equal
+ * @return < 0 if error
+ * @return > 0 if unequal
+ */
+int bootenv_compare(bootenv_t first, bootenv_t second);
+
+/**
+ * @brief Prototype for a PDD handling funtion
+ */
+
+/**
+ * @brief The PDD keep operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of PDD keep.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+
+/**
+ * @brief The PDD merge operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of merge-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief The PDD overwrite operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of overwrite-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_overwrite(bootenv_t env_new,
+ bootenv_t env_old, bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Dump a bootenv structure to stdout. (Debug)
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_dump(bootenv_t env);
+
+/**
+ * @brief Validate a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_valid(bootenv_t env);
+
+/**
+ * @brief Create a new bootenv list structure.
+ * @return NULL on error
+ * @return or a new list handle.
+ * @note This structure is used to store values in a list.
+ * A useful addition when handling PDD strings.
+ */
+int bootenv_list_create(bootenv_list_t *list);
+
+/**
+ * @brief Destroy a bootenv list structure
+ * @param list Handle to a bootenv list structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_destroy(bootenv_list_t *list);
+
+/**
+ * @brief Import a list from a comma seperated string
+ * @param list Handle to a bootenv list structure.
+ * @param str Comma seperated string list.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_import(bootenv_list_t list, const char *str);
+
+/**
+ * @brief Export a list to a string of comma seperated values.
+ * @param list Handle to a bootenv list structure.
+ * @return NULL one error
+ * @return or pointer to a newly allocated string.
+ * @warning Free the allocated memory by yourself!
+ */
+int bootenv_list_export(bootenv_list_t list, char **string);
+
+/**
+ * @brief Add an item to the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_add(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Remove an item from the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_remove(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Check if a given item is in a given list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 1 Item is in list.
+ * @return 0 Item is not in list.
+ */
+int bootenv_list_is_in(bootenv_list_t list, const char *item);
+
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_vector(bootenv_list_t list, size_t *size,
+ const char ***vector);
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__BOOTENV_H__ */
diff --git a/ubi-utils/src/common.c b/ubi-utils/src/common.c
deleted file mode 100644
index fec640d..0000000
--- a/ubi-utils/src/common.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2007, 2008 Nokia Corporation
- *
- * 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.
- */
-
-/*
- * This file contains various common stuff used by UBI utilities.
- *
- * Authors: Artem Bityutskiy
- * Adrian Hunter
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-/**
- * get_multiplier - convert size specifier to an integer multiplier.
- * @str: the size specifier string
- *
- * This function parses the @str size specifier, which may be one of
- * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive
- * size multiplier in case of success and %-1 in case of failure.
- */
-static int get_multiplier(const char *str)
-{
- if (!str)
- return 1;
-
- /* Remove spaces before the specifier */
- while (*str == ' ' || *str == '\t')
- str += 1;
-
- if (!strcmp(str, "KiB"))
- return 1024;
- if (!strcmp(str, "MiB"))
- return 1024 * 1024;
- if (!strcmp(str, "GiB"))
- return 1024 * 1024 * 1024;
-
- return -1;
-}
-
-/**
- * ubiutils_get_bytes - convert a string containing amount of bytes into an
- * integer
- * @str: string to convert
- *
- * This function parses @str which may have an onee of 'KiB', 'MiB', or 'GiB'
- * size specifiers. Returns positive amount of bytes in case of success and %-1
- * in case of failure.
- */
-long long ubiutils_get_bytes(const char *str)
-{
- char *endp;
- long long bytes = strtoull(str, &endp, 0);
-
- if (endp == str || bytes < 0) {
- fprintf(stderr, "incorrect amount of bytes: \"%s\"", str);
- return -1;
- }
-
- if (*endp != '\0') {
- int mult = get_multiplier(endp);
-
- if (mult == -1) {
- fprintf(stderr, "bad size specifier: \"%s\" - "
- "should be 'KiB', 'MiB' or 'GiB'", endp);
- return -1;
- }
- bytes *= mult;
- }
-
- return bytes;
-}
-
-/**
- * ubiutils_print_bytes - print bytes.
- * @bytes: variable to print
- * @bracket: whether brackets have to be put or not
- *
- * This is a helper function which prints amount of bytes in a human-readable
- * form, i.e., it prints the exact amount of bytes following by the approximate
- * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes
- * is.
- */
-void ubiutils_print_bytes(long long bytes, int bracket)
-{
- const char *p;
-
- if (bracket)
- p = " (";
- else
- p = ", ";
-
- printf("%lld bytes", bytes);
-
- if (bytes > 1024 * 1024 * 1024)
- printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024));
- else if (bytes > 1024 * 1024)
- printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024));
- else if (bytes > 1024 && bytes != 0)
- printf("%s%.1f KiB", p, (double)bytes / 1024);
- else
- return;
-
- if (bracket)
- printf(")");
-}
-
-/**
- * ubiutils_print_text - print text and fold it.
- * @stream: file stream to print to
- * @text: text to print
- * @width: maximum allowed text width
- *
- * Print text and fold it so that each line would not have more then @width
- * characters.
- */
-void ubiutils_print_text(FILE *stream, const char *text, int width)
-{
- int pos, bpos = 0;
- const char *p;
- char line[1024];
-
- if (width > 1023) {
- fprintf(stream, "%s\n", text);
- return;
- }
- p = text;
- pos = 0;
- while (p[pos]) {
- while (!isspace(p[pos])) {
- line[pos] = p[pos];
- if (!p[pos])
- break;
- ++pos;
- if (pos == width) {
- line[pos] = '\0';
- fprintf(stream, "%s\n", line);
- p += pos;
- pos = 0;
- }
- }
- while (pos < width) {
- line[pos] = p[pos];
- if (!p[pos]) {
- bpos = pos;
- break;
- }
- if (isspace(p[pos]))
- bpos = pos;
- ++pos;
- }
- line[bpos] = '\0';
- fprintf(stream, "%s\n", line);
- p += bpos;
- pos = 0;
- while (p[pos] && isspace(p[pos]))
- ++p;
- }
-}
diff --git a/ubi-utils/src/common.h b/ubi-utils/src/common.h
deleted file mode 100644
index a3fc51f..0000000
--- a/ubi-utils/src/common.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) Artem Bityutskiy, 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.
- */
-
-#ifndef __UBI_UTILS_COMMON_H__
-#define __UBI_UTILS_COMMON_H__
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define MIN(a ,b) ((a) < (b) ? (a) : (b))
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-/* Verbose messages */
-#define verbose(verbose, fmt, ...) do { \
- if (verbose) \
- printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \
-} while(0)
-
-/* Normal messages */
-#define normsg(fmt, ...) do { \
- printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \
-} while(0)
-
-/* Error messages */
-#define errmsg(fmt, ...) ({ \
- fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \
- -1; \
-})
-
-/* System error messages */
-#define sys_errmsg(fmt, ...) ({ \
- int _err = errno; \
- fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \
- fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \
- -1; \
-})
-
-/* Warnings */
-#define warnmsg(fmt, ...) do { \
- fprintf(stderr, PROGRAM_NAME " warning: " fmt "\n", ##__VA_ARGS__); \
-} while(0)
-
-long long ubiutils_get_bytes(const char *str);
-void ubiutils_print_bytes(long long bytes, int bracket);
-void ubiutils_print_text(FILE *stream, const char *txt, int len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !__UBI_UTILS_COMMON_H__ */
diff --git a/ubi-utils/src/config.h b/ubi-utils/src/config.h
new file mode 100644
index 0000000..55e60f3
--- /dev/null
+++ b/ubi-utils/src/config.h
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT \
+ "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define ubi_unused __attribute__((unused))
+
+#endif
diff --git a/ubi-utils/src/crc32.c b/ubi-utils/src/crc32.c
index 6b1e50c..666e217 100644
--- a/ubi-utils/src/crc32.c
+++ b/ubi-utils/src/crc32.c
@@ -1,95 +1,83 @@
/*
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
+ * Copyright (c) International Business Machines Corp., 2006
*
- * First, the polynomial itself and its table of feedback terms. The
- * polynomial is
- * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ * 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.
*
- * Note that we take it "backwards" and put the highest-order term in
- * the lowest-order bit. The X^32 term is "implied"; the LSB is the
- * X^31 term, etc. The X^0 term (usually shown as "+1") results in
- * the MSB being 1
+ * 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.
*
- * Note that the usual hardware shift register implementation, which
- * is what we're using (we're merely optimizing it by doing eight-bit
- * chunks at a time) shifts bits into the lowest-order term. In our
- * implementation, that means shifting towards the right. Why do we
- * do it this way? Because the calculated CRC must be transmitted in
- * order from highest-order term to lowest-order term. UARTs transmit
- * characters in order from LSB to MSB. By storing the CRC this way
- * we hand it to the UART in the order low-byte to high-byte; the UART
- * sends each low-bit to hight-bit; and the result is transmission bit
- * by bit from highest- to lowest-order term without requiring any bit
- * shuffling on our part. Reception works similarly
+ * 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.
*
- * The feedback terms table consists of 256, 32-bit entries. Notes
+ * Author: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
*
- * The table can be generated at runtime if desired; code to do so
- * is shown later. It might not be obvious, but the feedback
- * terms simply represent the results of eight shift/xor opera
- * tions for all combinations of data and CRC register values
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
*
- * The values must be right-shifted by eight bits by the "updcrc
- * logic; the shift must be unsigned (bring in zeroes). On some
- * hardware you could probably optimize the shift in assembler by
- * using byte-swap instructions
- * polynomial $edb88320
*/
#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY 0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table: pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+ uint32_t crc;
+ int i, j;
+
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ CRC_POLY;
+ else
+ crc >>= 1;
+ }
+ table[i] = crc;
+ }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table: pointer to the CRC table
+ * @crc: initial crc value
+ * @buf: pointer to the buffer
+ * @len: number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+ int len)
+{
+ const unsigned char *p = buf;
-const uint32_t crc32_table[256] = {
- 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
- 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
- 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
- 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
- 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
- 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
- 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
- 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
- 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
- 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
- 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
- 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
- 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
- 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
- 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
- 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
- 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
- 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
- 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
- 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
- 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
- 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
- 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
- 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
- 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
- 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
- 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
- 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
- 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
- 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
- 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
- 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
- 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
- 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
- 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
- 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
- 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
- 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
- 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
- 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
- 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
- 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
- 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
- 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
- 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
- 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
- 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
- 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
- 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
- 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
- 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
- 0x2d02ef8dL
-};
+ while(--len >= 0)
+ crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/ubi-utils/src/crc32.h b/ubi-utils/src/crc32.h
index ee3145b..31362b0 100644
--- a/ubi-utils/src/crc32.h
+++ b/ubi-utils/src/crc32.h
@@ -1,19 +1,36 @@
-#ifndef CRC32_H
-#define CRC32_H
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+/*
+ * Author: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
#include <stdint.h>
-extern const uint32_t crc32_table[256];
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
-/* Return a 32-bit CRC of the contents of the buffer. */
-
- static inline uint32_t
-crc32(uint32_t val, const void *ss, int len)
-{
- const unsigned char *s = ss;
- while (--len >= 0)
- val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
- return val;
-}
-
-#endif
+#endif /* __CRC32_H__ */
diff --git a/ubi-utils/src/dictionary.c b/ubi-utils/src/dictionary.c
deleted file mode 100644
index b7c9ebf..0000000
--- a/ubi-utils/src/dictionary.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/*-------------------------------------------------------------------------*/
-/**
- @file dictionary.c
- @author N. Devillard
- @date Sep 2007
- @version $Revision: 1.27 $
- @brief Implements a dictionary for string variables.
-
- This module implements a simple dictionary object, i.e. a list
- of string/string associations. This object is useful to store e.g.
- informations retrieved from a configuration file (ini files).
-*/
-/*--------------------------------------------------------------------------*/
-
-/*
- $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $
- $Revision: 1.27 $
-*/
-/*---------------------------------------------------------------------------
- Includes
- ---------------------------------------------------------------------------*/
-#include "dictionary.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/** Maximum value size for integers and doubles. */
-#define MAXVALSZ 1024
-
-/** Minimal allocated number of entries in a dictionary */
-#define DICTMINSZ 128
-
-/** Invalid key token */
-#define DICT_INVALID_KEY ((char*)-1)
-
-/*---------------------------------------------------------------------------
- Private functions
- ---------------------------------------------------------------------------*/
-
-/* Doubles the allocated size associated to a pointer */
-/* 'size' is the current allocated size. */
-static void * mem_double(void * ptr, int size)
-{
- void * newptr ;
-
- newptr = calloc(2*size, 1);
- if (newptr==NULL) {
- return NULL ;
- }
- memcpy(newptr, ptr, size);
- free(ptr);
- return newptr ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Duplicate a string
- @param s String to duplicate
- @return Pointer to a newly allocated string, to be freed with free()
-
- This is a replacement for strdup(). This implementation is provided
- for systems that do not have it.
- */
-/*--------------------------------------------------------------------------*/
-static char * xstrdup(char * s)
-{
- char * t ;
- if (!s)
- return NULL ;
- t = malloc(strlen(s)+1) ;
- if (t) {
- strcpy(t,s);
- }
- return t ;
-}
-
-/*---------------------------------------------------------------------------
- Function codes
- ---------------------------------------------------------------------------*/
-/*-------------------------------------------------------------------------*/
-/**
- @brief Compute the hash key for a string.
- @param key Character string to use for key.
- @return 1 unsigned int on at least 32 bits.
-
- This hash function has been taken from an Article in Dr Dobbs Journal.
- This is normally a collision-free function, distributing keys evenly.
- The key is stored anyway in the struct so that collision can be avoided
- by comparing the key itself in last resort.
- */
-/*--------------------------------------------------------------------------*/
-unsigned dictionary_hash(char * key)
-{
- int len ;
- unsigned hash ;
- int i ;
-
- len = strlen(key);
- for (hash=0, i=0 ; i<len ; i++) {
- hash += (unsigned)key[i] ;
- hash += (hash<<10);
- hash ^= (hash>>6) ;
- }
- hash += (hash <<3);
- hash ^= (hash >>11);
- hash += (hash <<15);
- return hash ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Create a new dictionary object.
- @param size Optional initial size of the dictionary.
- @return 1 newly allocated dictionary objet.
-
- This function allocates a new dictionary object of given size and returns
- it. If you do not know in advance (roughly) the number of entries in the
- dictionary, give size=0.
- */
-/*--------------------------------------------------------------------------*/
-dictionary * dictionary_new(int size)
-{
- dictionary * d ;
-
- /* If no size was specified, allocate space for DICTMINSZ */
- if (size<DICTMINSZ) size=DICTMINSZ ;
-
- if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
- return NULL;
- }
- d->size = size ;
- d->val = (char **)calloc(size, sizeof(char*));
- d->key = (char **)calloc(size, sizeof(char*));
- d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
- return d ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Delete a dictionary object
- @param d dictionary object to deallocate.
- @return void
-
- Deallocate a dictionary object and all memory associated to it.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_del(dictionary * d)
-{
- int i ;
-
- if (d==NULL) return ;
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]!=NULL)
- free(d->key[i]);
- if (d->val[i]!=NULL)
- free(d->val[i]);
- }
- free(d->val);
- free(d->key);
- free(d->hash);
- free(d);
- return ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get a value from a dictionary.
- @param d dictionary object to search.
- @param key Key to look for in the dictionary.
- @param def Default value to return if key not found.
- @return 1 pointer to internally allocated character string.
-
- This function locates a key in a dictionary and returns a pointer to its
- value, or the passed 'def' pointer if no such key can be found in
- dictionary. The returned character pointer points to data internal to the
- dictionary object, you should not try to free it or modify it.
- */
-/*--------------------------------------------------------------------------*/
-char * dictionary_get(dictionary * d, char * key, char * def)
-{
- unsigned hash ;
- int i ;
-
- hash = dictionary_hash(key);
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- /* Compare hash */
- if (hash==d->hash[i]) {
- /* Compare string, to avoid hash collisions */
- if (!strcmp(key, d->key[i])) {
- return d->val[i] ;
- }
- }
- }
- return def ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Set a value in a dictionary.
- @param d dictionary object to modify.
- @param key Key to modify or add.
- @param val Value to add.
- @return int 0 if Ok, anything else otherwise
-
- If the given key is found in the dictionary, the associated value is
- replaced by the provided one. If the key cannot be found in the
- dictionary, it is added to it.
-
- It is Ok to provide a NULL value for val, but NULL values for the dictionary
- or the key are considered as errors: the function will return immediately
- in such a case.
-
- Notice that if you dictionary_set a variable to NULL, a call to
- dictionary_get will return a NULL value: the variable will be found, and
- its value (NULL) is returned. In other words, setting the variable
- content to NULL is equivalent to deleting the variable from the
- dictionary. It is not possible (in this implementation) to have a key in
- the dictionary without value.
-
- This function returns non-zero in case of failure.
- */
-/*--------------------------------------------------------------------------*/
-int dictionary_set(dictionary * d, char * key, char * val)
-{
- int i ;
- unsigned hash ;
-
- if (d==NULL || key==NULL) return -1 ;
-
- /* Compute hash for this key */
- hash = dictionary_hash(key) ;
- /* Find if value is already in dictionary */
- if (d->n>0) {
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- if (hash==d->hash[i]) { /* Same hash value */
- if (!strcmp(key, d->key[i])) { /* Same key */
- /* Found a value: modify and return */
- if (d->val[i]!=NULL)
- free(d->val[i]);
- d->val[i] = val ? xstrdup(val) : NULL ;
- /* Value has been modified: return */
- return 0 ;
- }
- }
- }
- }
- /* Add a new value */
- /* See if dictionary needs to grow */
- if (d->n==d->size) {
-
- /* Reached maximum size: reallocate dictionary */
- d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ;
- d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ;
- d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
- if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
- /* Cannot grow dictionary */
- return -1 ;
- }
- /* Double size */
- d->size *= 2 ;
- }
-
- /* Insert key in the first empty slot */
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL) {
- /* Add key here */
- break ;
- }
- }
- /* Copy key */
- d->key[i] = xstrdup(key);
- d->val[i] = val ? xstrdup(val) : NULL ;
- d->hash[i] = hash;
- d->n ++ ;
- return 0 ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Delete a key in a dictionary
- @param d dictionary object to modify.
- @param key Key to remove.
- @return void
-
- This function deletes a key in a dictionary. Nothing is done if the
- key cannot be found.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_unset(dictionary * d, char * key)
-{
- unsigned hash ;
- int i ;
-
- if (key == NULL) {
- return;
- }
-
- hash = dictionary_hash(key);
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- /* Compare hash */
- if (hash==d->hash[i]) {
- /* Compare string, to avoid hash collisions */
- if (!strcmp(key, d->key[i])) {
- /* Found key */
- break ;
- }
- }
- }
- if (i>=d->size)
- /* Key not found */
- return ;
-
- free(d->key[i]);
- d->key[i] = NULL ;
- if (d->val[i]!=NULL) {
- free(d->val[i]);
- d->val[i] = NULL ;
- }
- d->hash[i] = 0 ;
- d->n -- ;
- return ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Dump a dictionary to an opened file pointer.
- @param d Dictionary to dump
- @param f Opened file pointer.
- @return void
-
- Dumps a dictionary onto an opened file pointer. Key pairs are printed out
- as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
- output file pointers.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_dump(dictionary * d, FILE * out)
-{
- int i ;
-
- if (d==NULL || out==NULL) return ;
- if (d->n<1) {
- fprintf(out, "empty dictionary\n");
- return ;
- }
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]) {
- fprintf(out, "%20s\t[%s]\n",
- d->key[i],
- d->val[i] ? d->val[i] : "UNDEF");
- }
- }
- return ;
-}
-
-
-/* Test code */
-#ifdef TESTDIC
-#define NVALS 20000
-int main(int argc, char *argv[])
-{
- dictionary * d ;
- char * val ;
- int i ;
- char cval[90] ;
-
- /* Allocate dictionary */
- printf("allocating...\n");
- d = dictionary_new(0);
-
- /* Set values in dictionary */
- printf("setting %d values...\n", NVALS);
- for (i=0 ; i<NVALS ; i++) {
- sprintf(cval, "%04d", i);
- dictionary_set(d, cval, "salut");
- }
- printf("getting %d values...\n", NVALS);
- for (i=0 ; i<NVALS ; i++) {
- sprintf(cval, "%04d", i);
- val = dictionary_get(d, cval, DICT_INVALID_KEY);
- if (val==DICT_INVALID_KEY) {
- printf("cannot get value for key [%s]\n", cval);
- }
- }
- printf("unsetting %d values...\n", NVALS);
- for (i=0 ; i<NVALS ; i++) {
- sprintf(cval, "%04d", i);
- dictionary_unset(d, cval);
- }
- if (d->n != 0) {
- printf("error deleting values\n");
- }
- printf("deallocating...\n");
- dictionary_del(d);
- return 0 ;
-}
-#endif
-/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/ubi-utils/src/dictionary.h b/ubi-utils/src/dictionary.h
deleted file mode 100644
index c7d1790..0000000
--- a/ubi-utils/src/dictionary.h
+++ /dev/null
@@ -1,174 +0,0 @@
-
-/*-------------------------------------------------------------------------*/
-/**
- @file dictionary.h
- @author N. Devillard
- @date Sep 2007
- @version $Revision: 1.12 $
- @brief Implements a dictionary for string variables.
-
- This module implements a simple dictionary object, i.e. a list
- of string/string associations. This object is useful to store e.g.
- informations retrieved from a configuration file (ini files).
-*/
-/*--------------------------------------------------------------------------*/
-
-/*
- $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $
- $Author: ndevilla $
- $Date: 2007-11-23 21:37:00 $
- $Revision: 1.12 $
-*/
-
-#ifndef _DICTIONARY_H_
-#define _DICTIONARY_H_
-
-/*---------------------------------------------------------------------------
- Includes
- ---------------------------------------------------------------------------*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/*---------------------------------------------------------------------------
- New types
- ---------------------------------------------------------------------------*/
-
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Dictionary object
-
- This object contains a list of string/string associations. Each
- association is identified by a unique string key. Looking up values
- in the dictionary is speeded up by the use of a (hopefully collision-free)
- hash function.
- */
-/*-------------------------------------------------------------------------*/
-typedef struct _dictionary_ {
- int n ; /** Number of entries in dictionary */
- int size ; /** Storage size */
- char ** val ; /** List of string values */
- char ** key ; /** List of string keys */
- unsigned * hash ; /** List of hash values for keys */
-} dictionary ;
-
-
-/*---------------------------------------------------------------------------
- Function prototypes
- ---------------------------------------------------------------------------*/
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Compute the hash key for a string.
- @param key Character string to use for key.
- @return 1 unsigned int on at least 32 bits.
-
- This hash function has been taken from an Article in Dr Dobbs Journal.
- This is normally a collision-free function, distributing keys evenly.
- The key is stored anyway in the struct so that collision can be avoided
- by comparing the key itself in last resort.
- */
-/*--------------------------------------------------------------------------*/
-unsigned dictionary_hash(char * key);
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Create a new dictionary object.
- @param size Optional initial size of the dictionary.
- @return 1 newly allocated dictionary objet.
-
- This function allocates a new dictionary object of given size and returns
- it. If you do not know in advance (roughly) the number of entries in the
- dictionary, give size=0.
- */
-/*--------------------------------------------------------------------------*/
-dictionary * dictionary_new(int size);
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Delete a dictionary object
- @param d dictionary object to deallocate.
- @return void
-
- Deallocate a dictionary object and all memory associated to it.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_del(dictionary * vd);
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get a value from a dictionary.
- @param d dictionary object to search.
- @param key Key to look for in the dictionary.
- @param def Default value to return if key not found.
- @return 1 pointer to internally allocated character string.
-
- This function locates a key in a dictionary and returns a pointer to its
- value, or the passed 'def' pointer if no such key can be found in
- dictionary. The returned character pointer points to data internal to the
- dictionary object, you should not try to free it or modify it.
- */
-/*--------------------------------------------------------------------------*/
-char * dictionary_get(dictionary * d, char * key, char * def);
-
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Set a value in a dictionary.
- @param d dictionary object to modify.
- @param key Key to modify or add.
- @param val Value to add.
- @return int 0 if Ok, anything else otherwise
-
- If the given key is found in the dictionary, the associated value is
- replaced by the provided one. If the key cannot be found in the
- dictionary, it is added to it.
-
- It is Ok to provide a NULL value for val, but NULL values for the dictionary
- or the key are considered as errors: the function will return immediately
- in such a case.
-
- Notice that if you dictionary_set a variable to NULL, a call to
- dictionary_get will return a NULL value: the variable will be found, and
- its value (NULL) is returned. In other words, setting the variable
- content to NULL is equivalent to deleting the variable from the
- dictionary. It is not possible (in this implementation) to have a key in
- the dictionary without value.
-
- This function returns non-zero in case of failure.
- */
-/*--------------------------------------------------------------------------*/
-int dictionary_set(dictionary * vd, char * key, char * val);
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Delete a key in a dictionary
- @param d dictionary object to modify.
- @param key Key to remove.
- @return void
-
- This function deletes a key in a dictionary. Nothing is done if the
- key cannot be found.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_unset(dictionary * d, char * key);
-
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Dump a dictionary to an opened file pointer.
- @param d Dictionary to dump
- @param f Opened file pointer.
- @return void
-
- Dumps a dictionary onto an opened file pointer. Key pairs are printed out
- as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
- output file pointers.
- */
-/*--------------------------------------------------------------------------*/
-void dictionary_dump(dictionary * d, FILE * out);
-
-#endif
diff --git a/ubi-utils/src/eb_chain.c b/ubi-utils/src/eb_chain.c
new file mode 100644
index 0000000..4647cf4
--- /dev/null
+++ b/ubi-utils/src/eb_chain.c
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+/*
+ * Author: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ */
+
+/* see eb_chain.h */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mtd_swab.h>
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define COPY(dst, src) \
+ do { \
+ dst = malloc(sizeof(*dst)); \
+ if (dst == NULL) \
+ return -ENOMEM; \
+ memcpy(dst, src, sizeof(*dst)); \
+ } while (0)
+
+
+/**
+ * inserts an eb_info into the chain starting at head, then searching
+ * linearly for the correct position;
+ * new should contain valid vid and ec headers and the data_crc should
+ * already have been checked before insertion, otherwise the chain
+ * could be have un an undesired manner;
+ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0,
+ * if not, the code reached the last line and returned -EAGAIN,
+ * meaning there is a bug or a case not being handled here;
+ **/
+int
+eb_chain_insert(struct eb_info **head, struct eb_info *new)
+{
+ uint32_t vol, num, ver;
+ uint32_t new_vol, new_num, new_ver;
+ struct eb_info *prev, *cur, *hist, *ins;
+ struct eb_info **prev_ptr;
+
+ if ((head == NULL) || (new == NULL))
+ return 0;
+
+ if (*head == NULL) {
+ COPY(*head, new);
+ (*head)->next = NULL;
+ return 0;
+ }
+
+ new_vol = be32_to_cpu(new->vid.vol_id);
+ new_num = be32_to_cpu(new->vid.lnum);
+ new_ver = be32_to_cpu(new->vid.leb_ver);
+
+ /** TRAVERSE HORIZONTALY **/
+
+ cur = *head;
+ prev = NULL;
+
+ /* traverse until vol_id/lnum align */
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+ while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) {
+ /* insert new at end of chain */
+ if (cur->next == NULL) {
+ COPY(ins, new);
+ ins->next = NULL;
+ cur->next = ins;
+ return 0;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+ }
+
+ if (prev == NULL)
+ prev_ptr = head;
+ else
+ prev_ptr = &(prev->next);
+
+ /* insert new into the middle of chain */
+ if ((new_vol != vol) || (new_num != num)) {
+ COPY(ins, new);
+ ins->next = cur;
+ *prev_ptr = ins;
+ return 0;
+ }
+
+ /** TRAVERSE VERTICALY **/
+
+ hist = cur;
+ prev = NULL;
+
+ /* traverse until versions align */
+ ver = be32_to_cpu(cur->vid.leb_ver);
+ while (new_ver < ver) {
+ /* insert new at bottom of history */
+ if (hist->older == NULL) {
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = NULL;
+ hist->older = ins;
+ return 0;
+ }
+
+ prev = hist;
+ hist = hist->older;
+ ver = be32_to_cpu(hist->vid.leb_ver);
+ }
+
+ if (prev == NULL) {
+ /* replace active version */
+ COPY(ins, new);
+ ins->next = hist->next;
+ *prev_ptr = ins;
+
+ /* place cur in vertical histroy */
+ ins->older = hist;
+ hist->next = NULL;
+ return 0;
+ }
+
+ /* insert between versions, beneath active version */
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = prev->older;
+ prev->older = ins;
+ return 0;
+}
+
+
+/**
+ * sets the pointer at pos to the position of the first entry in the chain
+ * with of vol_id and, if given, with the same lnum as *lnum;
+ * if there is no entry in the chain, then *pos is NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+ struct eb_info **pos)
+{
+ uint32_t vol, num;
+ struct eb_info *cur;
+
+ if ((head == NULL) || (*head == NULL) || (pos == NULL))
+ return 0;
+
+ *pos = NULL;
+
+ cur = *head;
+ while (cur != NULL) {
+ vol = be32_to_cpu(cur->vid.vol_id);
+ num = be32_to_cpu(cur->vid.lnum);
+
+ if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) {
+ *pos = cur;
+ return 0;
+ }
+
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * prints to stream, the vol_id, lnum and leb_ver for each entry in the
+ * chain, starting at head;
+ * this is intended for debuging purposes;
+ * always returns 0;
+ *
+ * FIXME I do not like the double list traversion ...
+ **/
+int
+eb_chain_print(FILE* stream, struct eb_info *head)
+{
+ struct eb_info *cur;
+
+ if (stream == NULL)
+ stream = stdout;
+
+ if (head == NULL) {
+ fprintf(stream, "EMPTY\n");
+ return 0;
+ }
+ /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/
+ fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n");
+ cur = head;
+ while (cur != NULL) {
+ struct eb_info *hist;
+
+ fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ be32_to_cpu(cur->vid.leb_ver),
+ cur->ec_crc_ok ? "ok":"bad",
+ cur->vid_crc_ok ? "ok":"bad");
+ if (cur->vid.vol_type == UBI_VID_STATIC)
+ fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad");
+ else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign");
+ fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block,
+ cur->phys_addr, be32_to_cpu(cur->vid.data_size),
+ be64_to_cpu(cur->ec.ec));
+
+ hist = cur->older;
+ while (hist != NULL) {
+ fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+ be32_to_cpu(hist->vid.vol_id),
+ be32_to_cpu(hist->vid.lnum),
+ be32_to_cpu(hist->vid.leb_ver),
+ hist->ec_crc_ok ? "ok":"bad",
+ hist->vid_crc_ok ? "ok":"bad");
+ if (hist->vid.vol_type == UBI_VID_STATIC)
+ fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad");
+ else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign");
+ fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n",
+ hist->phys_block, hist->phys_addr,
+ be32_to_cpu(hist->vid.data_size),
+ be64_to_cpu(hist->ec.ec));
+
+ hist = hist->older;
+ }
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * frees the memory of the entire chain, starting at head;
+ * head will be NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_destroy(struct eb_info **head)
+{
+ if (head == NULL)
+ return 0;
+
+ while (*head != NULL) {
+ struct eb_info *cur;
+ struct eb_info *hist;
+
+ cur = *head;
+ *head = (*head)->next;
+
+ hist = cur->older;
+ while (hist != NULL) {
+ struct eb_info *temp;
+
+ temp = hist;
+ hist = hist->older;
+ free(temp);
+ }
+ free(cur);
+ }
+ return 0;
+}
+
diff --git a/ubi-utils/src/error.c b/ubi-utils/src/error.c
new file mode 100644
index 0000000..4aaedad
--- /dev/null
+++ b/ubi-utils/src/error.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <string.h>
+#include "error.h"
+
+#define MAXLINE 4096
+#define MAXWIDTH 80
+
+static FILE *logfp = NULL;
+
+static void err_doit(int, int, const char *, va_list);
+
+int
+read_procfile(FILE *fp_out, const char *procfile)
+{
+ FILE *fp;
+
+ if (!fp_out)
+ return -ENXIO;
+
+ fp = fopen(procfile, "r");
+ if (!fp)
+ return -ENOENT;
+
+ while(!feof(fp)) {
+ int c = fgetc(fp);
+
+ if (c == EOF)
+ return 0;
+
+ if (putc(c, fp_out) == EOF)
+ return -EIO;
+
+ if (ferror(fp))
+ return -EIO;
+ }
+ return fclose(fp);
+}
+
+void
+error_initlog(const char *logfile)
+{
+ if (!logfile)
+ return;
+
+ logfp = fopen(logfile, "a+");
+ read_procfile(logfp, "/proc/cpuinfo");
+}
+
+void
+info_msg(const char *fmt, ...)
+{
+ FILE* fpout;
+ char buf[MAXLINE + 1];
+ va_list ap;
+ int n;
+
+ fpout = stdout;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ n = strlen(buf);
+ strcat(buf, "\n");
+
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stdout)
+ fclose(fpout);
+
+ va_end(ap);
+ return;
+}
+
+void
+__err_ret(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_INFO, fmt, ap);
+ va_end(ap);
+ return;
+}
+
+void
+__err_sys(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+
+void
+__err_msg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_INFO, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+void
+__err_quit(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+void
+__err_dump(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ abort(); /* dump core and terminate */
+ exit(EXIT_FAILURE); /* shouldn't get here */
+}
+
+/**
+ * If a logfile is used we must not print on stderr and stdout
+ * anymore. Since pfilfash might be used in a server context, it is
+ * even dangerous to write to those descriptors.
+ */
+static void
+err_doit(int errnoflag, int level __attribute__((unused)),
+ const char *fmt, va_list ap)
+{
+ FILE* fpout;
+ int errno_save, n;
+ char buf[MAXLINE + 1];
+ fpout = stderr;
+
+ errno_save = errno; /* value caller might want printed */
+
+ vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
+
+ n = strlen(buf);
+
+ if (errnoflag)
+ snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
+ strcat(buf, "\n");
+
+ if (logfp) {
+ fputs(buf, logfp);
+ fflush(logfp);
+ return; /* exit when logging completes */
+ }
+
+ if (fpout == stderr) {
+ /* perform line wrap when outputting to stderr */
+ int word_len, post_len, chars;
+ char *buf_ptr;
+ const char *frmt = "%*s%n %n";
+
+ chars = 0;
+ buf_ptr = buf;
+ while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) {
+ int i;
+ char word[word_len + 1];
+ char post[post_len + 1];
+
+ strncpy(word, buf_ptr, word_len);
+ word[word_len] = '\0';
+ buf_ptr += word_len;
+ post_len -= word_len;
+
+ if (chars + word_len > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars = 0;
+ }
+ fputs(word, fpout);
+ chars += word_len;
+
+ if (post_len > 0) {
+ strncpy(post, buf_ptr, post_len);
+ post[post_len] = '\0';
+ buf_ptr += post_len;
+ }
+ for (i = 0; i < post_len; i++) {
+ int inc = 1, chars_new;
+
+ if (post[i] == '\t')
+ inc = 8;
+ if (post[i] == '\n') {
+ inc = 0;
+ chars_new = 0;
+ } else
+ chars_new = chars + inc;
+
+ if (chars_new > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars_new = inc;
+ }
+ fputc(post[i], fpout);
+ chars = chars_new;
+ }
+ }
+ }
+ else
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stderr)
+ fclose(fpout);
+
+ return;
+}
diff --git a/ubi-utils/src/error.h b/ubi-utils/src/error.h
new file mode 100644
index 0000000..05d8078
--- /dev/null
+++ b/ubi-utils/src/error.h
@@ -0,0 +1,84 @@
+#ifndef __ERROR_H__
+#define __ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+void error_initlog(const char *logfile);
+int read_procfile(FILE *fp_out, const char *procfile);
+
+void __err_ret(const char *fmt, ...);
+void __err_sys(const char *fmt, ...);
+void __err_msg(const char *fmt, ...);
+void __err_quit(const char *fmt, ...);
+void __err_dump(const char *fmt, ...);
+
+void info_msg(const char *fmt, ...);
+
+#ifdef DEBUG
+#define __loc_msg(str) do { \
+ __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \
+ str, __FILE__, __FUNCTION__, __LINE__); \
+} while (0)
+#else
+#define __loc_msg(str)
+#endif
+
+
+#define err_dump(fmt, ...) do { \
+ __loc_msg("ErrDump"); \
+ __err_dump(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_quit(fmt, ...) do { \
+ __loc_msg("ErrQuit"); \
+ __err_quit(fmt, ##__VA_ARGS__); \
+} while (0)
+
+
+#define err_ret(fmt, ...) do { \
+ __loc_msg("ErrRet"); \
+ __err_ret(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_sys(fmt, ...) do { \
+ __loc_msg("ErrSys"); \
+ __err_sys(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_msg(fmt, ...) do { \
+ __loc_msg("ErrMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define log_msg(fmt, ...) do { \
+ /* __loc_msg("LogMsg"); */ \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#ifdef DEBUG
+#define dbg_msg(fmt, ...) do { \
+ __loc_msg("DbgMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define dbg_msg(fmt, ...) do {} while (0)
+#endif
+
+#endif /* __ERROR_H__ */
diff --git a/ubi-utils/src/example_ubi.h b/ubi-utils/src/example_ubi.h
new file mode 100644
index 0000000..23c7b54
--- /dev/null
+++ b/ubi-utils/src/example_ubi.h
@@ -0,0 +1,28 @@
+#ifndef __EXAMPLE_UBI_H__
+#define __EXAMPLE_UBI_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ * Defaults for our cards.
+ */
+#define EXAMPLE_UBI_DEVICE 0
+#define EXAMPLE_BOOTENV_VOL_ID_1 4
+#define EXAMPLE_BOOTENV_VOL_ID_2 5
+
+#endif /* __EXAMPLE_UBI_H__ */
diff --git a/ubi-utils/src/hashmap.c b/ubi-utils/src/hashmap.c
new file mode 100644
index 0000000..3511d56
--- /dev/null
+++ b/ubi-utils/src/hashmap.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do { \
+ info_msg(fmt); \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+ char* key; /* key '0' term. str */
+ char* value; /* payload '0' term. str */
+
+ hashentry_t next;
+};
+
+struct hashmap {
+ size_t entries; /* current #entries */
+ size_t maxsize; /* no. of hash buckets */
+ hashentry_t* data; /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+ return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+ hashmap_t res;
+ res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+ if (res == NULL)
+ return NULL;
+
+ res->maxsize = DEFAULT_BUCKETS;
+ res->entries = 0;
+
+ res->data = (hashentry_t*)
+ calloc(1, res->maxsize * sizeof(struct hashentry));
+
+ if (res->data == NULL)
+ return NULL;
+
+ return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+ hashentry_t res;
+
+ res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+ if (res == NULL)
+ return NULL;
+
+ /* allocate key and value and copy them */
+ res->key = strdup(key);
+ if (res->key == NULL) {
+ free(res);
+ return NULL;
+ }
+
+ res->value = strdup(value);
+ if (res->value == NULL) {
+ free(res->key);
+ free(res);
+ return NULL;
+ }
+
+ res->next = NULL;
+
+ return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+ if (!is_empty(e)) {
+ if(e->key != NULL) {
+ free(e->key);
+ }
+ if(e->value != NULL)
+ free(e->value);
+ free(e);
+ }
+
+ return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ if(strcmp(l->key,key) == 0) {
+ lnext = l->next;
+ l = free_entry(l);
+ (*entries)--;
+ return lnext;
+ }
+
+ l->next = remove_entry(l->next, key, entries);
+
+ return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+ if (is_empty(l)) {
+ (*entries)++;
+ return e;
+ }
+
+ /* check for update */
+ if (strcmp(l->key, e->key) == 0) {
+ e->next = l->next;
+ l = free_entry(l);
+ return e;
+ }
+
+ l->next = insert_entry(l->next, e, entries);
+ return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ lnext = l->next;
+ free_entry(l);
+ (*entries)--;
+
+ return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+ if (is_empty(l))
+ return NULL;
+
+ if (strcmp(l->key, key) == 0)
+ return l->value;
+
+ return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+ if (is_empty(l)) {
+ printf("\n");
+ return;
+ }
+
+ printf("%s=%s", l->key, l->value);
+ if (!is_empty(l->next)) {
+ printf(",");
+ }
+
+ print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+ if (is_empty(l))
+ return;
+
+ a[*i] = l->key;
+ (*i)++;
+
+ keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+ uint32_t hash = 0;
+ uint32_t x = 0;
+ uint32_t i = 0;
+ size_t len = strlen(str);
+
+ for(i = 0; i < len; str++, i++) {
+ hash = (hash << 4) + (*str);
+ if((x = hash & 0xF0000000L) != 0) {
+ hash ^= (x >> 24);
+ hash &= ~x;
+ }
+ }
+
+ return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+ if (map == NULL)
+ return -EINVAL;
+
+ return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return NULL;
+
+ i = hash_str(key, map->maxsize);
+
+ return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+ uint32_t i;
+ hashentry_t entry;
+
+ if ((map == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ entry = new_entry(key, value);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ map->data[i] = insert_entry(map->data[i],
+ entry, &map->entries);
+
+ INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value);
+ return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+ return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+ if (map != NULL)
+ return map->entries;
+ else
+ return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+ size_t i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+ /* "children" first */
+ for(i = 0; i < map->maxsize; i++) {
+ map->data[i] = remove_all(map->data[i], &map->entries);
+ }
+ free(map->data);
+ free(map);
+
+ return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+ size_t i;
+ if (map == NULL)
+ return -EINVAL;
+
+ for(i = 0; i < map->maxsize; i++) {
+ if (map->data[i] != NULL) {
+ printf("[%zd]: ", i);
+ print_all(map->data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+ /* uses bubblesort */
+ size_t i, j;
+ const char* tmp;
+
+ if (size <= 0)
+ return a;
+
+ for (i = size - 1; i > 0; i--) {
+ for (j = 0; j < i; j++) {
+ if (strcmp(a[j], a[j+1]) > 0) {
+ tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+ const char** res;
+ size_t i, j;
+ *size = map->entries;
+
+ res = (const char**) malloc(*size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ j = 0;
+ for(i=0; i < map->maxsize; i++) {
+ keys_to_array(map->data[i], res, &j);
+ }
+
+ if (sort)
+ res = sort_key_vector(res, *size);
+
+ return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (strcmp(vec[i], key) == 0) /* found */
+ return 1;
+ }
+
+ return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size)
+{
+ const char** res;
+ size_t i, j;
+
+ *res_size = vec2_size;
+
+ res = (const char**) malloc(*res_size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ /* get all keys from vec2 which are not set in vec1 */
+ j = 0;
+ for (i = 0; i < vec2_size; i++) {
+ if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+ res[j++] = vec2[i];
+ }
+
+ *res_size = j;
+ return res;
+}
diff --git a/ubi-utils/src/hashmap.h b/ubi-utils/src/hashmap.h
new file mode 100644
index 0000000..1b13e95
--- /dev/null
+++ b/ubi-utils/src/hashmap.h
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/ubi-utils/src/libiniparser.c b/ubi-utils/src/libiniparser.c
deleted file mode 100644
index 3bea51e..0000000
--- a/ubi-utils/src/libiniparser.c
+++ /dev/null
@@ -1,646 +0,0 @@
-
-/*-------------------------------------------------------------------------*/
-/**
- @file iniparser.c
- @author N. Devillard
- @date Sep 2007
- @version 3.0
- @brief Parser for ini files.
-*/
-/*--------------------------------------------------------------------------*/
-/*
- $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $
- $Revision: 2.18 $
- $Date: 2008-01-03 18:35:39 $
-*/
-/*---------------------------- Includes ------------------------------------*/
-#include <ctype.h>
-#include <libiniparser.h>
-
-/*---------------------------- Defines -------------------------------------*/
-#define ASCIILINESZ (1024)
-#define INI_INVALID_KEY ((char*)-1)
-
-/*---------------------------------------------------------------------------
- Private to this module
- ---------------------------------------------------------------------------*/
-/**
- * This enum stores the status for each parsed line (internal use only).
- */
-typedef enum _line_status_ {
- LINE_UNPROCESSED,
- LINE_ERROR,
- LINE_EMPTY,
- LINE_COMMENT,
- LINE_SECTION,
- LINE_VALUE
-} line_status ;
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Convert a string to lowercase.
- @param s String to convert.
- @return ptr to statically allocated string.
-
- This function returns a pointer to a statically allocated string
- containing a lowercased version of the input string. Do not free
- or modify the returned string! Since the returned string is statically
- allocated, it will be modified at each function call (not re-entrant).
- */
-/*--------------------------------------------------------------------------*/
-static char * strlwc(const char * s)
-{
- static char l[ASCIILINESZ+1];
- int i ;
-
- if (s==NULL) return NULL ;
- memset(l, 0, ASCIILINESZ+1);
- i=0 ;
- while (s[i] && i<ASCIILINESZ) {
- l[i] = (char)tolower((int)s[i]);
- i++ ;
- }
- l[ASCIILINESZ]=(char)0;
- return l ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Remove blanks at the beginning and the end of a string.
- @param s String to parse.
- @return ptr to statically allocated string.
-
- This function returns a pointer to a statically allocated string,
- which is identical to the input string, except that all blank
- characters at the end and the beg. of the string have been removed.
- Do not free or modify the returned string! Since the returned string
- is statically allocated, it will be modified at each function call
- (not re-entrant).
- */
-/*--------------------------------------------------------------------------*/
-static char * strstrip(char * s)
-{
- static char l[ASCIILINESZ+1];
- char * last ;
-
- if (s==NULL) return NULL ;
-
- while (isspace((int)*s) && *s) s++;
- memset(l, 0, ASCIILINESZ+1);
- strcpy(l, s);
- last = l + strlen(l);
- while (last > l) {
- if (!isspace((int)*(last-1)))
- break ;
- last -- ;
- }
- *last = (char)0;
- return (char*)l ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get number of sections in a dictionary
- @param d Dictionary to examine
- @return int Number of sections found in dictionary
-
- This function returns the number of sections found in a dictionary.
- The test to recognize sections is done on the string stored in the
- dictionary: a section name is given as "section" whereas a key is
- stored as "section:key", thus the test looks for entries that do not
- contain a colon.
-
- This clearly fails in the case a section name contains a colon, but
- this should simply be avoided.
-
- This function returns -1 in case of error.
- */
-/*--------------------------------------------------------------------------*/
-int iniparser_getnsec(dictionary * d)
-{
- int i ;
- int nsec ;
-
- if (d==NULL) return -1 ;
- nsec=0 ;
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- if (strchr(d->key[i], ':')==NULL) {
- nsec ++ ;
- }
- }
- return nsec ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get name for section n in a dictionary.
- @param d Dictionary to examine
- @param n Section number (from 0 to nsec-1).
- @return Pointer to char string
-
- This function locates the n-th section in a dictionary and returns
- its name as a pointer to a string statically allocated inside the
- dictionary. Do not free or modify the returned string!
-
- This function returns NULL in case of error.
- */
-/*--------------------------------------------------------------------------*/
-char * iniparser_getsecname(dictionary * d, int n)
-{
- int i ;
- int foundsec ;
-
- if (d==NULL || n<0) return NULL ;
- foundsec=0 ;
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- if (strchr(d->key[i], ':')==NULL) {
- foundsec++ ;
- if (foundsec>n)
- break ;
- }
- }
- if (foundsec<=n) {
- return NULL ;
- }
- return d->key[i] ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Dump a dictionary to an opened file pointer.
- @param d Dictionary to dump.
- @param f Opened file pointer to dump to.
- @return void
-
- This function prints out the contents of a dictionary, one element by
- line, onto the provided file pointer. It is OK to specify @c stderr
- or @c stdout as output files. This function is meant for debugging
- purposes mostly.
- */
-/*--------------------------------------------------------------------------*/
-void iniparser_dump(dictionary * d, FILE * f)
-{
- int i ;
-
- if (d==NULL || f==NULL) return ;
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- if (d->val[i]!=NULL) {
- fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
- } else {
- fprintf(f, "[%s]=UNDEF\n", d->key[i]);
- }
- }
- return ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Save a dictionary to a loadable ini file
- @param d Dictionary to dump
- @param f Opened file pointer to dump to
- @return void
-
- This function dumps a given dictionary into a loadable ini file.
- It is Ok to specify @c stderr or @c stdout as output files.
- */
-/*--------------------------------------------------------------------------*/
-void iniparser_dump_ini(dictionary * d, FILE * f)
-{
- int i, j ;
- char keym[ASCIILINESZ+1];
- int nsec ;
- char * secname ;
- int seclen ;
-
- if (d==NULL || f==NULL) return ;
-
- nsec = iniparser_getnsec(d);
- if (nsec<1) {
- /* No section in file: dump all keys as they are */
- for (i=0 ; i<d->size ; i++) {
- if (d->key[i]==NULL)
- continue ;
- fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
- }
- return ;
- }
- for (i=0 ; i<nsec ; i++) {
- secname = iniparser_getsecname(d, i) ;
- seclen = (int)strlen(secname);
- fprintf(f, "\n[%s]\n", secname);
- sprintf(keym, "%s:", secname);
- for (j=0 ; j<d->size ; j++) {
- if (d->key[j]==NULL)
- continue ;
- if (!strncmp(d->key[j], keym, seclen+1)) {
- fprintf(f,
- "%-30s = %s\n",
- d->key[j]+seclen+1,
- d->val[j] ? d->val[j] : "");
- }
- }
- }
- fprintf(f, "\n");
- return ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get the string associated to a key
- @param d Dictionary to search
- @param key Key string to look for
- @param def Default value to return if key not found.
- @return pointer to statically allocated character string
-
- This function queries a dictionary for a key. A key as read from an
- ini file is given as "section:key". If the key cannot be found,
- the pointer passed as 'def' is returned.
- The returned char pointer is pointing to a string allocated in
- the dictionary, do not free or modify it.
- */
-/*--------------------------------------------------------------------------*/
-char * iniparser_getstring(dictionary * d, const char * key, char * def)
-{
- char * lc_key ;
- char * sval ;
-
- if (d==NULL || key==NULL)
- return def ;
-
- lc_key = strlwc(key);
- sval = dictionary_get(d, lc_key, def);
- return sval ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get the string associated to a key, convert to an int
- @param d Dictionary to search
- @param key Key string to look for
- @param notfound Value to return in case of error
- @return integer
-
- This function queries a dictionary for a key. A key as read from an
- ini file is given as "section:key". If the key cannot be found,
- the notfound value is returned.
-
- Supported values for integers include the usual C notation
- so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
- are supported. Examples:
-
- "42" -> 42
- "042" -> 34 (octal -> decimal)
- "0x42" -> 66 (hexa -> decimal)
-
- Warning: the conversion may overflow in various ways. Conversion is
- totally outsourced to strtol(), see the associated man page for overflow
- handling.
-
- Credits: Thanks to A. Becker for suggesting strtol()
- */
-/*--------------------------------------------------------------------------*/
-int iniparser_getint(dictionary * d, const char * key, int notfound)
-{
- char * str ;
-
- str = iniparser_getstring(d, key, INI_INVALID_KEY);
- if (str==INI_INVALID_KEY) return notfound ;
- return (int)strtol(str, NULL, 0);
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get the string associated to a key, convert to a double
- @param d Dictionary to search
- @param key Key string to look for
- @param notfound Value to return in case of error
- @return double
-
- This function queries a dictionary for a key. A key as read from an
- ini file is given as "section:key". If the key cannot be found,
- the notfound value is returned.
- */
-/*--------------------------------------------------------------------------*/
-double iniparser_getdouble(dictionary * d, char * key, double notfound)
-{
- char * str ;
-
- str = iniparser_getstring(d, key, INI_INVALID_KEY);
- if (str==INI_INVALID_KEY) return notfound ;
- return atof(str);
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Get the string associated to a key, convert to a boolean
- @param d Dictionary to search
- @param key Key string to look for
- @param notfound Value to return in case of error
- @return integer
-
- This function queries a dictionary for a key. A key as read from an
- ini file is given as "section:key". If the key cannot be found,
- the notfound value is returned.
-
- A true boolean is found if one of the following is matched:
-
- - A string starting with 'y'
- - A string starting with 'Y'
- - A string starting with 't'
- - A string starting with 'T'
- - A string starting with '1'
-
- A false boolean is found if one of the following is matched:
-
- - A string starting with 'n'
- - A string starting with 'N'
- - A string starting with 'f'
- - A string starting with 'F'
- - A string starting with '0'
-
- The notfound value returned if no boolean is identified, does not
- necessarily have to be 0 or 1.
- */
-/*--------------------------------------------------------------------------*/
-int iniparser_getboolean(dictionary * d, const char * key, int notfound)
-{
- char * c ;
- int ret ;
-
- c = iniparser_getstring(d, key, INI_INVALID_KEY);
- if (c==INI_INVALID_KEY) return notfound ;
- if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
- ret = 1 ;
- } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
- ret = 0 ;
- } else {
- ret = notfound ;
- }
- return ret;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Finds out if a given entry exists in a dictionary
- @param ini Dictionary to search
- @param entry Name of the entry to look for
- @return integer 1 if entry exists, 0 otherwise
-
- Finds out if a given entry exists in the dictionary. Since sections
- are stored as keys with NULL associated values, this is the only way
- of querying for the presence of sections in a dictionary.
- */
-/*--------------------------------------------------------------------------*/
-int iniparser_find_entry(
- dictionary * ini,
- char * entry
-)
-{
- int found=0 ;
- if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
- found = 1 ;
- }
- return found ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Set an entry in a dictionary.
- @param ini Dictionary to modify.
- @param entry Entry to modify (entry name)
- @param val New value to associate to the entry.
- @return int 0 if Ok, -1 otherwise.
-
- If the given entry can be found in the dictionary, it is modified to
- contain the provided value. If it cannot be found, -1 is returned.
- It is Ok to set val to NULL.
- */
-/*--------------------------------------------------------------------------*/
-int iniparser_set(dictionary * ini, char * entry, char * val)
-{
- return dictionary_set(ini, strlwc(entry), val) ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Delete an entry in a dictionary
- @param ini Dictionary to modify
- @param entry Entry to delete (entry name)
- @return void
-
- If the given entry can be found, it is deleted from the dictionary.
- */
-/*--------------------------------------------------------------------------*/
-void iniparser_unset(dictionary * ini, char * entry)
-{
- dictionary_unset(ini, strlwc(entry));
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Load a single line from an INI file
- @param input_line Input line, may be concatenated multi-line input
- @param section Output space to store section
- @param key Output space to store key
- @param value Output space to store value
- @return line_status value
- */
-/*--------------------------------------------------------------------------*/
-static line_status iniparser_line(
- char * input_line,
- char * section,
- char * key,
- char * value)
-{
- line_status sta ;
- char line[ASCIILINESZ+1];
- int len ;
-
- strcpy(line, strstrip(input_line));
- len = (int)strlen(line);
-
- sta = LINE_UNPROCESSED ;
- if (len<1) {
- /* Empty line */
- sta = LINE_EMPTY ;
- } else if (line[0]=='#') {
- /* Comment line */
- sta = LINE_COMMENT ;
- } else if (line[0]=='[' && line[len-1]==']') {
- /* Section name */
- sscanf(line, "[%[^]]", section);
- strcpy(section, strstrip(section));
- strcpy(section, strlwc(section));
- sta = LINE_SECTION ;
- } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
- || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2
- || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
- /* Usual key=value, with or without comments */
- strcpy(key, strstrip(key));
- strcpy(key, strlwc(key));
- strcpy(value, strstrip(value));
- /*
- * sscanf cannot handle '' or "" as empty values
- * this is done here
- */
- if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
- value[0]=0 ;
- }
- sta = LINE_VALUE ;
- } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
- || sscanf(line, "%[^=] %[=]", key, value) == 2) {
- /*
- * Special cases:
- * key=
- * key=;
- * key=#
- */
- strcpy(key, strstrip(key));
- strcpy(key, strlwc(key));
- value[0]=0 ;
- sta = LINE_VALUE ;
- } else {
- /* Generate syntax error */
- sta = LINE_ERROR ;
- }
- return sta ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Parse an ini file and return an allocated dictionary object
- @param ininame Name of the ini file to read.
- @return Pointer to newly allocated dictionary
-
- This is the parser for ini files. This function is called, providing
- the name of the file to be read. It returns a dictionary object that
- should not be accessed directly, but through accessor functions
- instead.
-
- The returned dictionary must be freed using iniparser_freedict().
- */
-/*--------------------------------------------------------------------------*/
-dictionary * iniparser_load(const char * ininame)
-{
- FILE * in ;
-
- char line [ASCIILINESZ+1] ;
- char section [ASCIILINESZ+1] ;
- char key [ASCIILINESZ+1] ;
- char tmp [ASCIILINESZ+1] ;
- char val [ASCIILINESZ+1] ;
-
- int last=0 ;
- int len ;
- int lineno=0 ;
- int errs=0;
-
- dictionary * dict ;
-
- if ((in=fopen(ininame, "r"))==NULL) {
- fprintf(stderr, "iniparser: cannot open %s\n", ininame);
- return NULL ;
- }
-
- dict = dictionary_new(0) ;
- if (!dict) {
- fclose(in);
- return NULL ;
- }
-
- memset(line, 0, ASCIILINESZ);
- memset(section, 0, ASCIILINESZ);
- memset(key, 0, ASCIILINESZ);
- memset(val, 0, ASCIILINESZ);
- last=0 ;
-
- while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
- lineno++ ;
- len = (int)strlen(line)-1;
- /* Safety check against buffer overflows */
- if (line[len]!='\n') {
- fprintf(stderr,
- "iniparser: input line too long in %s (%d)\n",
- ininame,
- lineno);
- dictionary_del(dict);
- fclose(in);
- return NULL ;
- }
- /* Get rid of \n and spaces at end of line */
- while ((len>=0) &&
- ((line[len]=='\n') || (isspace(line[len])))) {
- line[len]=0 ;
- len-- ;
- }
- /* Detect multi-line */
- if (line[len]=='\\') {
- /* Multi-line value */
- last=len ;
- continue ;
- } else {
- last=0 ;
- }
- switch (iniparser_line(line, section, key, val)) {
- case LINE_EMPTY:
- case LINE_COMMENT:
- break ;
-
- case LINE_SECTION:
- errs = dictionary_set(dict, section, NULL);
- break ;
-
- case LINE_VALUE:
- sprintf(tmp, "%s:%s", section, key);
- errs = dictionary_set(dict, tmp, val) ;
- break ;
-
- case LINE_ERROR:
- fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
- ininame,
- lineno);
- fprintf(stderr, "-> %s\n", line);
- errs++ ;
- break;
-
- default:
- break ;
- }
- memset(line, 0, ASCIILINESZ);
- last=0;
- if (errs<0) {
- fprintf(stderr, "iniparser: memory allocation failure\n");
- break ;
- }
- }
- if (errs) {
- dictionary_del(dict);
- dict = NULL ;
- }
- fclose(in);
- return dict ;
-}
-
-/*-------------------------------------------------------------------------*/
-/**
- @brief Free all memory associated to an ini dictionary
- @param d Dictionary to free
- @return void
-
- Free all memory associated to an ini dictionary.
- It is mandatory to call this function before the dictionary object
- gets out of the current context.
- */
-/*--------------------------------------------------------------------------*/
-void iniparser_freedict(dictionary * d)
-{
- dictionary_del(d);
-}
-
-/* vim: set ts=4 et sw=4 tw=75 */
diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c
new file mode 100644
index 0000000..7e3d3b3
--- /dev/null
+++ b/ubi-utils/src/libpfiflash.c
@@ -0,0 +1,1325 @@
+/*
+ * Copyright 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: Oliver Lohmann <oliloh@de.ibm.com>
+ * Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <anrez@de.ibm.com>
+ */
+
+/* TODO Compare data before writing it. This implies that the volume
+ * parameters are compared first: size, alignment, name, type, ...,
+ * this is the same, compare the data. Volume deletion is deffered
+ * until the difference has been found out.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define __USE_GNU
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+
+#include <libubi.h>
+#include <pfiflash.h>
+
+#include <mtd/ubi-user.h> /* FIXME Is this ok here? */
+#include <mtd/mtd-user.h>
+
+#include "pfiflash_error.h"
+#include "ubimirror.h"
+#include "error.h"
+#include "reader.h"
+#include "example_ubi.h"
+#include "bootenv.h"
+
+/* ubi-header.h and crc32.h needed for CRC checking */
+#include <mtd/ubi-header.h> /* FIXME Is this ok here? */
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define COMPARE_BUFFER_SIZE 2048
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+static const char copyright [] ubi_unused =
+ "Copyright International Business Machines Corp., 2006, 2007";
+
+/* simply clear buffer, then write into front of it */
+#define EBUF(fmt...) \
+ snprintf(err_buf, err_buf_size, fmt);
+
+/* make a history of buffer and then prepend something in front */
+#define EBUF_PREPEND(fmt) \
+ do { \
+ int EBUF_HISTORY_LENGTH = strlen(err_buf); \
+ char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \
+ strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\
+ EBUF(fmt ": %s", EBUF_HISTORY); \
+ } while (0)
+
+/* An array of PDD function pointers indexed by the algorithm. */
+static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] =
+ {
+ &bootenv_pdd_keep,
+ &bootenv_pdd_merge,
+ &bootenv_pdd_overwrite
+ };
+
+typedef enum ubi_update_process_t {
+ UBI_REMOVE = 0,
+ UBI_WRITE,
+ UBI_COMPARE,
+} ubi_update_process_t;
+
+
+/**
+ * skip_raw_volumes - reads data from pfi to advance fp past raw block
+ * @pfi: fp to pfi data
+ * @pfi_raws: header information
+ *
+ * Error handling):
+ * when early EOF in pfi data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ **/
+static int
+skip_raw_volumes(FILE* pfi, list_t pfi_raws,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ void *i;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ rc = 0;
+ foreach(i, ptr, pfi_raws) {
+ size_t j;
+ pfi_raw_t raw;
+
+ raw = (pfi_raw_t)i;
+ for(j = 0; j < raw->data_size; j++) {
+ int c;
+
+ c = fgetc(pfi);
+ if (c == EOF)
+ rc = -PFIFLASH_ERR_EOF;
+ else if (ferror(pfi))
+ rc = -PFIFLASH_ERR_FIO;
+
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ err:
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ return rc;
+}
+
+
+/**
+ * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook
+ * @devno: UBI device number.
+ * @s: Current seqnum.
+ * @u: Information about the UBI volume from the PFI.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't create a volume
+ * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_mkvol(int devno, int s, pfi_ubi_t u,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, type;
+ char path[PATH_MAX];
+ libubi_t ulib;
+ struct ubi_mkvol_request req;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, "
+ "alig=%d, nlen=%d, name=%s",
+ u->ids[s], u->size, u->data_size, u->type, u->alignment,
+ strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ switch (u->type) {
+ case pfi_ubi_static:
+ type = UBI_STATIC_VOLUME; break;
+ case pfi_ubi_dynamic:
+ default:
+ type = UBI_DYNAMIC_VOLUME;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+ req.vol_id = u->ids[s];
+ req.alignment = u->alignment;
+ req.bytes = u->size;
+ req.vol_type = type;
+ req.name = u->names[s];
+
+ rc = ubi_mkvol(ulib, path, &req);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_MKVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ *
+ * If the volume does not exist, the function will return success.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't update (truncate) a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err
+ * when UBI system couldn't remove a volume
+ * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_rmvol(int devno, uint32_t id,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, fd;
+ char path[PATH_MAX];
+ libubi_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubirmvol id=%d", id);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ /* truncate whether it exist or not */
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ libubi_close(ulib);
+ return 0; /* not existent, return 0 */
+ }
+
+ rc = ubi_update_start(ulib, fd, 0);
+ close(fd);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err; /* if EBUSY than empty device, continue */
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+ rc = ubi_rmvol(ulib, path, id);
+ if (rc != 0) {
+#ifdef DEBUG
+ int rc_old = rc;
+ dbg_msg("Remove UBI volume %d returned with error: %d "
+ "errno=%d", id, rc_old, errno);
+#endif
+
+ rc = -PFIFLASH_ERR_UBI_RMVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+
+ /* TODO Define a ubi_rmvol return value which says
+ * sth like EUBI_NOSUCHDEV. In this case, a failed
+ * operation is acceptable. Everything else has to be
+ * classified as real error. But talk to Andreas Arnez
+ * before defining something odd...
+ */
+ /* if ((errno == EINVAL) || (errno == ENODEV))
+ return 0; */ /* currently it is EINVAL or ENODEV */
+
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * read_bootenv_volume - reads the current bootenv data from id into be_old
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ * @bootenv_old to hold old boot_env data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume to read
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't read bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ **/
+static int
+read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ FILE* fp_in;
+ char path[PATH_MAX];
+ libubi_t ulib;
+
+ rc = 0;
+ fp_in = NULL;
+ ulib = NULL;
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_in = fopen(path, "r");
+ if (!fp_in) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ log_msg("[ reading old bootenvs ...");
+
+ /* Save old bootenvs for reference */
+ rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ err:
+ if (fp_in)
+ fclose(fp_in);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @bootend_old old PDD data from machine
+ * @pdd_f function to handle PDD with
+ * @fp_in new pdd data contained in PFI
+ * @fp_in_size data size of new pdd data in PFI
+ * @pfi_crc crc value from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when bootenv can't be read
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ * when PDD handling function returns and error
+ * - passes rc and err_buf data
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when bootenv can't be resized
+ * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't write bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err
+ **/
+static int
+write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, warnings, fd_out;
+ uint32_t crc;
+ char path[PATH_MAX];
+ size_t update_size;
+ FILE *fp_out;
+ bootenv_t bootenv_new, bootenv_res;
+ libubi_t ulib;
+
+ rc = 0;
+ warnings = 0;
+ crc = 0;
+ update_size = 0;
+ fp_out = NULL;
+ bootenv_new = NULL;
+ bootenv_res = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in);
+
+ /* Workflow:
+ * 1. Apply PDD operation and get the size of the returning
+ * bootenv_res section. Without the correct size it wouldn't
+ * be possible to call UBI update vol.
+ * 2. Call UBI update vol
+ * 3. Get FILE* to vol dev
+ * 4. Write to FILE*
+ */
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_new);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'new'");
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'res'");
+ goto err;
+ }
+
+ rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling PDD");
+ goto err;
+ }
+ else if (warnings)
+ /* TODO do something with warnings */
+ dbg_msg("A warning in the PDD operation occured: %d",
+ warnings);
+
+ rc = bootenv_size(bootenv_res, &update_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_SIZE;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ fp_out = fdopen(fd_out, "r+");
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ rc = ubi_update_start(ulib, fd_out, update_size);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_WRITE;
+ EBUF(PFIFLASH_ERRSTR[-rc], devno, id);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+ if (bootenv_new != NULL)
+ bootenv_destroy(&bootenv_new);
+ if (bootenv_res != NULL)
+ bootenv_destroy(&bootenv_res);
+ if (fp_out)
+ fclose(fp_out);
+
+ return rc;
+}
+
+
+/**
+ * write_normal_volume - writes data from PFI file int to regular UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @update_size size of data stream
+ * @fp_in PFI data file pointer
+ * @pfi_crc CRC data from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when unexpected EOF is encountered
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ **/
+static int
+write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, fd_out;
+ uint32_t crc, crc32_table[256];
+ char path[PATH_MAX];
+ size_t bytes_left;
+ FILE* fp_out;
+ libubi_t ulib;
+
+ rc = 0;
+ crc = UBI_CRC32_INIT;
+ bytes_left = update_size;
+ fp_out = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p",
+ id, update_size, fp_in);
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ fp_out = fdopen(fd_out, "r+");
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+ rc = ubi_update_start(ulib, fd_out, update_size);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ init_crc32_table(crc32_table);
+ while (bytes_left) {
+ char buf[1024];
+ size_t to_rw = sizeof buf > bytes_left ?
+ bytes_left : sizeof buf;
+ if (fread(buf, 1, to_rw, fp_in) != to_rw) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ crc = clc_crc32(crc32_table, crc, buf, to_rw);
+ if (fwrite(buf, 1, to_rw, fp_out) != to_rw) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ bytes_left -= to_rw;
+ }
+
+ if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ err:
+ if (fp_out)
+ fclose(fp_out);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+ uint32_t data_size, pdd_func_t pdd_f, char *err_buf,
+ size_t err_buf_size)
+{
+ int rc, warnings = 0;
+ unsigned int i;
+ bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL;
+
+ rc = bootenv_create(&bootenv_pfi);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_read(fp_pfi, bootenv_pfi, data_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ rc = bootenv_create(&bootenv_flash);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ goto err;
+ }
+
+ rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ goto err;
+ }
+
+ rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res,
+ &warnings, err_buf, err_buf_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+ goto err;
+ }
+
+ rc = bootenv_compare(bootenv_flash, bootenv_res);
+ if (rc > 0) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ } else if (rc < 0) {
+ rc = -PFIFLASH_ERR_COMPARE;
+ goto err;
+ }
+
+ bootenv_destroy(&bootenv_flash);
+ bootenv_flash = NULL;
+ }
+
+err:
+ if (bootenv_pfi)
+ bootenv_destroy(&bootenv_pfi);
+ if (bootenv_res)
+ bootenv_destroy(&bootenv_res);
+ if (bootenv_flash)
+ bootenv_destroy(&bootenv_flash);
+
+ return rc;
+}
+
+static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+ uint32_t bytes_left)
+{
+ unsigned int i;
+ size_t read_bytes, rc = 0;
+ char buf_pfi[COMPARE_BUFFER_SIZE];
+ char *buf_flash[ids_size];
+
+ for (i = 0; i < ids_size; i++) {
+ buf_flash[i] = malloc(COMPARE_BUFFER_SIZE);
+ if (!buf_flash[i])
+ return -PFIFLASH_ERR_COMPARE;
+ }
+
+ while (bytes_left) {
+ if (bytes_left > COMPARE_BUFFER_SIZE)
+ read_bytes = COMPARE_BUFFER_SIZE;
+ else
+ read_bytes = bytes_left;
+
+ rc = fread(buf_pfi, 1, read_bytes, fp_pfi);
+ if (rc != read_bytes) {
+ rc = -PFIFLASH_ERR_COMPARE;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]);
+ if (rc != read_bytes) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ }
+
+ rc = memcmp(buf_pfi, buf_flash[i], read_bytes);
+ if (rc != 0) {
+ rc = -PFIFLASH_CMP_DIFF;
+ goto err;
+ }
+ }
+
+ bytes_left -= read_bytes;
+ }
+
+err:
+ for (i = 0; i < ids_size; i++)
+ free(buf_flash[i]);
+
+ return rc;
+}
+
+static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi,
+ pdd_func_t pdd_f, char *err_buf, size_t err_buf_size)
+{
+ int rc, is_bootenv = 0;
+ unsigned int i;
+ char path[PATH_MAX];
+ libubi_t ulib = NULL;
+ FILE *fp_flash[u->ids_size];
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ goto err;
+ }
+
+ for (i = 0; i < u->ids_size; i++) {
+ if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 ||
+ u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2)
+ is_bootenv = 1;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]);
+
+ fp_flash[i] = fopen(path, "r");
+ if (fp_flash[i] == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ goto err;
+ }
+ }
+
+ if (is_bootenv)
+ rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size,
+ u->data_size, pdd_f, err_buf, err_buf_size);
+ else
+ rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size);
+
+err:
+ if (rc < 0)
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+
+ for (i = 0; i < u->ids_size; i++)
+ fclose(fp_flash[i]);
+ if (ulib)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+static int
+erase_mtd_region(FILE* file_p, int start, int length)
+{
+ int rc, fd;
+ erase_info_t erase;
+ mtd_info_t mtdinfo;
+ loff_t offset = start;
+ loff_t end = offset + length;
+
+ fd = fileno(file_p);
+ if (fd < 0)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ rc = ioctl(fd, MEMGETINFO, &mtdinfo);
+ if (rc)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ /* check for bad blocks in case of NAND flash */
+ if (mtdinfo.type == MTD_NANDFLASH) {
+ while (offset < end) {
+ rc = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (rc > 0) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ offset += mtdinfo.erasesize;
+ }
+ }
+
+ erase.start = start;
+ erase.length = length;
+
+ rc = ioctl(fd, MEMERASE, &erase);
+ if (rc) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ return rc;
+}
+
+/**
+ * process_raw_volumes - writes the raw sections of the PFI data
+ * @pfi PFI data file pointer
+ * @pfi_raws list of PFI raw headers
+ * @rawdev device to use to write raw data
+ *
+ * Error handling:
+ * when early EOF in PFI data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when opening MTD device fails
+ * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err
+ * when closing MTD device fails
+ * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err
+ **/
+static int
+process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ char *pfi_data;
+ void *i;
+ uint32_t crc, crc32_table[256];
+ size_t j, k;
+ FILE* mtd = NULL;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ if (rawdev == NULL)
+ return 0;
+
+ rc = 0;
+
+ pfi_data = NULL;
+
+ log_msg("[ rawupdate dev=%s", rawdev);
+
+ crc = UBI_CRC32_INIT;
+ init_crc32_table(crc32_table);
+
+ /* most likely only one element in list, but just in case */
+ foreach(i, ptr, pfi_raws) {
+ pfi_raw_t r = (pfi_raw_t)i;
+
+ /* read in pfi data */
+ if (pfi_data != NULL)
+ free(pfi_data);
+ pfi_data = malloc(r->data_size * sizeof(char));
+ for (j = 0; j < r->data_size; j++) {
+ int c = fgetc(pfi);
+ if (c == EOF) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (ferror(pfi)) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ pfi_data[j] = (char)c;
+ }
+ crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size);
+
+ /* check crc */
+ if (crc != r->crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc);
+ goto err;
+ }
+
+ /* open device */
+ mtd = fopen(rawdev, "r+");
+ if (mtd == NULL) {
+ rc = -PFIFLASH_ERR_MTD_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+
+ for (j = 0; j < r->starts_size; j++) {
+ rc = erase_mtd_region(mtd, r->starts[j], r->data_size);
+ if (rc) {
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fseek(mtd, r->starts[j], SEEK_SET);
+ for (k = 0; k < r->data_size; k++) {
+ int c = fputc((int)pfi_data[k], mtd);
+ if (c == EOF) {
+ fclose(mtd);
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ if ((char)c != pfi_data[k]) {
+ fclose(mtd);
+ rc = -1;
+ goto err;
+ }
+ }
+ }
+ rc = fclose(mtd);
+ mtd = NULL;
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_MTD_CLOSE;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+ }
+
+ err:
+ if (mtd != NULL)
+ fclose(mtd);
+ if (pfi_data != NULL)
+ free(pfi_data);
+ return rc;
+}
+
+
+/**
+ * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest
+ * @devno UBI device number
+ * @pfi_ubis list of UBI header data
+ *
+ * Error handling:
+ * when UBI id is out of bounds
+ * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err
+ * when UBI volume can't be removed
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+static int
+erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES];
+ size_t i;
+ list_t ptr;
+ pfi_ubi_t u;
+
+ rc = 0;
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++)
+ ubi_volumes[i] = 1;
+
+ foreach(u, ptr, pfi_ubis) {
+ /* iterate over each vol_id */
+ for(i = 0; i < u->ids_size; i++) {
+ if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) {
+ rc = -PFIFLASH_ERR_UBI_VID_OOB;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]);
+ goto err;
+ }
+ /* remove from removal list */
+ ubi_volumes[u->ids[i]] = 0;
+ }
+ }
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) {
+ if (ubi_volumes[i]) {
+ rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("remove volume failed");
+ goto err;
+ }
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * process_ubi_volumes - delegate tasks regarding UBI volumes
+ * @pfi PFI data file pointer
+ * @seqnum sequence number
+ * @pfi_ubis list of UBI header data
+ * @bootenv_old storage for current system PDD
+ * @pdd_f function to handle PDD
+ * @ubi_update_process whether reading or writing
+ *
+ * Error handling:
+ * when and unknown ubi_update_process is given
+ * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err
+ * otherwise
+ * - passes rc and err_buf
+ **/
+static int
+process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis,
+ bootenv_t bootenv_old, pdd_func_t pdd_f,
+ ubi_update_process_t ubi_update_process,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ pfi_ubi_t u;
+ list_t ptr;
+
+ rc = 0;
+
+ foreach(u, ptr, pfi_ubis) {
+ int s = seqnum;
+
+ if (s > ((int)u->ids_size - 1))
+ s = 0; /* per default use the first */
+ u->curr_seqnum = s;
+
+ switch (ubi_update_process) {
+ case UBI_REMOVE:
+ /* TODO are all these "EXAMPLE" vars okay? */
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s], bootenv_old,
+ err_buf, err_buf_size);
+ /* it's okay if there is no bootenv
+ * we're going to write one */
+ if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) ||
+ (rc == -PFIFLASH_ERR_BOOTENV_READ))
+ rc = 0;
+ if (rc != 0)
+ goto err;
+ }
+
+ rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ break;
+ case UBI_WRITE:
+ rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("creating volume");
+ goto err;
+ }
+
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ bootenv_old, pdd_f,
+ pfi,
+ u->data_size,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("bootenv volume");
+ } else {
+ rc = write_normal_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ u->data_size, pfi,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("normal volume");
+ }
+ if (rc != 0)
+ goto err;
+
+ break;
+ case UBI_COMPARE:
+ rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("compare volume");
+ goto err;
+ }
+
+ break;
+ default:
+ rc = -PFIFLASH_ERR_UBI_UNKNOWN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * mirror_ubi_volumes - mirror redundant pairs of volumes
+ * @devno UBI device number
+ * @pfi_ubis list of PFI header data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ **/
+static int
+mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint32_t j;
+ list_t ptr;
+ pfi_ubi_t i;
+ libubi_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ mirror ...");
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ /**
+ * Execute all mirror operations on redundant groups.
+ * Create a volume within a redundant group if it does
+ * not exist already (this is a precondition of
+ * ubimirror).
+ */
+ foreach(i, ptr, pfi_ubis) {
+ for (j = 0; j < i->ids_size; j++) {
+ /* skip self-match */
+ if (i->ids[j] == i->ids[i->curr_seqnum])
+ continue;
+
+ rc = my_ubi_rmvol(devno, i->ids[j],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ rc = my_ubi_mkvol(devno, j, i,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ foreach(i, ptr, pfi_ubis) {
+ rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+
+
+ err:
+ if (ulib != NULL)
+ libubi_close(ulib);
+
+ return rc;
+}
+
+
+/**
+ * pfiflash_with_options - exposed func to flash memory with a PFI file
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @compare flag to compare
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ *
+ * Error handling:
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when PFI headers can't be read, or
+ * when fail to skip raw sections, or
+ * when error occurs while processing raw volumes, or
+ * when fail to erase unmapped UBI vols, or
+ * when error occurs while processing UBI volumes, or
+ * when error occurs while mirroring UBI volumes
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+int
+pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ bootenv_t bootenv;
+ pdd_func_t pdd_f;
+
+ if (pfi == NULL)
+ return -EINVAL;
+
+ rc = 0;
+ pdd_f = NULL;
+
+ /* If the user didnt specify a seqnum we start per default
+ * with the index 0 */
+ int curr_seqnum = seqnum < 0 ? 0 : seqnum;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+
+ rc = bootenv_create(&bootenv);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], "");
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("reading PFI header");
+ goto err;
+ }
+
+ if (rawdev == NULL || compare)
+ rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size);
+ else
+ rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf,
+ err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling raw section");
+ goto err;
+ }
+
+ if (complete && !compare) {
+ rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("deleting unmapped UBI volumes");
+ goto err;
+ }
+ }
+
+ if (((int)pdd_handling >= 0) &&
+ (pdd_handling < PDD_HANDLING_NUM))
+ pdd_f = pdd_funcs[pdd_handling];
+ else {
+ rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ if (!compare) {
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_REMOVE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("removing UBI volumes");
+ goto err;
+ }
+
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_WRITE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("writing UBI volumes");
+ goto err;
+ }
+
+ if (seqnum < 0) { /* mirror redundant pairs */
+ rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("mirroring UBI volumes");
+ goto err;
+ }
+ }
+ } else {
+ /* only compare volumes, don't alter the content */
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+ pdd_f, UBI_COMPARE, err_buf, err_buf_size);
+
+ if (rc == -PFIFLASH_CMP_DIFF)
+ /* update is necessary, return positive value */
+ rc = 1;
+
+ if (rc < 0) {
+ EBUF_PREPEND("comparing UBI volumes");
+ goto err;
+ }
+ }
+
+ err:
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ bootenv_destroy(&bootenv);
+ return rc;
+}
+
+
+/**
+ * pfiflash - passes to pfiflash_with_options
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ **/
+int
+pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size)
+{
+ return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling,
+ NULL, err_buf, err_buf_size);
+}
diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c
index b53f18c..a028fc6 100644
--- a/ubi-utils/src/libubi.c
+++ b/ubi-utils/src/libubi.c
@@ -27,480 +27,13 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
+#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <limits.h>
#include "libubi.h"
#include "libubi_int.h"
-/**
- * mkpath - compose full path from 2 given components.
- * @path: the first component
- * @name: the second component
- *
- * This function returns the resulting path in case of success and %NULL in
- * case of failure.
- */
-static char *mkpath(const char *path, const char *name)
-{
- char *n;
- int len1 = strlen(path);
- int len2 = strlen(name);
-
- n = malloc(len1 + len2 + 2);
- if (!n) {
- sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2);
- return NULL;
- }
-
- memcpy(n, path, len1);
- if (n[len1 - 1] != '/')
- n[len1++] = '/';
-
- memcpy(n + len1, name, len2 + 1);
- return n;
-}
-
-/**
- * read_positive_ll - read a positive 'long long' value from a file.
- * @file: the file to read from
- * @value: the result is stored here
- *
- * This function reads file @file and interprets its contents as a positive
- * 'long long' integer. If this is not true, it fails with %EINVAL error code.
- * Returns %0 in case of success and %-1 in case of failure.
- */
-static int read_positive_ll(const char *file, long long *value)
-{
- int fd, rd;
- char buf[50];
-
- fd = open(file, O_RDONLY);
- if (fd == -1)
- return -1;
-
- rd = read(fd, buf, 50);
- if (rd == -1) {
- sys_errmsg("cannot read \"%s\"", file);
- goto out_error;
- }
- if (rd == 50) {
- errmsg("contents of \"%s\" is too long", file);
- errno = EINVAL;
- goto out_error;
- }
-
- if (sscanf(buf, "%lld\n", value) != 1) {
- /* This must be a UBI bug */
- errmsg("cannot read integer from \"%s\"\n", file);
- errno = EINVAL;
- goto out_error;
- }
-
- if (*value < 0) {
- errmsg("negative value %lld in \"%s\"", *value, file);
- errno = EINVAL;
- goto out_error;
- }
-
- if (close(fd))
- return sys_errmsg("close failed on \"%s\"", file);
-
- return 0;
-
-out_error:
- close(fd);
- return -1;
-}
-
-/**
- * read_positive_int - read a positive 'int' value from a file.
- * @file: the file to read from
- * @value: the result is stored here
- *
- * This function is the same as 'read_positive_ll()', but it reads an 'int'
- * value, not 'long long'.
- */
-static int read_positive_int(const char *file, int *value)
-{
- long long res;
-
- if (read_positive_ll(file, &res))
- return -1;
-
- /* Make sure the value is not too big */
- if (res > INT_MAX) {
- errmsg("value %lld read from file \"%s\" is out of range",
- res, file);
- errno = EINVAL;
- return -1;
- }
-
- *value = res;
- return 0;
-}
-
-/**
- * read_data - read data from a file.
- * @file: the file to read from
- * @buf: the buffer to read to
- * @buf_len: buffer length
- *
- * This function returns number of read bytes in case of success and %-1 in
- * case of failure. Note, if the file contains more then @buf_len bytes of
- * date, this function fails with %EINVAL error code.
- */
-static int read_data(const char *file, void *buf, int buf_len)
-{
- int fd, rd, tmp, tmp1;
-
- fd = open(file, O_RDONLY);
- if (fd == -1)
- return -1;
-
- rd = read(fd, buf, buf_len);
- if (rd == -1) {
- sys_errmsg("cannot read \"%s\"", file);
- goto out_error;
- }
-
- /* Make sure all data is read */
- tmp1 = read(fd, &tmp, 1);
- if (tmp1 == 1) {
- sys_errmsg("cannot read \"%s\"", file);
- goto out_error;
- }
- if (tmp1) {
- errmsg("file \"%s\" contains too much data (> %d bytes)",
- file, buf_len);
- errno = EINVAL;
- goto out_error;
- }
-
- if (close(fd)) {
- sys_errmsg("close failed on \"%s\"", file);
- return -1;
- }
-
- return rd;
-
-out_error:
- close(fd);
- return -1;
-}
-
-/**
- * read_major - read major and minor numbers from a file.
- * @file: name of the file to read from
- * @major: major number is returned here
- * @minor: minor number is returned here
- *
- * This function returns % in case of succes, and %-1 in case of failure.
- */
-static int read_major(const char *file, int *major, int *minor)
-{
- int ret;
- char buf[50];
-
- ret = read_data(file, buf, 50);
- if (ret < 0)
- return ret;
-
- ret = sscanf(buf, "%d:%d\n", major, minor);
- if (ret != 2) {
- errno = EINVAL;
- return errmsg("\"%s\" does not have major:minor format", file);
- }
-
- if (*major < 0 || *minor < 0) {
- errno = EINVAL;
- return errmsg("bad major:minor %d:%d in \"%s\"",
- *major, *minor, file);
- }
-
- return 0;
-}
-
-/**
- * dev_read_int - read a positive 'int' value from an UBI device sysfs file.
- * @patt: file pattern to read from
- * @dev_num: UBI device number
- * @value: the result is stored here
- *
- * This function returns %0 in case of success and %-1 in case of failure.
- */
-static int dev_read_int(const char *patt, int dev_num, int *value)
-{
- char file[strlen(patt) + 50];
-
- sprintf(file, patt, dev_num);
- return read_positive_int(file, value);
-}
-
-/**
- * vol_read_int - read a positive 'int' value from an UBI volume sysfs file.
- * @patt: file pattern to read from
- * @dev_num: UBI device number
- * @vol_id: volume ID
- * @value: the result is stored here
- *
- * This function returns %0 in case of success and %-1 in case of failure.
- */
-static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
-{
- char file[strlen(patt) + 100];
-
- sprintf(file, patt, dev_num, vol_id);
- return read_positive_int(file, value);
-}
-
-/**
- * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file.
- * @patt: file pattern to read from
- * @dev_num: UBI device number
- * @value: the result is stored here
- *
- * This function returns %0 in case of success and %-1 in case of failure.
- */
-static int dev_read_ll(const char *patt, int dev_num, long long *value)
-{
- char file[strlen(patt) + 50];
-
- sprintf(file, patt, dev_num);
- return read_positive_ll(file, value);
-}
-
-/**
- * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file.
- * @patt: file pattern to read from
- * @dev_num: UBI device number
- * @vol_id: volume ID
- * @value: the result is stored here
- *
- * This function returns %0 in case of success and %-1 in case of failure.
- */
-static int vol_read_ll(const char *patt, int dev_num, int vol_id,
- long long *value)
-{
- char file[strlen(patt) + 100];
-
- sprintf(file, patt, dev_num, vol_id);
- return read_positive_ll(file, value);
-}
-
-/**
- * vol_read_data - read data from an UBI volume's sysfs file.
- * @patt: file pattern to read from
- * @dev_num: UBI device number
- * @vol_id: volume ID
- * @buf: buffer to read to
- * @buf_len: buffer length
- *
- * This function returns number of read bytes in case of success and %-1 in
- * case of failure.
- */
-static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
- int buf_len)
-{
- char file[strlen(patt) + 100];
-
- sprintf(file, patt, dev_num, vol_id);
- return read_data(file, buf, buf_len);
-}
-
-/**
- * dev_get_major - get major and minor numbers of an UBI device.
- * @lib: libubi descriptor
- * @dev_num: UBI device number
- * @major: major number is returned here
- * @minor: minor number is returned here
- *
- * This function returns zero in case of succes and %-1 in case of failure.
- */
-static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor)
-{
- char file[strlen(lib->dev_dev) + 50];
-
- sprintf(file, lib->dev_dev, dev_num);
- return read_major(file, major, minor);
-}
-
-/**
- * vol_get_major - get major and minor numbers of an UBI volume.
- * @lib: libubi descriptor
- * @dev_num: UBI device number
- * @vol_id: volume ID
- * @major: major number is returned here
- * @minor: minor number is returned here
- *
- * This function returns zero in case of succes and %-1 in case of failure.
- */
-static int vol_get_major(struct libubi *lib, int dev_num, int vol_id,
- int *major, int *minor)
-{
- char file[strlen(lib->vol_dev) + 100];
-
- sprintf(file, lib->vol_dev, dev_num, vol_id);
- return read_major(file, major, minor);
-}
-
-/**
- * vol_node2nums - find UBI device number and volume ID by volume device node
- * file.
- * @lib: UBI library descriptor
- * @node: UBI character device node name
- * @dev_num: UBI device number is returned here
- * @vol_id: volume ID is returned hers
- *
- * This function returns zero in case of succes and %-1 in case of failure.
- */
-static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num,
- int *vol_id)
-{
- struct stat st;
- struct ubi_info info;
- int i, fd, major, minor;
- char file[strlen(lib->ubi_vol) + 100];
-
- if (lstat(node, &st))
- return -1;
-
- if (!S_ISCHR(st.st_mode)) {
- errno = EINVAL;
- return errmsg("\"%s\" is not a character device", node);
- }
-
- major = major(st.st_rdev);
- minor = minor(st.st_rdev);
-
- if (minor == 0) {
- errno = EINVAL;
- return errmsg("\"%s\" is not a volume character device", node);
- }
-
- if (ubi_get_info((libubi_t *)lib, &info))
- return -1;
-
- for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
- int major1, minor1, ret;
-
- ret = dev_get_major(lib, i, &major1, &minor1);
- if (ret) {
- if (errno == ENOENT)
- continue;
- return -1;
- }
-
- if (major1 == major)
- break;
- }
-
- if (i > info.highest_dev_num) {
- errno = ENODEV;
- return -1;
- }
-
- /* Make sure this UBI volume exists */
- sprintf(file, lib->ubi_vol, i, minor - 1);
- fd = open(file, O_RDONLY);
- if (fd == -1) {
- errno = ENODEV;
- return -1;
- }
-
- *dev_num = i;
- *vol_id = minor - 1;
- errno = 0;
- return 0;
-}
-
-/**
- * dev_node2num - find UBI device number by its character device node.
- * @lib: UBI library descriptor
- * @node: UBI character device node name
- *
- * This function returns positive UBI device number in case of success and %-1
- * in case of failure.
- */
-static int dev_node2num(struct libubi *lib, const char *node, int *dev_num)
-{
- struct stat stat;
- struct ubi_info info;
- int i, major, minor;
-
- if (lstat(node, &stat))
- return -1;
-
- if (!S_ISCHR(stat.st_mode)) {
- errno = EINVAL;
- return errmsg("\"%s\" is not a character device", node);
- }
-
- major = major(stat.st_rdev);
- minor = minor(stat.st_rdev);
-
- if (minor != 0) {
- errno = EINVAL;
- return errmsg("\"%s\" is not an UBI character device", node);
- }
-
- if (ubi_get_info((libubi_t *)lib, &info))
- return -1;
-
- for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
- int major1, minor1, ret;
-
- ret = dev_get_major(lib, i, &major1, &minor1);
- if (ret) {
- if (errno == ENOENT)
- continue;
- return -1;
- }
-
- if (major1 == major) {
- if (minor1 != 0) {
- errmsg("UBI character device minor number is "
- "%d, but must be 0", minor1);
- errno = EINVAL;
- return -1;
- }
- errno = 0;
- *dev_num = i;
- return 0;
- }
- }
-
- errno = ENODEV;
- return -1;
-}
-
-static int mtd_num2ubi_dev(struct libubi *lib, int mtd_num, int *dev_num)
-{
- struct ubi_info info;
- int i, ret, mtd_num1;
-
- if (ubi_get_info((libubi_t *)lib, &info))
- return -1;
-
- for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
- ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1);
- if (ret) {
- if (errno == ENOENT)
- continue;
- return -1;
- }
-
- if (mtd_num1 == mtd_num) {
- errno = 0;
- *dev_num = i;
- return 0;
- }
- }
-
- errno = ENODEV;
- return -1;
-}
-
libubi_t libubi_open(void)
{
int fd, version;
@@ -513,130 +46,132 @@ libubi_t libubi_open(void)
/* TODO: this must be discovered instead */
lib->sysfs = strdup("/sys");
if (!lib->sysfs)
- goto out_error;
-
- lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL);
- if (!lib->sysfs_ctrl)
- goto out_error;
-
- lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV);
- if (!lib->ctrl_dev)
- goto out_error;
+ goto error;
lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI);
if (!lib->sysfs_ubi)
- goto out_error;
+ goto error;
/* Make sure UBI is present */
fd = open(lib->sysfs_ubi, O_RDONLY);
- if (fd == -1) {
- errmsg("cannot open \"%s\", UBI does not seem to exist in system",
- lib->sysfs_ubi);
- goto out_error;
- }
-
- if (close(fd)) {
- sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi);
- goto out_error;
- }
+ if (fd == -1)
+ goto error;
+ close(fd);
lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT);
if (!lib->ubi_dev)
- goto out_error;
+ goto error;
lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER);
if (!lib->ubi_version)
- goto out_error;
+ goto error;
lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV);
if (!lib->dev_dev)
- goto out_error;
+ goto error;
lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS);
if (!lib->dev_avail_ebs)
- goto out_error;
+ goto error;
lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS);
if (!lib->dev_total_ebs)
- goto out_error;
+ goto error;
lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT);
if (!lib->dev_bad_count)
- goto out_error;
+ goto error;
lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE);
if (!lib->dev_eb_size)
- goto out_error;
+ goto error;
lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC);
if (!lib->dev_max_ec)
- goto out_error;
+ goto error;
lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD);
if (!lib->dev_bad_rsvd)
- goto out_error;
+ goto error;
lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS);
if (!lib->dev_max_vols)
- goto out_error;
+ goto error;
lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE);
if (!lib->dev_min_io_size)
- goto out_error;
-
- lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM);
- if (!lib->dev_mtd_num)
- goto out_error;
+ goto error;
lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT);
if (!lib->ubi_vol)
- goto out_error;
+ goto error;
lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE);
if (!lib->vol_type)
- goto out_error;
+ goto error;
lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV);
if (!lib->vol_dev)
- goto out_error;
+ goto error;
lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT);
if (!lib->vol_alignment)
- goto out_error;
+ goto error;
lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES);
if (!lib->vol_data_bytes)
- goto out_error;
+ goto error;
lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS);
if (!lib->vol_rsvd_ebs)
- goto out_error;
+ goto error;
lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE);
if (!lib->vol_eb_size)
- goto out_error;
+ goto error;
lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED);
if (!lib->vol_corrupted)
- goto out_error;
+ goto error;
lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME);
if (!lib->vol_name)
- goto out_error;
+ goto error;
- if (read_positive_int(lib->ubi_version, &version))
- goto out_error;
+ if (read_int(lib->ubi_version, &version))
+ goto error;
if (version != LIBUBI_UBI_VERSION) {
fprintf(stderr, "LIBUBI: this library was made for UBI version "
"%d, but UBI version %d is detected\n",
LIBUBI_UBI_VERSION, version);
- goto out_error;
+ goto error;
}
return lib;
-out_error:
- libubi_close((libubi_t)lib);
+error:
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->sysfs);
+ free(lib);
return NULL;
}
@@ -653,7 +188,6 @@ void libubi_close(libubi_t desc)
free(lib->vol_dev);
free(lib->vol_type);
free(lib->ubi_vol);
- free(lib->dev_mtd_num);
free(lib->dev_min_io_size);
free(lib->dev_max_vols);
free(lib->dev_bad_rsvd);
@@ -666,141 +200,10 @@ void libubi_close(libubi_t desc)
free(lib->ubi_version);
free(lib->ubi_dev);
free(lib->sysfs_ubi);
- free(lib->ctrl_dev);
- free(lib->sysfs_ctrl);
free(lib->sysfs);
free(lib);
}
-int ubi_attach_mtd(libubi_t desc, const char *node,
- struct ubi_attach_request *req)
-{
- int fd, ret;
- struct ubi_attach_req r;
-
- memset(&r, sizeof(struct ubi_attach_req), '\0');
-
- desc = desc;
- r.ubi_num = req->dev_num;
- r.mtd_num = req->mtd_num;
- r.vid_hdr_offset = req->vid_hdr_offset;
-
- fd = open(node, O_RDONLY);
- if (fd == -1)
- return -1;
-
- ret = ioctl(fd, UBI_IOCATT, &r);
- close(fd);
- if (ret == -1)
- return -1;
-
- req->dev_num = r.ubi_num;
-
-#ifdef UDEV_SETTLE_HACK
- if (system("udevsettle") == -1)
- return -1;
- if (system("udevsettle") == -1)
- return -1;
-#endif
-
- return ret;
-}
-
-int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num)
-{
- int ret, ubi_dev;
-
- ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev);
- if (ret == -1)
- return ret;
-
- return ubi_remove_dev(desc, node, ubi_dev);
-}
-
-int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev)
-{
- int fd, ret;
-
- desc = desc;
-
- fd = open(node, O_RDONLY);
- if (fd == -1)
- return -1;
- ret = ioctl(fd, UBI_IOCDET, &ubi_dev);
- if (ret == -1)
- goto out_close;
-
-#ifdef UDEV_SETTLE_HACK
- if (system("udevsettle") == -1)
- return -1;
-#endif
-
-out_close:
- close(fd);
- return ret;
-}
-
-int ubi_node_type(libubi_t desc, const char *node)
-{
- struct stat st;
- struct ubi_info info;
- int i, fd, major, minor;
- struct libubi *lib = (struct libubi *)desc;
- char file[strlen(lib->ubi_vol) + 100];
-
- if (lstat(node, &st))
- return -1;
-
- if (!S_ISCHR(st.st_mode)) {
- errno = EINVAL;
- return -1;
- }
-
- major = major(st.st_rdev);
- minor = minor(st.st_rdev);
-
- if (ubi_get_info((libubi_t *)lib, &info))
- return -1;
-
- for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
- int major1, minor1, ret;
-
- ret = dev_get_major(lib, i, &major1, &minor1);
- if (ret) {
- if (errno == ENOENT)
- continue;
- return -1;
- }
-
- if (major1 == major)
- break;
- }
-
- if (i > info.highest_dev_num) {
- /*
- * The character device node does not correspond to any
- * existing UBI device or volume, but we do not want to return
- * any error number in this case, to indicate the fact that it
- * could be a UBI device/volume, but it doesn't.
- */
- errno = 0;
- return -1;
- }
-
- if (minor == 0)
- return 1;
-
- /* This is supposdely an UBI volume device node */
- sprintf(file, lib->ubi_vol, i, minor - 1);
- fd = open(file, O_RDONLY);
- if (fd == -1) {
- errno = 0;
- return -1;
- }
-
- return 2;
-}
-
int ubi_get_info(libubi_t desc, struct ubi_info *info)
{
DIR *sysfs_ubi;
@@ -809,42 +212,20 @@ int ubi_get_info(libubi_t desc, struct ubi_info *info)
memset(info, '\0', sizeof(struct ubi_info));
- if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) {
- /*
- * Older UBI versions did not have control device, so we do not
- * panic here for compatibility reasons. May be few years later
- * we could return -1 here, but for now just set major:minor to
- * -1.
- */
- info->ctrl_major = info->ctrl_minor = -1;
- }
-
/*
* We have to scan the UBI sysfs directory to identify how many UBI
* devices are present.
*/
sysfs_ubi = opendir(lib->sysfs_ubi);
if (!sysfs_ubi)
- return sys_errmsg("cannot open %s", lib->sysfs_ubi);
+ return -1;
info->lowest_dev_num = INT_MAX;
- while (1) {
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
int dev_num, ret;
- char tmp_buf[256];
-
- errno = 0;
- dirent = readdir(sysfs_ubi);
- if (!dirent)
- break;
-
- if (strlen(dirent->d_name) > 256) {
- errmsg("invalid entry in %s: \"%s\"",
- lib->sysfs_ubi, dirent->d_name);
- goto out_close;
- }
- ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s",
- &dev_num, tmp_buf);
+ ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num);
if (ret == 1) {
info->dev_count += 1;
if (dev_num > info->highest_dev_num)
@@ -854,23 +235,15 @@ int ubi_get_info(libubi_t desc, struct ubi_info *info)
}
}
- if (!dirent && errno) {
- sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
- goto out_close;
- }
-
- if (closedir(sysfs_ubi))
- return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
-
if (info->lowest_dev_num == INT_MAX)
info->lowest_dev_num = 0;
- if (read_positive_int(lib->ubi_version, &info->version))
- return -1;
+ if (read_int(lib->ubi_version, &info->version))
+ goto close;
- return 0;
+ return closedir(sysfs_ubi);
-out_close:
+close:
closedir(sysfs_ubi);
return -1;
}
@@ -881,8 +254,6 @@ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
struct ubi_mkvol_req r;
size_t n;
- memset(&r, sizeof(struct ubi_mkvol_req), '\0');
-
desc = desc;
r.vol_id = req->vol_id;
r.alignment = req->alignment;
@@ -901,17 +272,9 @@ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
return -1;
ret = ioctl(fd, UBI_IOCMKVOL, &r);
- if (ret == -1)
- goto out_close;
-
- req->vol_id = r.vol_id;
-
-#ifdef UDEV_SETTLE_HACK
- if (system("udevsettle") == -1)
- return -1;
-#endif
+ if (!ret)
+ req->vol_id = r.vol_id;
-out_close:
close(fd);
return ret;
}
@@ -926,15 +289,6 @@ int ubi_rmvol(libubi_t desc, const char *node, int vol_id)
return -1;
ret = ioctl(fd, UBI_IOCRMVOL, &vol_id);
- if (ret == -1)
- goto out_close;
-
-#ifdef UDEV_SETTLE_HACK
- if (system("udevsettle") == -1)
- return -1;
-#endif
-
-out_close:
close(fd);
return ret;
}
@@ -965,19 +319,16 @@ int ubi_update_start(libubi_t desc, int fd, long long bytes)
return 0;
}
-int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype)
+int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
{
- struct ubi_leb_change_req req;
-
- desc = desc;
- memset(&req, 0, sizeof(struct ubi_leb_change_req));
- req.lnum = lnum;
- req.bytes = bytes;
- req.dtype = dtype;
+ int dev_num;
+ struct libubi *lib = (struct libubi *)desc;
- if (ioctl(fd, UBI_IOCEBCH, &req))
+ dev_num = find_dev_num(lib, node);
+ if (dev_num == -1)
return -1;
- return 0;
+
+ return ubi_get_dev_info1(desc, dev_num, info);
}
int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
@@ -994,23 +345,11 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
return -1;
info->lowest_vol_num = INT_MAX;
-
- while (1) {
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
int vol_id, ret, devno;
- char tmp_buf[256];
-
- errno = 0;
- dirent = readdir(sysfs_ubi);
- if (!dirent)
- break;
- if (strlen(dirent->d_name) > 256) {
- errmsg("invalid entry in %s: \"%s\"",
- lib->sysfs_ubi, dirent->d_name);
- goto out_close;
- }
-
- ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf);
+ ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id);
if (ret == 2 && devno == dev_num) {
info->vol_count += 1;
if (vol_id > info->highest_vol_num)
@@ -1020,27 +359,18 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
}
}
- if (!dirent && errno) {
- sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
- goto out_close;
- }
-
- if (closedir(sysfs_ubi))
- return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
+ closedir(sysfs_ubi);
if (info->lowest_vol_num == INT_MAX)
info->lowest_vol_num = 0;
- if (dev_get_major(lib, dev_num, &info->major, &info->minor))
- return -1;
-
- if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs))
+ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs))
return -1;
- if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs))
+ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs))
return -1;
if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count))
return -1;
- if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size))
+ if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size))
return -1;
if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd))
return -1;
@@ -1051,25 +381,26 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size))
return -1;
- info->avail_bytes = info->avail_lebs * info->leb_size;
- info->total_bytes = info->total_lebs * info->leb_size;
+ info->avail_bytes = info->avail_ebs * info->eb_size;
+ info->total_bytes = info->total_ebs * info->eb_size;
return 0;
-
-out_close:
- closedir(sysfs_ubi);
- return -1;
}
-int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
+int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
{
- int dev_num;
+ int vol_id, dev_num;
struct libubi *lib = (struct libubi *)desc;
- if (dev_node2num(lib, node, &dev_num))
+ dev_num = find_dev_num_vol(lib, node);
+ if (dev_num == -1)
return -1;
- return ubi_get_dev_info1(desc, dev_num, info);
+ vol_id = find_vol_num(lib, dev_num, node);
+ if (vol_id == -1)
+ return -1;
+
+ return ubi_get_vol_info1(desc, dev_num, vol_id, info);
}
int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
@@ -1083,21 +414,16 @@ int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
info->dev_num = dev_num;
info->vol_id = vol_id;
- if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor))
- return -1;
- if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor))
- return -1;
-
- ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50);
+ ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50);
if (ret < 0)
return -1;
- if (strncmp(buf, "static\n", ret) == 0)
+ if (strncmp(&buf[0], "static\n", ret) == 0)
info->type = UBI_STATIC_VOLUME;
- else if (strncmp(buf, "dynamic\n", ret) == 0)
+ else if (strncmp(&buf[0], "dynamic\n", ret) == 0)
info->type = UBI_DYNAMIC_VOLUME;
else {
- errmsg("bad value at \"%s\"", buf);
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
errno = EINVAL;
return -1;
}
@@ -1110,17 +436,17 @@ int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
&info->data_bytes);
if (ret)
return -1;
- ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs);
+ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs);
if (ret)
return -1;
- ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size);
+ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size);
if (ret)
return -1;
ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id,
&info->corrupted);
if (ret)
return -1;
- info->rsvd_bytes = info->leb_size * info->rsvd_lebs;
+ info->rsvd_bytes = info->eb_size * info->rsvd_ebs;
ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name,
UBI_VOL_NAME_MAX + 2);
@@ -1131,13 +457,459 @@ int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
return 0;
}
-int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
+/**
+ * read_int - read an 'int' value from a file.
+ *
+ * @file the file to read from
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_int(const char *file, int *value)
{
- int vol_id, dev_num;
- struct libubi *lib = (struct libubi *)desc;
+ int fd, rd;
+ char buf[50];
- if (vol_node2nums(lib, node, &dev_num, &vol_id))
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
return -1;
- return ubi_get_vol_info1(desc, dev_num, vol_id, info);
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_int - read an 'int' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_int(const char *patt, int dev_num, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_ll - read a 'long long' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_ll(const char *patt, int dev_num, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_data - read data from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @buf buffer to read data to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * vol_read_int - read an 'int' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_data - read data from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @buf buffer to read to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * mkpath - compose full path from 2 given components.
+ *
+ * @path first component
+ * @name second component
+ *
+ * This function returns the resulting path in case of success and %NULL in
+ * case of failure.
+ */
+static char *mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n)
+ return NULL;
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+/**
+ * find_dev_num - find UBI device number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num(struct libubi *lib, const char *node)
+{
+ struct stat stat;
+ struct ubi_info info;
+ int i, major, minor;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+ minor = minor(stat.st_rdev);
+
+ if (minor != 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_dev_num_vol - find UBI device number by volume character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num_vol(struct libubi *lib, const char *node)
+{
+ struct stat stat;
+ struct ubi_info info;
+ int i, major;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+
+ if (minor(stat.st_rdev) == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_vol_num - find UBI volume number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @dev_num UBI device number
+ * @node UBI volume character device node name
+ *
+ * This function returns positive UBI volume number in case of success and %-1
+ * in case of failure.
+ */
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node)
+{
+ struct stat stat;
+ struct ubi_dev_info info;
+ int i, major, minor;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+ minor = minor(stat.st_rdev);
+
+ if (minor == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info))
+ return -1;
+
+ for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
}
diff --git a/ubi-utils/src/libubi_int.h b/ubi-utils/src/libubi_int.h
index dab3e62..e68b791 100644
--- a/ubi-utils/src/libubi_int.h
+++ b/ubi-utils/src/libubi_int.h
@@ -23,45 +23,20 @@
#ifndef __LIBUBI_INT_H__
#define __LIBUBI_INT_H__
-#include <string.h>
-#include <errno.h>
-
#ifdef __cplusplus
extern "C" {
#endif
-/* Error messages */
-#define errmsg(fmt, ...) ({ \
- fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \
- -1; \
-})
-
-/* System error messages */
-#define sys_errmsg(fmt, ...) ({ \
- int _err = errno; \
- fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \
- fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \
- -1; \
-})
-
/*
+ * UBI heavily makes use of the sysfs file system to interact with users-pace.
* The below are pre-define UBI file and directory names.
- *
- * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'.
- * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is
- * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y'
- * directories to '/sys/class/ubi/'. For now libubi assumes old layout.
*/
#define SYSFS_UBI "class/ubi"
-#define SYSFS_CTRL "class/misc/ubi_ctrl/"
-
-#define CTRL_DEV "dev"
-
-#define UBI_VER "version"
#define UBI_DEV_NAME_PATT "ubi%d"
-
+#define UBI_VER "version"
#define DEV_DEV "dev"
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
#define DEV_AVAIL_EBS "avail_eraseblocks"
#define DEV_TOTAL_EBS "total_eraseblocks"
#define DEV_BAD_COUNT "bad_peb_count"
@@ -70,9 +45,6 @@ extern "C" {
#define DEV_MAX_RSVD "reserved_for_bad"
#define DEV_MAX_VOLS "max_vol_count"
#define DEV_MIN_IO_SIZE "min_io_size"
-#define DEV_MTD_NUM "mtd_num"
-
-#define UBI_VOL_NAME_PATT "ubi%d_%d"
#define VOL_TYPE "type"
#define VOL_DEV "dev"
#define VOL_ALIGNMENT "alignment"
@@ -84,37 +56,34 @@ extern "C" {
/**
* libubi - UBI library description data structure.
- * @sysfs: sysfs file system path
- * @sysfs_ctrl: UBI control device directory in sysfs
- * @ctrl_dev: UBI control device major/minor numbers sysfs file
- * @sysfs_ubi: UBI directory in sysfs
- * @ubi_dev: UBI device sysfs directory pattern
- * @ubi_version: UBI version file sysfs path
- * @dev_dev: UBI device major/minor numbers file pattern
- * @dev_avail_ebs: count of available eraseblocks sysfs path pattern
- * @dev_total_ebs: total eraseblocks count sysfs path pattern
- * @dev_bad_count: count of bad eraseblocks sysfs path pattern
- * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern
- * @dev_max_ec: maximum erase counter sysfs path pattern
- * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks
- * handling
- * @dev_max_vols: maximum volumes number count sysfs path pattern
- * @dev_min_io_size: minimum I/O unit size sysfs path pattern
- * @ubi_vol: UBI volume sysfs directory pattern
- * @vol_type: volume type sysfs path pattern
- * @vol_dev: volume major/minor numbers file pattern
- * @vol_alignment: volume alignment sysfs path pattern
- * @vol_data_bytes: volume data size sysfs path pattern
- * @vol_rsvd_ebs: volume reserved size sysfs path pattern
- * @vol_eb_size: volume eraseblock size sysfs path pattern
- * @vol_corrupted: volume corruption flag sysfs path pattern
- * @vol_name: volume name sysfs path pattern
+ *
+ * @sysfs sysfs file system path
+ * @sysfs_ubi UBI directory in sysfs
+ * @ubi_dev UBI device sysfs directory pattern
+ * @ubi_version UBI version file sysfs path
+ * @dev_dev UBI device's major/minor numbers file pattern
+ * @dev_avail_ebs count of available eraseblocks sysfs path pattern
+ * @dev_total_ebs total eraseblocks count sysfs path pattern
+ * @dev_bad_count count of bad eraseblocks sysfs path pattern
+ * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern
+ * @dev_max_ec maximum erase counter sysfs path pattern
+ * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks
+ * handling
+ * @dev_max_vols maximum volumes number count sysfs path pattern
+ * @dev_min_io_size minimum I/O unit size sysfs path pattern
+ * @ubi_vol UBI volume sysfs directory pattern
+ * @vol_type volume type sysfs path pattern
+ * @vol_dev volume's major/minor numbers file pattern
+ * @vol_alignment volume alignment sysfs path pattern
+ * @vol_data_bytes volume data size sysfs path pattern
+ * @vol_rsvd_ebs volume reserved size sysfs path pattern
+ * @vol_eb_size volume eraseblock size sysfs path pattern
+ * @vol_corrupted volume corruption flag sysfs path pattern
+ * @vol_name volume name sysfs path pattern
*/
struct libubi
{
char *sysfs;
- char *sysfs_ctrl;
- char *ctrl_dev;
char *sysfs_ubi;
char *ubi_dev;
char *ubi_version;
@@ -127,7 +96,6 @@ struct libubi
char *dev_bad_rsvd;
char *dev_max_vols;
char *dev_min_io_size;
- char *dev_mtd_num;
char *ubi_vol;
char *vol_type;
char *vol_dev;
@@ -140,6 +108,20 @@ struct libubi
char *vol_max_count;
};
+static int read_int(const char *file, int *value);
+static int dev_read_int(const char *patt, int dev_num, int *value);
+static int dev_read_ll(const char *patt, int dev_num, long long *value);
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len);
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value);
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value);
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len);
+static char *mkpath(const char *path, const char *name);
+static int find_dev_num(struct libubi *lib, const char *node);
+static int find_dev_num_vol(struct libubi *lib, const char *node);
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node);
+
#ifdef __cplusplus
}
#endif
diff --git a/ubi-utils/src/libubigen.c b/ubi-utils/src/libubigen.c
index 8d71fde..1fce3f9 100644
--- a/ubi-utils/src/libubigen.c
+++ b/ubi-utils/src/libubigen.c
@@ -1,6 +1,5 @@
/*
* Copyright (c) International Business Machines Corp., 2006
- * Copyright (C) 2008 Nokia Corporation
*
* 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
@@ -15,307 +14,474 @@
* 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.
- */
-
-/*
- * Generating UBI images.
*
- * Authors: Oliver Lohmann
- * Artem Bityutskiy
+ * Author: Oliver Lohmann
+ *
+ * Add UBI headers to binary data.
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
-#include <unistd.h>
+#include <errno.h>
#include <string.h>
-
#include <mtd/ubi-header.h>
-#include <libubigen.h>
+#include <mtd_swab.h>
+
+#include "config.h"
+#include "ubigen.h"
#include "crc32.h"
-#include "common.h"
-
-#define PROGRAM_NAME "libubigen"
-
-/**
- * ubigen_info_init - initialize libubigen.
- * @ui: libubigen information
- * @peb_size: flash physical eraseblock size
- * @min_io_size: flash minimum input/output unit size
- * @subpage_size: flash sub-page, if present (has to be equivalent to
- * @min_io_size if does not exist)
- * @vid_hdr_offs: offset of the VID header
- * @ubi_ver: UBI version
- * @ec: initial erase counter
- */
-void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
- int subpage_size, int vid_hdr_offs, int ubi_ver,
- long long ec)
+
+#define UBI_NAME_SIZE 256
+#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+
+struct ubi_info {
+ struct ubi_vid_hdr* v; /* Volume ID header */
+ struct ubi_ec_hdr* ec; /* Erase count header */
+
+ FILE* fp_in; /* Input Stream */
+ FILE* fp_out; /* Output stream */
+
+ size_t eb_size; /* Physical EB size in bytes */
+ size_t leb_size; /* Size of a logical EB in a physical EB */
+ size_t leb_total; /* Total input size in logical EB */
+ size_t alignment; /* Block alignment */
+ size_t data_pad; /* Size of padding in each physical EB */
+
+ size_t bytes_total; /* Total input size in bytes */
+ size_t bytes_read; /* Nymber of read bytes (total) */
+
+ uint32_t blks_written; /* Number of written logical EB */
+
+ uint8_t* buf; /* Allocated buffer */
+ uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */
+ uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */
+ uint8_t* ptr_data; /* Pointer to data region in buf */
+};
+
+
+static uint32_t
+byte_to_blk(uint64_t byte, uint32_t eb_size)
{
- if (!vid_hdr_offs)
- vid_hdr_offs = subpage_size;
-
- ui->peb_size = peb_size;
- ui->min_io_size = min_io_size;
- ui->vid_hdr_offs = vid_hdr_offs;
- ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1;
- ui->data_offs /= min_io_size;
- ui->data_offs *= min_io_size;
- ui->leb_size = peb_size - ui->data_offs;
- ui->ubi_ver = ubi_ver;
- ui->ec = ec;
-
- ui->vtbl_size = ui->leb_size;
- if (ui->vtbl_size > UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE)
- ui->vtbl_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
- ui->max_volumes = ui->vtbl_size / UBI_VTBL_RECORD_SIZE;
+ return (byte % eb_size) == 0
+ ? (byte / eb_size)
+ : (byte / eb_size) + 1;
}
-/**
- * ubigen_create_empty_vtbl - creates empty volume table.
- *
- * This function creates an empty volume table and returns a pointer to it in
- * case of success and %NULL in case of failure. The returned object has to be
- * freed with 'free()' call.
- */
-struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui)
+static int
+validate_ubi_info(ubi_info_t u)
{
- struct ubi_vtbl_record *vtbl;
- int i;
-
- vtbl = calloc(1, ui->vtbl_size);
- if (!vtbl) {
- errmsg("cannot allocate %d bytes of memory", ui->vtbl_size);
- return NULL;
+ if ((u->v->vol_type != UBI_VID_DYNAMIC) &&
+ (u->v->vol_type != UBI_VID_STATIC)) {
+ return EUBIGEN_INVALID_TYPE;
}
- for (i = 0; i < UBI_MAX_VOLUMES; i++) {
- uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i],
- UBI_VTBL_RECORD_SIZE_CRC);
- vtbl[i].crc = cpu_to_be32(crc);
+ if (be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) {
+ return EUBIGEN_INVALID_HDR_OFFSET;
}
- return vtbl;
+ return 0;
}
-/**
- * ubigen_add_volume - add a volume to the volume table.
- * @ui: libubigen information
- * @vi: volume information
- * @vtbl: volume table to add to
- *
- * This function adds volume described by input parameters to the volume table
- * @vtbl.
- */
-int ubigen_add_volume(const struct ubigen_info *ui,
- const struct ubigen_vol_info *vi,
- struct ubi_vtbl_record *vtbl)
+static int
+skip_blks(ubi_info_t u, uint32_t blks)
{
- struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id];
- uint32_t tmp;
-
- if (vi->id >= ui->max_volumes)
- return errmsg("too high volume id %d, max. volumes is %d",
- vi->id, ui->max_volumes);
-
- if (vi->alignment >= ui->leb_size)
- return errmsg("too large alignment %d, max is %d (LEB size)",
- vi->alignment, ui->leb_size);
-
- memset(vtbl_rec, '\0', sizeof(struct ubi_vtbl_record));
- tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size;
- vtbl_rec->reserved_pebs = cpu_to_be32(tmp);
- vtbl_rec->alignment = cpu_to_be32(vi->alignment);
- vtbl_rec->vol_type = vi->type;
- tmp = ui->leb_size % vi->alignment;
- vtbl_rec->data_pad = cpu_to_be32(tmp);
- vtbl_rec->flags = vi->flags;
-
- memcpy(vtbl_rec->name, vi->name, vi->name_len);
- vtbl_rec->name[vi->name_len] = '\0';
- vtbl_rec->name_len = cpu_to_be16(vi->name_len);
-
- tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC);
- vtbl_rec->crc = cpu_to_be32(tmp);
+ uint32_t i;
+ size_t read = 0, to_read = 0;
+
+ /* Step to a maximum of leb_total - 1 to keep the
+ restrictions. */
+ for (i = 0; i < MIN(blks, u->leb_total-1); i++) {
+ /* Read in data */
+ to_read = MIN(u->leb_size,
+ (u->bytes_total - u->bytes_read));
+ read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (read != to_read) {
+ return -EIO;
+ }
+ u->bytes_read += read;
+ u->blks_written++;
+ }
+
return 0;
}
-/**
- * init_ec_hdr - initialize EC header.
- * @ui: libubigen information
- * @hdr: the EC header to initialize
- */
-static void init_ec_hdr(const struct ubigen_info *ui,
- struct ubi_ec_hdr *hdr)
+static void
+clear_buf(ubi_info_t u)
{
- uint32_t crc;
+ memset(u->buf, 0xff, u->eb_size);
+}
- memset(hdr, '\0', sizeof(struct ubi_ec_hdr));
+static void
+write_ec_hdr(ubi_info_t u)
+{
+ memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE);
+}
- hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC);
- hdr->version = ui->ubi_ver;
- hdr->ec = cpu_to_be64(ui->ec);
- hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs);
+static int
+fill_data_buffer_from_file(ubi_info_t u, size_t* read)
+{
+ size_t to_read = 0;
- hdr->data_offset = cpu_to_be32(ui->data_offs);
+ if (u-> fp_in == NULL)
+ return -EIO;
- crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC);
- hdr->hdr_crc = cpu_to_be32(crc);
+ to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read));
+ *read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (*read != to_read) {
+ return -EIO;
+ }
+ return 0;
}
-/**
- * init_vid_hdr - initialize VID header.
- * @ui: libubigen information
- * @vi: volume information
- * @hdr: the VID header to initialize
- * @lnum: logical eraseblock number
- * @data: the contents of the LEB (static volumes only)
- * @data_size: amount of data in this LEB (static volumes only)
- *
- * Note, @used_ebs, @data and @data_size are ignored in case of dynamic
- * volumes.
- */
-static void init_vid_hdr(const struct ubigen_info *ui,
- const struct ubigen_vol_info *vi,
- struct ubi_vid_hdr *hdr, int lnum,
- const void *data, int data_size)
+static void
+add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action)
{
- uint32_t crc;
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->ptr_data, data_size);
- memset(hdr, '\0', sizeof(struct ubi_vid_hdr));
-
- hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
- hdr->version = ui->ubi_ver;
- hdr->vol_type = vi->type;
- hdr->vol_id = cpu_to_be32(vi->id);
- hdr->lnum = cpu_to_be32(lnum);
- hdr->data_pad = cpu_to_be32(vi->data_pad);
- hdr->compat = vi->compat;
-
- if (vi->type == UBI_VID_STATIC) {
- hdr->data_size = cpu_to_be32(data_size);
- hdr->used_ebs = cpu_to_be32(vi->used_ebs);
- crc = crc32(UBI_CRC32_INIT, data, data_size);
- hdr->data_crc = cpu_to_be32(crc);
+ u->v->data_size = cpu_to_be32(data_size);
+ u->v->data_crc = cpu_to_be32(crc);
+
+ if (action & BROKEN_DATA_CRC) {
+ u->v->data_crc =
+ cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1);
+ }
+ if (action & BROKEN_DATA_SIZE) {
+ u->v->data_size =
+ cpu_to_be32(be32_to_cpu(u->v->data_size) + 1);
}
+}
- crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC);
- hdr->hdr_crc = cpu_to_be32(crc);
+static void
+write_vid_hdr(ubi_info_t u, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->v, UBI_VID_HDR_SIZE_CRC);
+ /* Write VID header */
+ u->v->hdr_crc = cpu_to_be32(crc);
+ if (action & BROKEN_HDR_CRC) {
+ u->v->hdr_crc = cpu_to_be32(be32_to_cpu(u->v->hdr_crc) + 1);
+ }
+ memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE);
}
-/**
- * ubigen_write_volume - write UBI volume.
- * @ui: libubigen information
- * @vi: volume information
- * @bytes: volume size in bytes
- * @in: input file descriptor (has to be properly seeked)
- * @out: output file descriptor
- *
- * This function reads the contents of the volume from the input file @in and
- * writes the UBI volume to the output file @out. Returns zero on success and
- * %-1 on failure.
- */
-int ubigen_write_volume(const struct ubigen_info *ui,
- const struct ubigen_vol_info *vi,
- long long bytes, FILE *in, FILE *out)
+static int
+write_to_output_stream(ubi_info_t u)
{
- int len = vi->usable_leb_size, rd, lnum = 0;
- char inbuf[ui->leb_size], outbuf[ui->peb_size];
+ size_t written;
- if (vi->id >= ui->max_volumes)
- return errmsg("too high volume id %d, max. volumes is %d",
- vi->id, ui->max_volumes);
+ written = fwrite(u->buf, 1, u->eb_size, u->fp_out);
+ if (written != u->eb_size) {
+ return -EIO;
+ }
+ return 0;
+}
- if (vi->alignment >= ui->leb_size)
- return errmsg("too large alignment %d, max is %d (LEB size)",
- vi->alignment, ui->leb_size);
+int
+ubigen_write_leb(ubi_info_t u, ubigen_action_t action)
+{
+ int rc = 0;
+ size_t read = 0;
- memset(outbuf, 0xFF, ui->data_offs);
- init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf);
+ clear_buf(u);
+ write_ec_hdr(u);
- while (bytes) {
- int l;
- struct ubi_vid_hdr *vid_hdr;
+ rc = fill_data_buffer_from_file(u, &read);
+ if (rc != 0)
+ return rc;
- if (bytes < len)
- len = bytes;
- bytes -= len;
+ if (u->v->vol_type == UBI_VID_STATIC) {
+ add_static_info(u, read, action);
+ }
- l = len;
- do {
- rd = fread(inbuf + len - l, 1, l, in);
- if (rd == 0) {
- if (ferror(in))
- return errmsg("cannot read %d bytes from the input file", l);
- else
- return errmsg("not enough data in the input file");
- }
+ u->v->lnum = cpu_to_be32(u->blks_written);
- l -= rd;
- } while (l);
+ if (action & MARK_AS_UPDATE) {
+ u->v->copy_flag = (u->v->copy_flag)++;
+ }
- vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]);
- init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len);
+ write_vid_hdr(u, action);
+ rc = write_to_output_stream(u);
+ if (rc != 0)
+ return rc;
- memcpy(outbuf + ui->data_offs, inbuf, len);
- memset(outbuf + ui->data_offs + len, 0xFF,
- ui->peb_size - ui->data_offs - len);
+ /* Update current handle */
+ u->bytes_read += read;
+ u->blks_written++;
+ return 0;
+}
- if (fwrite(outbuf, 1, ui->peb_size, out) != ui->peb_size)
- return errmsg("cannot write %d bytes from the output file", l);
+int
+ubigen_write_complete(ubi_info_t u)
+{
+ size_t i;
+ int rc = 0;
- lnum += 1;
+ for (i = 0; i < u->leb_total; i++) {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ if (rc != 0)
+ return rc;
}
return 0;
}
-/**
- * ubigen_write_layout_vol - write UBI layout volume
- * @ui: libubigen information
- * @vtbl: volume table
- * @out: output file stream
- *
- * This function creates the UBI layout volume which contains 2 copies of the
- * volume table. Returns zero in case of success and %-1 in case of failure.
- */
-int ubigen_write_layout_vol(const struct ubigen_info *ui,
- struct ubi_vtbl_record *vtbl, FILE *out)
+int
+ubigen_write_broken_update(ubi_info_t u, uint32_t blk)
{
- int size = ui->leb_size;
- struct ubigen_vol_info vi;
- char outbuf[ui->peb_size];
- struct ubi_vid_hdr *vid_hdr;
-
- if (size > UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE)
- size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
-
- vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS;
- vi.id = UBI_LAYOUT_VOLUME_ID;
- vi.alignment = UBI_LAYOUT_VOLUME_ALIGN;
- vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN;
- vi.usable_leb_size = ui->leb_size - vi.data_pad;
- vi.data_pad = ui->leb_size - vi.usable_leb_size;
- vi.type = UBI_LAYOUT_VOLUME_TYPE;
- vi.name = UBI_LAYOUT_VOLUME_NAME;
- vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME);
- vi.compat = UBI_LAYOUT_VOLUME_COMPAT;
-
- memset(outbuf, 0xFF, ui->data_offs);
- vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]);
- init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf);
- memcpy(outbuf + ui->data_offs, vtbl, size);
- memset(outbuf + ui->data_offs + size, 0xFF,
- ui->peb_size - ui->data_offs - size);
-
- init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0);
- size = fwrite(outbuf, 1, ui->peb_size, out);
- if (size == ui->peb_size) {
- init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0);
- size = fwrite(outbuf, 1, ui->peb_size, out);
- if (size != ui->peb_size)
- return sys_errmsg("cannot write %d bytes", ui->peb_size);
+ int rc = 0;
+
+ rc = skip_blks(u, blk);
+ if (rc != 0)
+ return rc;
+
+ rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC);
+ if (rc != 0)
+ return rc;
+
+
+ return 0;
+}
+
+void
+dump_info(ubi_info_t u ubi_unused)
+{
+#ifdef DEBUG
+ int err = 0;
+ if (!u) {
+ fprintf(stderr, "<empty>");
+ return;
+ }
+ if (!u->ec) {
+ fprintf(stderr, "<ec-empty>");
+ err = 1;
+ }
+ if (!u->v) {
+ fprintf(stderr, "<v-empty>");
+ err = 1;
+ }
+ if (err) return;
+
+ fprintf(stderr, "ubi volume\n");
+ fprintf(stderr, "version : %8d\n", u->v->version);
+ fprintf(stderr, "vol_id : %8d\n", be32_to_cpu(u->v->vol_id));
+ fprintf(stderr, "vol_type : %8s\n",
+ u->v->vol_type == UBI_VID_STATIC ?
+ "static" : "dynamic");
+ fprintf(stderr, "used_ebs : %8d\n",
+ be32_to_cpu(u->v->used_ebs));
+ fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size);
+ fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size);
+ fprintf(stderr, "data_pad : 0x%08x\n",
+ be32_to_cpu(u->v->data_pad));
+ fprintf(stderr, "leb_total : %8d\n", u->leb_total);
+ fprintf(stderr, "header offs : 0x%08x\n",
+ be32_to_cpu(u->ec->vid_hdr_offset));
+ fprintf(stderr, "bytes_total : %8d\n", u->bytes_total);
+ fprintf(stderr, " + in MiB : %8.2f M\n",
+ ((float)(u->bytes_total)) / 1024 / 1024);
+ fprintf(stderr, "-------------------------------\n\n");
+#else
+ return;
+#endif
+}
+
+int
+ubigen_destroy(ubi_info_t *u)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ ubi_info_t tmp = *u;
+
+ if (tmp) {
+ if (tmp->v)
+ free(tmp->v);
+ if (tmp->ec)
+ free(tmp->ec);
+ if (tmp->buf)
+ free(tmp->buf);
+ free(tmp);
+ }
+ *u = NULL;
+ return 0;
+}
+
+void
+ubigen_init(void)
+{
+ init_crc32_table(crc32_table);
+}
+
+int
+ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag,
+ size_t data_size, FILE* fp_in, FILE* fp_out)
+{
+ int rc = 0;
+ ubi_info_t res = NULL;
+ uint32_t crc;
+ uint32_t data_offset;
+
+ if (alignment == 0) {
+ rc = EUBIGEN_INVALID_ALIGNMENT;
+ goto ubigen_create_err;
+ }
+ if ((fp_in == NULL) || (fp_out == NULL)) {
+ rc = -EINVAL;
+ goto ubigen_create_err;
+ }
+
+ res = (ubi_info_t) calloc(1, sizeof(struct ubi_info));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr));
+ if (res->v == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
}
+ res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr));
+ if (res->ec == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* data which is needed in the general process */
+ vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET;
+ data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE;
+ res->bytes_total = data_size;
+ res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE;
+ res->data_pad = (res->eb_size - data_offset) % alignment;
+ res->leb_size = res->eb_size - data_offset - res->data_pad;
+ res->leb_total = byte_to_blk(data_size, res->leb_size);
+ res->alignment = alignment;
+
+ if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) {
+ rc = EUBIGEN_TOO_SMALL_EB;
+ goto ubigen_create_err;
+ }
+ res->fp_in = fp_in;
+ res->fp_out = fp_out;
+
+ /* vid hdr data which doesn't change */
+ res->v->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
+ res->v->version = version ? version : UBI_VERSION;
+ res->v->vol_type = vol_type;
+ res->v->vol_id = cpu_to_be32(vol_id);
+ res->v->compat = compat_flag;
+ res->v->data_pad = cpu_to_be32(res->data_pad);
+
+ /* static only: used_ebs */
+ if (res->v->vol_type == UBI_VID_STATIC) {
+ res->v->used_ebs = cpu_to_be32(byte_to_blk
+ (res->bytes_total,
+ res->leb_size));
+ }
+
+ /* ec hdr (fixed, doesn't change) */
+ res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC);
+ res->ec->version = version ? version : UBI_VERSION;
+ res->ec->ec = cpu_to_be64(ec);
+ res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset);
+
+ res->ec->data_offset = cpu_to_be32(data_offset);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec,
+ UBI_EC_HDR_SIZE_CRC);
+ res->ec->hdr_crc = cpu_to_be32(crc);
+
+ /* prepare a read buffer */
+ res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t));
+ if (res->buf == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* point to distinct regions within the buffer */
+ res->ptr_ec_hdr = res->buf;
+ res->ptr_vid_hdr = res->buf + be32_to_cpu(res->ec->vid_hdr_offset);
+ res->ptr_data = res->buf + be32_to_cpu(res->ec->vid_hdr_offset)
+ + UBI_VID_HDR_SIZE;
+
+ rc = validate_ubi_info(res);
+ if (rc != 0) {
+ fprintf(stderr, "Volume validation failed: %d\n", rc);
+ goto ubigen_create_err;
+ }
+
+ dump_info(res);
+ *u = res;
+ return rc;
+
+ ubigen_create_err:
+ if (res) {
+ if (res->v)
+ free(res->v);
+ if (res->ec)
+ free(res->ec);
+ if (res->buf)
+ free(res->buf);
+ free(res);
+ }
+ *u = NULL;
+ return rc;
+}
+
+int
+ubigen_get_leb_size(ubi_info_t u, size_t* size)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *size = u->leb_size;
+ return 0;
+}
+
+
+int
+ubigen_get_leb_total(ubi_info_t u, size_t* total)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *total = u->leb_total;
+ return 0;
+}
+
+int
+ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* vol_name, struct ubi_vtbl_record *lvol_rec)
+{
+ uint32_t crc;
+
+ if ((u == NULL) || (vol_name == NULL))
+ return -EINVAL;
+
+ memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE);
+
+ lvol_rec->reserved_pebs =
+ cpu_to_be32(byte_to_blk(reserved_bytes, u->leb_size));
+ lvol_rec->alignment = cpu_to_be32(u->alignment);
+ lvol_rec->data_pad = u->v->data_pad;
+ lvol_rec->vol_type = u->v->vol_type;
+
+ lvol_rec->name_len =
+ cpu_to_be16((uint16_t)strlen((const char*)vol_name));
+
+ memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ lvol_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ lvol_rec->crc = cpu_to_be32(crc);
+
return 0;
}
diff --git a/ubi-utils/src/libubimirror.c b/ubi-utils/src/libubimirror.c
new file mode 100644
index 0000000..d06770e
--- /dev/null
+++ b/ubi-utils/src/libubimirror.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <memory.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include "ubimirror.h"
+
+#define COMPARE_BUF_SIZE (128 * 1024)
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+#define EBUF(fmt...) do { \
+ snprintf(err_buf, err_buf_size, fmt); \
+} while (0)
+
+enum {
+ compare_error = -1,
+ seek_error = -2,
+ write_error = -3,
+ read_error = -4,
+ update_error = -5,
+ ubi_error = -6,
+ open_error = -7,
+ close_error = -8,
+ compare_equal = 0,
+ compare_different = 1
+};
+
+/*
+ * Read len number of bytes from fd.
+ * Return 0 on EOF, -1 on error.
+ */
+static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t got, have = 0;
+
+ do {
+ got = read(fd, buf + have, len - have);
+ if (got == -1 && errno != EINTR)
+ return -1;
+ have += got;
+ } while (got > 0 && have < len);
+ return have;
+}
+
+/*
+ * Write len number of bytes to fd.
+ * Return bytes written (>= 0), -1 on error.
+ */
+static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t done, have = 0;
+
+ do {
+ done = write(fd, buf + have, len - have);
+ if (done == -1 && errno != EINTR)
+ return -1;
+ have += done;
+ } while (done > 0 && have < len);
+ return have;
+}
+
+/*
+ * Compare two files. Return 0, 1, or -1, depending on whether the
+ * files are equal, different, or an error occured.
+ * Return compare-different when target volume can not be read. Might be
+ * an interrupted volume update and then the target device returns -EIO but
+ * can be updated.
+ *
+ * fd_a is source
+ * fd_b is destination
+ */
+static int compare_files(int fd_a, int fd_b)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ int rc;
+
+ for (;;) {
+ len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a));
+ if (len_a == -1) {
+ rc = compare_error;
+ break;
+ }
+ len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b));
+ if (len_b == -1) {
+ rc = compare_different;
+ break;
+ }
+ if (len_a != len_b) {
+ rc = compare_different;
+ break;
+ }
+ if (len_a == 0) { /* Size on both files equal and EOF */
+ rc = compare_equal;
+ break;
+ }
+ if (memcmp(buf_a, buf_b, len_a) != 0 ) {
+ rc = compare_different;
+ break;
+ }
+ }
+ /* Position both files at the beginning */
+ if (lseek(fd_a, 0, SEEK_SET) == -1 ||
+ lseek(fd_b, 0, SEEK_SET) == -1)
+ rc = seek_error;
+ return rc;
+}
+
+int vol_get_used_bytes(int vol_fd, unsigned long long *bytes)
+{
+ off_t res;
+
+ res = lseek(vol_fd, 0, SEEK_END);
+ if (res == (off_t)-1)
+ return -1;
+ *bytes = (unsigned long long) res;
+ res = lseek(vol_fd, 0, SEEK_SET);
+ return res == (off_t)-1 ? -1 : 0;
+}
+
+static int copy_files(libubi_t ulib, int fd_in, int fd_out)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ unsigned long long update_size, copied;
+
+ if (vol_get_used_bytes(fd_in, &update_size) == -1 ||
+ ubi_update_start(ulib, fd_out, update_size) == -1)
+ return update_error;
+ for (copied = 0; copied < update_size; copied += len_b ) {
+ len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a));
+ if (len_a == -1)
+ return read_error;
+ if (len_a == 0) /* Reach EOF */
+ return 0;
+ len_b = flush_buffer(fd_out, buf_a, len_a);
+ if (len_b != len_a)
+ return write_error;
+ }
+ return 0;
+}
+
+int ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ uint32_t src_id;
+ char path[PATH_MAX];
+ libubi_t ulib;
+ int fd_in = -1, i = 0, fd_out = -1;
+
+ if (ids_size == 0)
+ return 0;
+ else {
+ if ((seqnum < 0) || (seqnum > (ids_size - 1))) {
+ EBUF("volume id %d out of range", seqnum);
+ return EUBIMIRROR_NO_SRC;
+ }
+ src_id = ids[seqnum];
+ }
+
+ ulib = libubi_open();
+ if (ulib == NULL)
+ return ubi_error;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id);
+
+ fd_in = open(path, O_RDONLY);
+ if (fd_in == -1) {
+ EBUF("open error source volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ if (ids[i] == src_id) /* skip self-mirror */
+ continue;
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]);
+
+ fd_out = open(path, O_RDWR);
+ if (fd_out < 0){
+ EBUF("open error destination volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+ rc = compare_files(fd_in, fd_out);
+ if (rc < 0) {
+ EBUF("compare error volume %d and %d", src_id, ids[i]);
+ goto err;
+ } else if (rc == compare_different) {
+ rc = copy_files(ulib, fd_in, fd_out);
+ if (rc != 0) {
+ EBUF("mirror error volume %d to %d", src_id,
+ ids[i]);
+ goto err;
+ }
+ }
+ if ((rc = close(fd_out)) == -1) {
+ EBUF("close error volume %d", ids[i]);
+ rc = close_error;
+ goto err;
+ } else
+ fd_out = -1;
+ }
+err:
+ if (fd_out != -1)
+ close(fd_out);
+ if (fd_in != -1)
+ close(fd_in);
+ if (ulib != NULL)
+ libubi_close(ulib);
+ return rc;
+}
diff --git a/ubi-utils/src/list.c b/ubi-utils/src/list.c
new file mode 100644
index 0000000..6eb716b
--- /dev/null
+++ b/ubi-utils/src/list.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "list.h"
+
+list_t
+mk_empty(void)
+{
+ return (list_t) NULL;
+}
+
+int
+is_empty(list_t l)
+{
+ return l == NULL;
+}
+
+info_t
+head(list_t l)
+{
+ assert(!is_empty(l));
+ return l->info;
+}
+
+list_t
+tail(list_t l)
+{
+ assert(!is_empty(l));
+ return l->next;
+}
+
+list_t
+remove_head(list_t l)
+{
+ list_t res;
+ assert(!is_empty(l));
+
+ res = l->next;
+ free(l);
+ return res;
+}
+
+list_t
+cons(info_t e, list_t l)
+{
+ list_t res = malloc(sizeof(*l));
+ if (!res)
+ return NULL;
+ res->info = e;
+ res->next = l;
+
+ return res;
+}
+
+list_t
+prepend_elem(info_t e, list_t l)
+{
+ return cons(e,l);
+}
+
+list_t
+append_elem(info_t e, list_t l)
+{
+ if (is_empty(l)) {
+ return cons(e,l);
+ }
+ l->next = append_elem(e, l->next);
+
+ return l;
+}
+
+list_t
+insert_sorted(cmp_func_t cmp, info_t e, list_t l)
+{
+ if (is_empty(l))
+ return cons(e, l);
+
+ switch (cmp(e, l->info)) {
+ case -1:
+ case 0:
+ return l;
+ break;
+ case 1:
+ l->next = insert_sorted(cmp, e, l);
+ break;
+ default:
+ break;
+ }
+
+ /* never reached */
+ return NULL;
+}
+
+list_t
+remove_all(free_func_t free_func, list_t l)
+{
+ if (is_empty(l))
+ return l;
+ list_t lnext = l->next;
+
+ if (free_func && l->info) {
+ free_func(&(l->info));
+ }
+ free(l);
+
+ return remove_all(free_func, lnext);
+}
+
+
+info_t
+is_in(cmp_func_t cmp, info_t e, list_t l)
+{
+ return
+ (is_empty(l))
+ ? NULL
+ : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next);
+}
+
+
+void
+apply(process_func_t process_func, list_t l)
+{
+ list_t ptr;
+ void *i;
+ foreach(i, ptr, l) {
+ process_func(i);
+ }
+}
diff --git a/ubi-utils/src/list.h b/ubi-utils/src/list.h
new file mode 100644
index 0000000..e8452a2
--- /dev/null
+++ b/ubi-utils/src/list.h
@@ -0,0 +1,56 @@
+#ifndef __LIST_H__
+#define __LIST_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+
+#define foreach(elem, ptr, list) \
+ for (elem = list != NULL ? (typeof(elem)) head(list) \
+ : NULL, ptr = list; \
+ ptr != NULL; \
+ ptr = tail(ptr), \
+ elem = (typeof(elem)) ptr ? head(ptr) : NULL)
+
+typedef struct node* list_t;
+typedef void* info_t;
+typedef int (*free_func_t)(info_t*);
+typedef int (*cmp_func_t)(info_t, info_t);
+typedef void (*process_func_t)(info_t);
+
+struct node {
+ list_t next;
+ info_t info;
+};
+
+list_t mk_empty(void);
+int is_empty(list_t l);
+info_t is_in(cmp_func_t cmp, info_t e, list_t l);
+info_t head(list_t l);
+list_t tail(list_t l);
+list_t remove_head(list_t l);
+list_t cons(info_t e, list_t l);
+list_t prepend_elem(info_t e, list_t);
+list_t append_elem(info_t e, list_t);
+list_t remove_all(free_func_t free_func, list_t l);
+list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l);
+void apply(process_func_t process_func, list_t l);
+
+#endif /* __LIST_H__ */
diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c
new file mode 100644
index 0000000..952f651
--- /dev/null
+++ b/ubi-utils/src/mkbootenv.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Create boot-parameter/pdd data from an ASCII-text input file.
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+
+#define PROGRAM_VERSION "1.3"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "mkbootenv - processes bootenv text files and convertes "
+ "them into a binary format.\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -o, --output=<fname> Write the output data to <output> instead of\n"
+" stdout.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: mkbootenv [-c?V] [-o <output>] [--copyright] [--output=<output>]\n"
+" [--help] [--usage] [--version] [bootenv-txt-file]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ FILE* fp_in;
+ FILE* fp_out;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "co:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o':
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for input\n",
+ argv[optind]);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env;
+
+ myargs args = {
+ .fp_in = stdin,
+ .fp_out = stdout,
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle.");
+ goto err;
+ }
+ rc = bootenv_read_txt(args.fp_in, env);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from input file.");
+ goto err;
+ }
+ rc = bootenv_write(args.fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to output file.");
+ goto err;
+ }
+
+ if (args.fp_in != stdin) {
+ fclose(args.fp_in);
+ }
+ if (args.fp_out != stdout) {
+ fclose(args.fp_out);
+ }
+
+err:
+ bootenv_destroy(&env);
+ return rc;
+}
diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c
new file mode 100644
index 0000000..be62e30
--- /dev/null
+++ b/ubi-utils/src/nand2bin.c
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to decompose NAND images and strip OOB off. Not yet finished ...
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ * 1.4 Fixed OOB output file
+ * 1.5 Added verbose output and option to set blocksize.
+ * Added split block mode for more convenient analysis.
+ * 1.6 Fixed ECC error detection and correction.
+ */
+
+#include <config.h>
+#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 <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define MAXPATH 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ const char *oob_file;
+ const char *output_file;
+ size_t pagesize;
+ size_t blocksize;
+ int split_blocks;
+ size_t in_len; /* size of input file */
+ int correct_ecc;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .output_file = "data.bin",
+ .oob_file = "oob.bin",
+ .pagesize = 2048,
+ .blocksize = 128 * 1024,
+ .in_len = 0,
+ .split_blocks = 0,
+ .correct_ecc = 0,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "nand2bin - split data and OOB.\n";
+
+static const char *optionsstr =
+" -o, --output=<output> Data output file\n"
+" -O, --oob=<oob> OOB output file\n"
+" -p, --pagesize=<pagesize> NAND pagesize\n"
+" -b, --blocksize=<blocksize> NAND blocksize\n"
+" -s, --split-blocks generate binaries for each block\n"
+" -e, --correct-ecc Correct data according to ECC info\n"
+" -v, --verbose verbose output\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n";
+
+static const char *usage =
+"Usage: nand2bin [-?] [-o <output>] [-O <oob>] [-p <pagesize>]\n"
+" [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n"
+" [--usage] input.mif\n";
+
+static int verbose = 0;
+
+static struct option long_options[] = {
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' },
+ { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * str_to_num - Convert string into number and cope with endings like
+ * k, K, kib, KiB for kilobyte
+ * m, M, mib, MiB for megabyte
+ */
+static uint32_t str_to_num(char *str)
+{
+ char *s = str;
+ ulong 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);
+ }
+ }
+ return num;
+}
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The argument list
+ * @param args Pointer to program args structure
+ *
+ * @return error
+ *
+ */
+static int parse_opt(int argc, char **argv, struct args *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "b:eo:O:p:sv?", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* --pagesize<pagesize> */
+ args->pagesize = str_to_num(optarg);
+ break;
+
+ case 'b': /* --blocksize<blocksize> */
+ args->blocksize = str_to_num(optarg);
+ break;
+
+ case 'v': /* --verbose */
+ verbose++;
+ break;
+
+ case 's': /* --split-blocks */
+ args->split_blocks = 1;
+ break;
+
+ case 'e': /* --correct-ecc */
+ args->correct_ecc = 1;
+ break;
+
+ case 'o': /* --output=<output.bin> */
+ args->output_file = optarg;
+ break;
+
+ case 'O': /* --oob=<oob.bin> */
+ args->oob_file = optarg;
+ break;
+
+ case '?': /* help */
+ printf("Usage: nand2bin [OPTION...] input.mif\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->arg1 = argv[optind++];
+
+ return 0;
+}
+
+static int calc_oobsize(size_t pagesize)
+{
+ switch (pagesize) {
+ case 512: return 16;
+ case 2048: return 64;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size)
+{
+ int k;
+
+ for (k = 0; k < size; k++) {
+ fprintf(fp, "%02x ", buf[k]);
+ if ((k & 15) == 15)
+ fprintf(fp, "\n");
+ }
+}
+
+static int process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize)
+{
+ int eccpoi, oobsize;
+ size_t i;
+
+ switch (pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ fprintf(stderr, "Unsupported page size: %zd\n", pagesize);
+ return -EINVAL;
+ }
+ memset(oobbuf, 0xff, oobsize);
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+ return 0;
+}
+
+static int bad_marker_offs_in_oob(int pagesize)
+{
+ switch (pagesize) {
+ case 2048: return 0;
+ case 512: return 5;
+ }
+ return -EINVAL;
+}
+
+static int decompose_image(struct args *args, FILE *in_fp,
+ FILE *bin_fp, FILE *oob_fp)
+{
+ int read, rc, page = 0;
+ size_t oobsize = calc_oobsize(args->pagesize);
+ uint8_t *buf = malloc(args->pagesize);
+ uint8_t *oob = malloc(oobsize);
+ uint8_t *calc_oob = malloc(oobsize);
+ uint8_t *calc_buf = malloc(args->pagesize);
+ uint8_t *page_buf;
+ int pages_per_block = args->blocksize / args->pagesize;
+ int eccpoi = 0, eccpoi_start;
+ unsigned int i;
+ int badpos = bad_marker_offs_in_oob(args->pagesize);
+
+ switch (args->pagesize) {
+ case 2048: eccpoi_start = 64 / 2; break;
+ case 512: eccpoi_start = 16 / 2; break;
+ default: exit(EXIT_FAILURE);
+ }
+
+ if (!buf)
+ exit(EXIT_FAILURE);
+ if (!oob)
+ exit(EXIT_FAILURE);
+ if (!calc_oob)
+ exit(EXIT_FAILURE);
+ if (!calc_buf)
+ exit(EXIT_FAILURE);
+
+ while (!feof(in_fp)) {
+ /* read page by page */
+ read = fread(buf, 1, args->pagesize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ if (read != (ssize_t)args->pagesize)
+ break;
+
+ read = fread(oob, 1, oobsize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page_buf = buf; /* default is unmodified data */
+
+ if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) {
+ if (verbose)
+ printf("Block %d is bad\n",
+ page / pages_per_block);
+ goto write_data;
+ }
+ if (args->correct_ecc)
+ page_buf = calc_buf;
+
+ process_page(buf, calc_oob, args->pagesize);
+ memcpy(calc_buf, buf, args->pagesize);
+
+ /*
+ * Our oob format uses only the last 3 bytes out of 4.
+ * The first byte is 0x00 when the ECC is generated by
+ * our toolset and 0xff when generated by Linux. This
+ * is to be fixed when we want nand2bin work for other
+ * ECC layouts too.
+ */
+ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize;
+ i += 256, eccpoi += 4)
+ oob[eccpoi] = calc_oob[eccpoi] = 0xff;
+
+ if (verbose && memcmp(oob, calc_oob, oobsize) != 0) {
+ printf("\nECC compare mismatch found at block %d page %d!\n",
+ page / pages_per_block, page % pages_per_block);
+
+ printf("Read out OOB Data:\n");
+ hexdump(stdout, oob, oobsize);
+
+ printf("Calculated OOB Data:\n");
+ hexdump(stdout, calc_oob, oobsize);
+ }
+
+ /* Do correction on subpage base */
+ for (i = 0, eccpoi = eccpoi_start; i < args->pagesize;
+ i += 256, eccpoi += 4) {
+ rc = nand_correct_data(calc_buf + i, &oob[eccpoi + 1],
+ &calc_oob[eccpoi + 1]);
+
+ if (rc == -1)
+ fprintf(stdout, "Uncorrectable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ else if (rc > 0)
+ fprintf(stdout, "Correctable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ }
+
+ write_data:
+ rc = fwrite(page_buf, 1, args->pagesize, bin_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(oob, 1, oobsize, oob_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page++;
+ }
+ free(calc_buf);
+ free(calc_oob);
+ free(oob);
+ free(buf);
+ return 0;
+}
+
+static int split_blocks(struct args *args, FILE *in_fp)
+{
+ uint8_t *buf;
+ size_t oobsize = calc_oobsize(args->pagesize);
+ int pages_per_block = args->blocksize / args->pagesize;
+ int block_len = pages_per_block * (args->pagesize + oobsize);
+ int blocks = args->in_len / block_len;
+ char bname[256] = { 0, };
+ int badpos = bad_marker_offs_in_oob(args->pagesize);
+ int bad_blocks = 0, i, bad_block = 0;
+ ssize_t rc;
+ FILE *b;
+
+ buf = malloc(block_len);
+ if (!buf) {
+ perror("Not enough memory");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < blocks; i++) {
+ rc = fread(buf, 1, block_len, in_fp);
+ if (rc != block_len) {
+ fprintf(stderr, "cannot read enough data!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* do block analysis */
+ bad_block = 0;
+ if ((buf[args->pagesize + badpos] != 0xff) ||
+ (buf[2 * args->pagesize + oobsize + badpos] != 0xff)) {
+ bad_blocks++;
+ bad_block = 1;
+ }
+ if ((verbose && bad_block) || (verbose > 1)) {
+ printf("-- (block %d oob of page 0 and 1)\n", i);
+ hexdump(stdout, buf + args->pagesize, oobsize);
+ printf("--\n");
+ hexdump(stdout, buf + 2 * args->pagesize +
+ oobsize, oobsize);
+ }
+
+ /* write complete block out */
+ snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i);
+ b = fopen(bname, "w+");
+ if (!b) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(buf, 1, block_len, b);
+ if (rc != block_len) {
+ fprintf(stderr, "could not write all data!\n");
+ exit(EXIT_FAILURE);
+ }
+ fclose(b);
+ }
+
+ free(buf);
+ if (bad_blocks || verbose)
+ fprintf(stderr, "%d blocks, %d bad blocks\n",
+ blocks, bad_blocks);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in, *bin = NULL, *oob = NULL;
+ struct stat file_info;
+
+ parse_opt(argc, argv, &myargs);
+
+ if (!myargs.arg1) {
+ fprintf(stderr, "Please specify input file!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (lstat(myargs.arg1, &file_info) != 0) {
+ perror("Cannot fetch file size from input file.\n");
+ exit(EXIT_FAILURE);
+ }
+ myargs.in_len = file_info.st_size;
+
+ in = fopen(myargs.arg1, "r");
+ if (!in) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+
+ if (myargs.split_blocks) {
+ split_blocks(&myargs, in);
+ goto out;
+ }
+
+ bin = fopen(myargs.output_file, "w+");
+ if (!bin) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ oob = fopen(myargs.oob_file, "w+");
+ if (!oob) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ decompose_image(&myargs, in, bin, oob);
+
+ out:
+ if (in) fclose(in);
+ if (bin) fclose(bin);
+ if (oob) fclose(oob);
+ exit(EXIT_SUCCESS);
+}
diff --git a/ubi-utils/src/nandcorr.c b/ubi-utils/src/nandcorr.c
new file mode 100644
index 0000000..caa07e2
--- /dev/null
+++ b/ubi-utils/src/nandcorr.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in
+ * a 256 bytes of data.
+ *
+ * Reimplement by Thomas Gleixner after staring long enough at the
+ * mess in drivers/mtd/nand/nandecc.c
+ *
+ */
+
+#include "nandecc.h"
+
+static int countbits(uint32_t byte)
+{
+ int res = 0;
+
+ for (;byte; byte >>= 1)
+ res += byte & 0x01;
+ return res;
+}
+
+/**
+ * @dat: data which should be corrected
+ * @read_ecc: ecc information read from flash
+ * @calc_ecc: calculated ecc information from the data
+ * @return: number of corrected bytes
+ * or -1 when no correction is possible
+ */
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+ const uint8_t *calc_ecc)
+{
+ uint8_t s0, s1, s2;
+
+ /*
+ * Do error detection
+ *
+ * Be careful, the index magic is due to a pointer to a
+ * uint32_t.
+ */
+ s0 = calc_ecc[0] ^ read_ecc[0];
+ s1 = calc_ecc[1] ^ read_ecc[1];
+ s2 = calc_ecc[2] ^ read_ecc[2];
+
+ if ((s0 | s1 | s2) == 0)
+ return 0;
+
+ /* Check for a single bit error */
+ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+ ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+ ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+ uint32_t byteoffs, bitnum;
+
+ byteoffs = (s1 << 0) & 0x80;
+ byteoffs |= (s1 << 1) & 0x40;
+ byteoffs |= (s1 << 2) & 0x20;
+ byteoffs |= (s1 << 3) & 0x10;
+
+ byteoffs |= (s0 >> 4) & 0x08;
+ byteoffs |= (s0 >> 3) & 0x04;
+ byteoffs |= (s0 >> 2) & 0x02;
+ byteoffs |= (s0 >> 1) & 0x01;
+
+ bitnum = (s2 >> 5) & 0x04;
+ bitnum |= (s2 >> 4) & 0x02;
+ bitnum |= (s2 >> 3) & 0x01;
+
+ dat[byteoffs] ^= (1 << bitnum);
+
+ return 1;
+ }
+
+ if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+ return 1;
+
+ return -1;
+}
+
diff --git a/ubi-utils/src/nandecc.c b/ubi-utils/src/nandecc.c
new file mode 100644
index 0000000..71660ef
--- /dev/null
+++ b/ubi-utils/src/nandecc.c
@@ -0,0 +1,159 @@
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+ * This file 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 or (at your option) any
+ * later version.
+ *
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include "nandecc.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2: line parity reg 2
+ * @reg3: line parity reg 3
+ * @ecc_code: ecc
+ *
+ * Creates non-inverted ECC code from line parity
+ */
+static void nand_trans_result(uint8_t reg2, uint8_t reg3,
+ uint8_t *ecc_code)
+{
+ uint8_t a, b, i, tmp1, tmp2;
+
+ /* Initialize variables */
+ a = b = 0x80;
+ tmp1 = tmp2 = 0;
+
+ /* Calculate first ECC byte */
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Calculate second ECC byte */
+ b = 0x80;
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Store two of the ECC bytes */
+ ecc_code[1] = tmp1;
+ ecc_code[0] = tmp2;
+}
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for
+ * 256 byte block
+ *
+ * @dat: raw data
+ * @ecc_code: buffer for ECC
+ */
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code)
+{
+ uint8_t idx, reg1, reg2, reg3;
+ int j;
+
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+ ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+
+ /* Build up column parity */
+ for(j = 0; j < 256; j++) {
+
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[dat[j]];
+ reg1 ^= (idx & 0x3f);
+
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (uint8_t) j;
+ reg2 ^= ~((uint8_t) j);
+ }
+ }
+
+ /* Create non-inverted ECC code from line parity */
+ nand_trans_result(reg2, reg3, ecc_code);
+
+ /* Calculate final ECC code */
+ ecc_code[0] = ~ecc_code[0];
+ ecc_code[1] = ~ecc_code[1];
+ ecc_code[2] = ((~reg1) << 2) | 0x03;
+ return 0;
+}
diff --git a/ubi-utils/src/nandecc.h b/ubi-utils/src/nandecc.h
new file mode 100644
index 0000000..bcf1982
--- /dev/null
+++ b/ubi-utils/src/nandecc.h
@@ -0,0 +1,29 @@
+#ifndef _NAND_ECC_H
+#define _NAND_ECC_H
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * NAND ecc functions
+ */
+
+#include <stdint.h>
+
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code);
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+ const uint8_t *calc_ecc);
+
+#endif
diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c
new file mode 100644
index 0000000..515efd6
--- /dev/null
+++ b/ubi-utils/src/pddcustomize.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * PDD (platform description data) contains a set of system specific
+ * boot-parameters. Some of those parameters need to be handled
+ * special on updates, e.g. the MAC addresses. They must also be kept
+ * if the system is updated and one must be able to modify them when
+ * the system has booted the first time. This tool is intended to do
+ * PDD modification.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ * 1.5 Migrated to new libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "libubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.5"
+
+#define DEFAULT_DEV_PATTERN "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_LIST,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pddcustomize - customize bootenv and pdd values.\n";
+
+static const char *optionsstr =
+" -b, --both Mirror updated PDD to redundand copy.\n"
+" -c, --copyright Print copyright information.\n"
+" -i, --input=<input> Binary input file. For debug purposes.\n"
+" -l, --list List card bootenv/pdd values.\n"
+" -o, --output=<output> Binary output file. For debug purposes.\n"
+" -s, --side=<seqnum> The side/seqnum to update.\n"
+" -x, --host use x86 platform for debugging.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pddcustomize [-bclx?V] [-i <input>] [-o <output>] [-s <seqnum>]\n"
+" [--both] [--copyright] [--input=<input>] [--list]\n"
+" [--output=<output>] [--side=<seqnum>] [--host] [--help] [--usage]\n"
+" [--version] [key=value] [...]\n";
+
+struct option long_options[] = {
+ { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' },
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+typedef struct myargs {
+ action_t action;
+ const char* file_in;
+ const char* file_out;
+ int both;
+ int side;
+ int x86; /* X86 host, use files for testing */
+ bootenv_t env_in;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+static int
+extract_pair(bootenv_t env, const char* str)
+{
+ int rc = 0;
+ char* key;
+ char* val;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ err_msg("Wrong argument: %s\n"
+ "Expecting key=value pair.\n", str);
+ rc = -1;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+ rc = bootenv_set(env, key, val);
+
+err:
+ free(key);
+ return rc;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int rc = 0;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "clbxs:i:o:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s\n", copyright);
+ ABORT_ARGP;
+ break;
+ case 'l':
+ args->action = ACT_LIST;
+ break;
+ case 'b':
+ args->both = 1;
+ break;
+ case 'x':
+ args->x86 = 1;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %d.\n"
+ "Supported seqnums are "
+ "'0' and '1'\n",
+ args->side, optarg);
+ ERR_ARGP;
+ }
+ break;
+ case 'i':
+ args->file_in = optarg;
+ break;
+ case 'o':
+ args->file_out = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pddcustomize [OPTION...] "
+ "[key=value] [...]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ rc = extract_pair(args->env_in, argv[optind++]);
+ if (rc != 0)
+ ERR_ARGP;
+ }
+
+ return 0;
+}
+
+static int
+list_bootenv(bootenv_t env)
+{
+ int rc = 0;
+ rc = bootenv_write_txt(stdout, env);
+ if (rc != 0) {
+ err_msg("Cannot list bootenv/pdd. rc: %d\n", rc);
+ goto err;
+ }
+err:
+ return rc;
+}
+
+static int
+process_key_value(bootenv_t env_in, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, i;
+ const char* tmp;
+ const char** key_vec = NULL;
+
+ rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ rc = bootenv_get(env_in, key_vec[i], &tmp);
+ if (rc != 0) {
+ err_msg("Cannot read value to input key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ rc = bootenv_set(env, key_vec[i], tmp);
+ if (rc != 0) {
+ err_msg("Cannot set value key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ }
+
+err:
+ if (key_vec != NULL)
+ free(key_vec);
+ return rc;
+}
+
+static int
+read_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_in = NULL;
+
+ fp_in = fopen(file, "rb");
+ if (fp_in == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from file %s. rc: %d\n",
+ file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_in);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ libubi_t ulib;
+ int rc = 0;
+ char path[PATH_MAX];
+ FILE* fp_in = NULL;
+
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ err_msg("Cannot allocate ubi structure\n");
+ return -1;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_in = fopen(path, "r");
+ if (fp_in == NULL) {
+ err_msg("Cannot open volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+err:
+ if (fp_in)
+ fclose(fp_in);
+ libubi_close(ulib);
+ return rc;
+}
+
+static int
+write_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_out;
+
+ fp_out = fopen(file, "wb");
+ if (fp_out == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_out);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ libubi_t ulib;
+ int rc = 0;
+ char path[PATH_MAX];
+ FILE* fp_out = NULL;
+ size_t nbytes ;
+
+ rc = bootenv_size(env, &nbytes);
+ if (rc) {
+ err_msg("Cannot determine size of bootenv structure\n");
+ return rc;
+ }
+ ulib = libubi_open();
+ if (ulib == NULL) {
+ err_msg("Cannot allocate ubi structure\n");
+ return rc;
+ }
+
+ snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+ fp_out = fopen(path, "r+");
+ if (fp_out == NULL) {
+ err_msg("Cannot fopen volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to volume %d number:%d\n",
+ devno, id);
+ goto err;
+ }
+
+err:
+ if( fp_out )
+ fclose(fp_out);
+ libubi_close(ulib);
+ return rc;
+}
+
+static int
+do_mirror(int volno)
+{
+ char errbuf[1024];
+ uint32_t ids[2];
+ int rc;
+ int src_volno_idx = 0;
+
+ ids[0] = EXAMPLE_BOOTENV_VOL_ID_1;
+ ids[1] = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if (volno == EXAMPLE_BOOTENV_VOL_ID_2)
+ src_volno_idx = 1;
+
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf,
+ sizeof errbuf);
+ if( rc )
+ err_msg(errbuf);
+ return rc;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env = NULL;
+ uint32_t boot_volno;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .file_in = NULL,
+ .file_out = NULL,
+ .side = -1,
+ .x86 = 0,
+ .both = 0,
+ .env_in = NULL,
+
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ rc = bootenv_create(&(args.env_in));
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = -1;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 0;
+ goto out;
+ }
+
+ if ((args.side == 0) || (args.side == -1))
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_1;
+ else
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if( args.x86 )
+ rc = read_bootenv(args.file_in, env);
+ else
+ rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (args.action == ACT_LIST) {
+ rc = list_bootenv(env);
+ if (rc != 0) {
+ goto err;
+ }
+ goto out;
+ }
+
+ rc = process_key_value(args.env_in, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if( args.x86 )
+ rc = write_bootenv(args.file_in, env);
+ else
+ rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0) {
+ goto err;
+ }
+ if( args.both ) /* No side specified, update both */
+ rc = do_mirror(boot_volno);
+
+ out:
+ err:
+ bootenv_destroy(&env);
+ bootenv_destroy(&(args.env_in));
+ return rc;
+}
diff --git a/ubi-utils/src/peb.c b/ubi-utils/src/peb.c
new file mode 100644
index 0000000..08b770f
--- /dev/null
+++ b/ubi-utils/src/peb.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "peb.h"
+
+int
+peb_cmp(peb_t eb_1, peb_t eb_2)
+{
+ assert(eb_1);
+ assert(eb_2);
+
+ return eb_1->num == eb_2->num ? 0
+ : eb_1->num > eb_2->num ? 1 : -1;
+}
+
+int
+peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb)
+{
+ int rc = 0;
+
+ peb_t res = (peb_t) malloc(sizeof(struct peb));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ res->num = eb_num;
+ res->size = eb_size;
+ res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t));
+ if (!res->data) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(res->data, 0xff, res->size);
+
+ *peb = res;
+ return 0;
+err:
+ if (res) {
+ if (res->data)
+ free(res->data);
+ free(res);
+ }
+ *peb = NULL;
+ return rc;
+}
+
+int
+peb_fill(peb_t peb, uint8_t* buf, size_t buf_size)
+{
+ if (!peb)
+ return -EINVAL;
+
+ if (buf_size > peb->size)
+ return -EINVAL;
+
+ memcpy(peb->data, buf, buf_size);
+ return 0;
+}
+
+int
+peb_write(FILE* fp_out, peb_t peb)
+{
+ size_t written = 0;
+
+ if (peb == NULL)
+ return -EINVAL;
+
+ written = fwrite(peb->data, 1, peb->size, fp_out);
+
+ if (written != peb->size)
+ return -EIO;
+
+ return 0;
+}
+
+int
+peb_free(peb_t* peb)
+{
+ peb_t tmp = *peb;
+ if (tmp) {
+ if (tmp->data)
+ free(tmp->data);
+ free(tmp);
+ }
+ *peb = NULL;
+
+ return 0;
+}
+
+void peb_dump(FILE* fp_out, peb_t peb)
+{
+ fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size);
+}
diff --git a/ubi-utils/src/peb.h b/ubi-utils/src/peb.h
new file mode 100644
index 0000000..246bce8
--- /dev/null
+++ b/ubi-utils/src/peb.h
@@ -0,0 +1,41 @@
+#ifndef __RAW_BLOCK_H__
+#define __RAW_BLOCK_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct peb *peb_t;
+struct peb {
+ uint32_t num; /* Physical eraseblock number
+ * in the RAW file. */
+ uint32_t size; /* Data Size (equals physical
+ * erase block size) */
+ uint8_t* data; /* Data buffer */
+};
+
+int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb);
+int peb_free(peb_t* peb);
+int peb_cmp(peb_t peb_1, peb_t peb_2);
+int peb_write(FILE* fp_out, peb_t peb);
+void peb_dump(FILE* fp_out, peb_t peb);
+
+#endif /* __RAW_BLOCK_H__ */
diff --git a/ubi-utils/src/pfi.c b/ubi-utils/src/pfi.c
new file mode 100644
index 0000000..fa835e2
--- /dev/null
+++ b/ubi-utils/src/pfi.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * @file pfi.c
+ *
+ * @author Oliver Lohmann
+ * Andreas Arnez
+ * Joern Engel
+ * Frank Haverkamp
+ *
+ * @brief libpfi holds all code to create and process pfi files.
+ *
+ * <oliloh@de.ibm.com> Wed Feb 8 11:38:22 CET 2006: Initial creation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "pfi.h"
+
+#define PFI_MAGIC "PFI!\n"
+#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */
+#define PFI_MAGIC_LEN 5
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+enum key_id {
+ /* version 1 */
+ key_version, /* must be index position 0! */
+ key_mode,
+ key_size,
+ key_crc,
+ key_label,
+ key_flags,
+ key_ubi_ids,
+ key_ubi_size,
+ key_ubi_type,
+ key_ubi_names,
+ key_ubi_alignment,
+ key_raw_starts,
+ key_raw_total_size,
+ num_keys,
+};
+
+struct pfi_header {
+ char defined[num_keys]; /* reserve all possible keys even if
+ version does not require this. */
+ int mode_no; /* current mode no. -> can only increase */
+ union {
+ char *str;
+ uint32_t num;
+ } value[num_keys];
+};
+
+
+#define PFI_MANDATORY 0x0001
+#define PFI_STRING 0x0002
+#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */
+#define PFI_MANDATORY_UBI 0x0008
+#define PFI_MANDATORY_RAW 0x0010
+
+struct key_descriptor {
+ enum key_id id;
+ const char *name;
+ uint32_t flags;
+};
+
+static const struct key_descriptor key_desc_v1[] = {
+ { key_version, "version", PFI_MANDATORY },
+ { key_mode, "mode", PFI_MANDATORY | PFI_STRING },
+ { key_size, "size", PFI_MANDATORY },
+ { key_crc, "crc", PFI_MANDATORY },
+ { key_label, "label", PFI_MANDATORY | PFI_STRING },
+ { key_flags, "flags", PFI_MANDATORY },
+ { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI },
+ { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI },
+ { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING },
+ { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW },
+};
+
+static const struct key_descriptor *key_descriptors[] = {
+ NULL,
+ key_desc_v1, /* version 1 */
+};
+
+static const int key_descriptors_max[] = {
+ 0, /* version 0 */
+ sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */
+
+/* latest version contains all possible keys */
+static const struct key_descriptor *key_desc = key_desc_v1;
+
+#define PFI_IS_UBI(mode) \
+ (((mode) != NULL) && (strcmp("ubi", (mode)) == 0))
+
+#define PFI_IS_RAW(mode) \
+ (((mode) != NULL) && (strcmp("raw", (mode)) == 0))
+
+/**
+ * @return <0 On Error.
+ * >=0 Mode no.
+ */
+static int
+get_mode_no(const char* mode)
+{
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(modes); i++)
+ if (strcmp(mode, modes[i]) == 0)
+ return i;
+ return -1;
+}
+
+static int
+find_key_by_name (const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_keys; i++) {
+ if (strcmp(name, key_desc[i].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int
+check_valid (pfi_header head)
+{
+ int i;
+ int max_keys;
+ uint32_t version;
+ const char *mode;
+ const struct key_descriptor *desc;
+ uint32_t to_check = PFI_MANDATORY;
+
+ /*
+ * For the validity check the list of possible keys depends on
+ * the version of the PFI file used.
+ */
+ version = head->value[key_version].num;
+ if (version > PFI_HDRVERSION)
+ return PFI_ENOHEADER;
+
+ max_keys = key_descriptors_max[version];
+ desc = key_descriptors[version];
+
+ if (!desc)
+ return PFI_ENOVERSION;
+
+ mode = head->value[key_mode].str;
+ if (PFI_IS_UBI(mode)) {
+ to_check |= PFI_MANDATORY_UBI;
+ }
+ else if (PFI_IS_RAW(mode)) {
+ to_check |= PFI_MANDATORY_RAW;
+ }
+ else { /* neither UBI nor RAW == ERR */
+ return PFI_EINSUFF;
+ }
+
+ for (i = 0; i < max_keys; i++) {
+ if ((desc[i].flags & to_check) && !head->defined[i]) {
+ fprintf(stderr, "libpfi: %s missing\n", desc[i].name);
+ return PFI_EINSUFF;
+ }
+ }
+
+ return 0;
+}
+
+int pfi_header_init (pfi_header *head)
+{
+ int i;
+ pfi_header self = (pfi_header) malloc(sizeof(*self));
+
+ *head = self;
+ if (self == NULL)
+ return PFI_ENOMEM;
+
+ /* initialize maximum number of possible keys */
+ for (i = 0; i < num_keys; i++) {
+ memset(self, 0, sizeof(*self));
+ self->defined[i] = 0;
+ }
+
+ return 0;
+}
+
+int pfi_header_destroy (pfi_header *head)
+{
+ int i;
+ pfi_header self = *head;
+
+ for (i = 0; i < num_keys; i++) {
+ if (self->defined[i] && (key_desc[i].flags & PFI_STRING) &&
+ self->value[i].str) {
+ free(self->value[i].str);
+ }
+ }
+ free(*head);
+ *head = NULL;
+ return 0;
+}
+
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ head->value[key_id].num = value;
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (value == NULL)
+ return PFI_EINSUFF;
+
+ if ((key_id < 0) || (key_id >= num_keys))
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING) {
+ /*
+ * The value is a string. Copy to a newly allocated
+ * buffer. Delete the old value, if already set.
+ */
+ size_t len = strlen(value) + 1;
+ char *old_str = NULL;
+ char *str;
+
+ old_str = head->value[key_id].str;
+ if (old_str != NULL)
+ free(old_str);
+
+ str = head->value[key_id].str = (char *) malloc(len);
+ if (str == NULL)
+ return PFI_ENOMEM;
+
+ strcpy(str, value);
+ } else {
+ int len;
+ int ret;
+ /* FIXME: here we assume that the value is always
+ given in hex and starts with '0x'. */
+ ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len);
+ if (ret < 1 || value[len] != '\0')
+ return PFI_EBADTYPE;
+ }
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ *value = head->value[key_id].num;
+ return 0;
+}
+
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (!(key_desc[key_id].flags & PFI_STRING))
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ strncpy(value, head->value[key_id].str, size-1);
+ value[size-1] = '\0';
+ return 0;
+}
+
+int pfi_header_write (FILE *out, pfi_header head)
+{
+ int i;
+ int ret;
+
+ pfi_header_setnumber(head, "version", PFI_HDRVERSION);
+
+ if ((ret = check_valid(head)) != 0)
+ return ret;
+
+ /* OK. Now write the header. */
+
+ ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out);
+ if (ret < PFI_MAGIC_LEN)
+ return ret;
+
+
+ for (i = 0; i < num_keys; i++) {
+ if (!head->defined[i])
+ continue;
+
+ ret = fprintf(out, "%s=", key_desc[i].name);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ if (key_desc[i].flags & PFI_STRING) {
+ ret = fprintf(out, "%s", head->value[i].str);
+ if (ret < 0)
+ return PFI_EFILE;
+ } else {
+ ret = fprintf(out, "0x%8x", head->value[i].num);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+
+ ret = fflush(out);
+ if (ret != 0)
+ return PFI_EFILE;
+
+ return 0;
+}
+
+int pfi_header_read (FILE *in, pfi_header head)
+{
+ char magic[PFI_MAGIC_LEN];
+ char mode[PFI_KEYWORD_LEN];
+ char buf[256];
+
+ if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in))
+ return PFI_EFILE;
+ if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0) {
+ if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) {
+ return PFI_DATA_START;
+ }
+ return PFI_ENOHEADER;
+ }
+
+ while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') {
+ char *value;
+ char *end;
+ value = strchr(buf, '=');
+ if (value == NULL)
+ return PFI_ENOHEADER;
+
+ *value = '\0';
+ value++;
+ end = strchr(value, '\n');
+ if (end)
+ *end = '\0';
+
+ if (pfi_header_setvalue(head, buf, value))
+ return PFI_ENOHEADER;
+ }
+
+ if (check_valid(head) != 0)
+ return PFI_ENOHEADER;
+
+ /* set current mode no. in head */
+ pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN);
+ if (head->mode_no > get_mode_no(mode)) {
+ return PFI_EMODE;
+ }
+ head->mode_no = get_mode_no(mode);
+ return 0;
+}
+
+int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__)))
+{
+ fprintf(out, "Sorry not implemented yet. Write mail to "
+ "Andreas Arnez and complain!\n");
+ return 0;
+}
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data)
+{
+ int rc;
+ pfi_header header;
+
+ rc = pfi_header_init (&header);
+ if (0 != rc)
+ return rc;
+ if (!func)
+ return PFI_EINVAL;
+
+ while ((0 == rc) && !feof(in)) {
+ /*
+ * Read header and check consistency of the fields.
+ */
+ rc = pfi_header_read( in, header );
+ if (0 != rc)
+ break;
+ if (func) {
+ rc = func(in, header, priv_data);
+ if (rc != 0)
+ break;
+ }
+ }
+
+ pfi_header_destroy(&header);
+ return rc;
+}
diff --git a/ubi-utils/src/pfi.h b/ubi-utils/src/pfi.h
new file mode 100644
index 0000000..8c5cc07
--- /dev/null
+++ b/ubi-utils/src/pfi.h
@@ -0,0 +1,244 @@
+#ifndef __pfi_h
+#define __pfi_h
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ * Andreas Arnez <arnez@de.ibm.com>
+ * Joern Engel <engeljoe@de.ibm.com>
+ * Frank Haverkamp <haverkam@de.ibm.com>
+ *
+ * @brief libpfi will hold all code to create and process pfi
+ * images. Definitions made in this file are equaly usable for the
+ * development host and the target system.
+ *
+ * @note This header additionally holds the official definitions for
+ * the pfi headers.
+ */
+
+#include <stdio.h> /* FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions. */
+
+#define PFI_HDRVERSION 1 /* current header version */
+
+#define PFI_ENOVERSION 1 /* unknown version */
+#define PFI_ENOHEADER 2 /* not a pfi header */
+#define PFI_EINSUFF 3 /* insufficient information */
+#define PFI_EUNDEF 4 /* key not defined */
+#define PFI_ENOMEM 5 /* out of memory */
+#define PFI_EBADTYPE 6 /* bad data type */
+#define PFI_EFILE 7 /* file I/O error: see errno */
+#define PFI_EFILEINVAL 8 /* file format not valid */
+#define PFI_EINVAL 9 /* invalid parameter */
+#define PFI_ERANGE 10 /* invalid range */
+#define PFI_EMODE 11 /* expecting other mode in this header */
+#define PFI_DATA_START 12 /* data section starts */
+#define PFI_EMAX 13 /* should be always larger as the largest
+ error code */
+
+#define PFI_LABEL_LEN 64 /* This is the maximum length for a
+ PFI header label */
+#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an
+ entry in the mode and type fields */
+
+#define PFI_UBI_MAX_VOLUMES 128
+#define PFI_UBI_VOL_NAME_LEN 127
+
+/**
+ * @brief The pfi header allows to set flags which influence the flashing
+ * behaviour.
+ */
+#define PFI_FLAG_PROTECTED 0x00000001
+
+
+/**
+ * @brief Handle to pfi header. Used in most of the functions associated
+ * with pfi file handling.
+ */
+typedef struct pfi_header *pfi_header;
+
+
+/**
+ * @brief Initialize a pfi header object.
+ *
+ * @param head Pointer to handle. This function allocates memory
+ * for this data structure.
+ * @return 0 on success, otherwise:
+ * PFI_ENOMEM : no memory available for the handle.
+ */
+int pfi_header_init (pfi_header *head);
+
+
+/**
+ * @brief Destroy a pfi header object.
+ *
+ * @param head handle. head is invalid after calling this function.
+ * @return 0 always.
+ */
+int pfi_header_destroy (pfi_header *head);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Must be 0 terminated.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_ENOMEM : no memory available for the handle.
+ * PFI_EBADTYPE : value is not an hex string. This happens
+ * when the key stores an integer and the
+ * new value is not convertable e.g. not in
+ * 0xXXXXXXXX format.
+ */
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object. Provide the
+ * value as a number.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value value to set.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : value is not a string. This happens
+ * when the key stores a string.
+ */
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value);
+
+
+/**
+ * @brief For a given key, return the numerical value stored in a
+ * pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not an integer but a string.
+ */
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value);
+
+
+static inline uint32_t
+pfi_getnumber(pfi_header head, const char *key)
+{
+ uint32_t value;
+ pfi_header_getnumber(head, key, &value);
+ return value;
+}
+
+/**
+ * @brief For a given key, return the string value stored in a pfi
+ * header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Memory must be allocated by the user.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not a string but an integer.
+ */
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size);
+
+
+/**
+ * @brief Write a pfi header object into a given file.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_EINSUFF : not all mandatory fields are filled.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ */
+int pfi_header_write (FILE *out, pfi_header head);
+
+
+/**
+ * @brief Read a pfi header object from a given file.
+ *
+ * @param in input stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_ENOVERSION: unknown header version.
+ * PFI_EFILE : cannot read enough data.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ *
+ * If the header verification returned success the user can assume that
+ * all mandatory fields for a particular version are accessible. Checking
+ * the return code when calling the get-function for those keys is not
+ * required in those cases. For optional fields the checking must still be
+ * done.
+ */
+int pfi_header_read (FILE *in, pfi_header head);
+
+
+/**
+ * @brief Display a pfi header in human-readable form.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return always 0.
+ *
+ * @note Prints out that it is not implemented and whom you should
+ * contact if you need it urgently!.
+ */
+int pfi_header_dump (FILE *out, pfi_header head);
+
+
+/*
+ * @brief Iterates over a stream of pfi files. The iterator function
+ * must advance the file pointer in FILE *in to the next pfi
+ * header. Function exists on feof(in).
+ *
+ * @param in input file descriptor, must be open and valid.
+ * @param func iterator function called when pfi header could be
+ * read and was validated. The function must return 0 on
+ * success.
+ * @return See pfi_header_init and pfi_header_read.
+ * PFI_EINVAL : func is not valid
+ * 0 ok.
+ */
+typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data);
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __pfi_h */
diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c
new file mode 100644
index 0000000..a8c76a3
--- /dev/null
+++ b/ubi-utils/src/pfi2bin.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Convert a PFI file (partial flash image) into a plain binary file.
+ * This tool can be used to prepare the data to be burned into flash
+ * chips in a manufacturing step where the flashes are written before
+ * being soldered onto the hardware. For NAND images another step is
+ * required to add the right OOB data to the binary image.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <ubigen.h>
+#include <mtd/ubi-header.h>
+#include <mtd_swab.h>
+
+#include "config.h"
+#include "list.h"
+#include "error.h"
+#include "reader.h"
+#include "peb.h"
+#include "crc32.h"
+
+#define PROGRAM_VERSION "1.4"
+
+#define MAX_FNAME 255
+#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */
+#define ERR_BUF_SIZE 1024
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+static char err_buf[ERR_BUF_SIZE];
+
+/*
+ * Data used to buffer raw blocks which have to be
+ * located at a specific point inside the generated RAW file
+ */
+
+typedef enum action_t {
+ ACT_NOTHING = 0x00000000,
+ ACT_RAW = 0x00000001,
+} action_t;
+
+static const char copyright [] __attribute__((unused)) =
+ "(c) Copyright IBM Corp 2006\n";
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pfi2bin - a tool to convert PFI files into binary images.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright\n"
+" -v, --verbose Print more information.\n"
+"\n"
+" Input:\n"
+" -j, --platform=pdd-file PDD information which contains the card settings.\n"
+"\n"
+" Output:\n"
+" -o, --output=filename Outputfile, default: stdout.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n"
+" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n"
+" [--usage] [--version] pfifile\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct io {
+ FILE* fp_pdd; /* a FilePointer to the PDD data */
+ FILE* fp_pfi; /* a FilePointer to the PFI input stream */
+ FILE* fp_out; /* a FilePointer to the output stream */
+} *io_t;
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+ const char *f_in_pfi;
+ const char *f_in_pdd;
+ const char *f_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* common settings */
+ case 'v': /* --verbose=<level> */
+ args->verbose = 1;
+ break;
+
+ case 'c': /* --copyright */
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+
+ case 'j': /* --platform */
+ args->f_in_pdd = optarg;
+ break;
+
+ case 'o': /* --output */
+ args->f_out = optarg;
+ break;
+
+ case '?': /* help */
+ printf("pfi2bin [OPTION...] pfifile\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->f_in_pfi = argv[optind++];
+
+ return 0;
+}
+
+
+static size_t
+byte_to_blk(size_t byte, size_t blk_size)
+{
+ return (byte % blk_size) == 0
+ ? byte / blk_size
+ : byte / blk_size + 1;
+}
+
+
+
+
+/**
+ * @precondition IO: File stream points to first byte of RAW data.
+ * @postcondition IO: File stream points to first byte of next
+ * or EOF.
+ */
+static int
+memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs,
+ io_t io)
+{
+ int rc = 0;
+ uint32_t i;
+
+ size_t read, to_read, eb_num;
+ size_t bytes_left;
+ list_t pebs = *raw_pebs;
+ peb_t peb = NULL;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < pfi_raw->starts_size; i++) {
+ bytes_left = pfi_raw->data_size;
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size);
+ while (bytes_left) {
+ to_read = MIN(bytes_left, pdd->eb_size);
+ rc = peb_new(eb_num++, pdd->eb_size, &peb);
+ if (rc != 0)
+ goto err;
+ read = fread(peb->data, 1, to_read, io->fp_pfi);
+ if (read != to_read) {
+ rc = -EIO;
+ goto err;
+ }
+ pebs = append_elem(peb, pebs);
+ bytes_left -= read;
+ }
+
+ }
+ *raw_pebs = pebs;
+ return 0;
+err:
+ pebs = remove_all((free_func_t)&peb_free, pebs);
+ return rc;
+}
+
+static int
+convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vtbl_record *vol_tab,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ uint32_t i, j;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ ubi_info_t u;
+ size_t leb_total = 0;
+ uint8_t vol_type;
+
+ switch (ubi->type) {
+ case pfi_ubi_static:
+ vol_type = UBI_VID_STATIC; break;
+ case pfi_ubi_dynamic:
+ vol_type = UBI_VID_DYNAMIC; break;
+ default:
+ vol_type = UBI_VID_DYNAMIC;
+ }
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < ubi->ids_size; i++) {
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_create(&u, ubi->ids[i], vol_type,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ ubi->alignment, UBI_VERSION,
+ pdd->vid_hdr_offset, 0, ubi->data_size,
+ io->fp_pfi, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ j = 0;
+ while(j < leb_total) {
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ /* memorize volume table entry */
+ rc = ubigen_set_lvol_rec(u, ubi->size,
+ ubi->names[i],
+ (void*) &vol_tab[ubi->ids[i]]);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+ }
+
+ peb_free(&cmp_peb);
+ return 0;
+
+err:
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ return rc;
+}
+
+
+static FILE*
+my_fmemopen (void *buf, size_t size, const char *opentype)
+{
+ FILE* f;
+ size_t ret;
+
+ assert(strcmp(opentype, "r") == 0);
+
+ f = tmpfile();
+ ret = fwrite(buf, 1, size, f);
+ rewind(f);
+
+ return f;
+}
+
+/**
+ * @brief Builds a UBI volume table from a volume entry list.
+ * @return 0 On success.
+ * else Error.
+ */
+static int
+write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vtbl_record *vol_tab, size_t vol_tab_size,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ ubi_info_t u;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ size_t leb_size, leb_total, j = 0;
+ uint8_t *ptr = NULL;
+ FILE* fp_leb = NULL;
+ int vt_slots;
+ size_t vol_tab_size_limit;
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ /* @FIXME: Artem creates one volume with 2 LEBs.
+ * IMO 2 volumes would be more convenient. In order
+ * to get 2 reserved LEBs from ubigen, I have to
+ * introduce this stupid mechanism. Until no final
+ * decision of the VTAB structure is made... Good enough.
+ */
+ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION,
+ pdd->vid_hdr_offset, UBI_COMPAT_REJECT,
+ vol_tab_size, stdin, io->fp_out);
+ /* @FIXME stdin for fp_in is a hack */
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_size(u, &leb_size);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+
+ /*
+ * The number of supported volumes is restricted by the eraseblock size
+ * and by the UBI_MAX_VOLUMES constant.
+ */
+ vt_slots = leb_size / UBI_VTBL_RECORD_SIZE;
+ if (vt_slots > UBI_MAX_VOLUMES)
+ vt_slots = UBI_MAX_VOLUMES;
+ vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE;
+
+ ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t));
+ if (ptr == NULL)
+ goto err;
+
+ memset(ptr, 0xff, leb_size);
+ memcpy(ptr, vol_tab, vol_tab_size_limit);
+ fp_leb = my_fmemopen(ptr, leb_size, "r");
+
+ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION, pdd->vid_hdr_offset,
+ UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS,
+ fp_leb, io->fp_out);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(fp_leb);
+ while(j < leb_total) {
+ rc = fseek(fp_leb, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ free(ptr);
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ fclose(fp_leb);
+ return rc;
+}
+
+static int
+write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written,
+ FILE* fp_out)
+{
+ int rc = 0;
+ uint32_t j, delta;
+ list_t ptr;
+ peb_t empty_eb, peb;
+
+ /* create an empty 0xff EB (for padding) */
+ rc = peb_new(0, pdd->eb_size, &empty_eb);
+
+ foreach(peb, ptr, raw_blocks) {
+ if (peb->num < *ebs_written) {
+ continue; /* omit blocks which
+ are already passed */
+ }
+
+ if (peb->num < *ebs_written) {
+ err_msg("eb_num: %d\n", peb->num);
+ err_msg("Bug: This should never happen. %d %s",
+ __LINE__, __FILE__);
+ goto err;
+ }
+
+ delta = peb->num - *ebs_written;
+ if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) {
+ err_msg("RAW block outside of flash_size.");
+ goto err;
+ }
+ for (j = 0; j < delta; j++) {
+ rc = peb_write(fp_out, empty_eb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ rc = peb_write(fp_out, peb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ peb_free(&empty_eb);
+ return rc;
+}
+
+static int
+init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size)
+{
+ uint32_t crc;
+ size_t i;
+ struct ubi_vtbl_record* res = NULL;
+
+ *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
+
+ res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size);
+ if (vol_tab == NULL) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < UBI_MAX_VOLUMES; i++) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ &(res[i]), UBI_VTBL_RECORD_SIZE_CRC);
+ res[i].crc = cpu_to_be32(crc);
+ }
+
+ *vol_tab = res;
+ return 0;
+}
+
+static int
+create_raw(io_t io)
+{
+ int rc = 0;
+ size_t ebs_written = 0; /* eraseblocks written already... */
+ size_t vol_tab_size;
+ list_t ptr;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+ list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */
+
+ struct ubi_vtbl_record *vol_tab = NULL;
+ pdd_data_t pdd = NULL;
+
+ rc = init_vol_tab (&vol_tab, &vol_tab_size);
+ if (rc != 0) {
+ err_msg("Cannot initialize volume table.");
+ goto err;
+ }
+
+ rc = read_pdd_data(io->fp_pdd, &pdd,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read necessary pdd_data: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read pfi header: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ pfi_raw_t pfi_raw;
+ foreach(pfi_raw, ptr, pfi_raws) {
+ rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs,
+ io);
+ if (rc != 0) {
+ err_msg("Cannot create raw_block in mem. rc: %d\n",
+ rc);
+ goto err;
+ }
+ }
+
+ pfi_ubi_t pfi_ubi;
+ foreach(pfi_ubi, ptr, pfi_ubis) {
+ rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs,
+ vol_tab, &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot convert UBI volume. rc: %d\n", rc);
+ goto err;
+ }
+ }
+
+ rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size,
+ &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot write UBI volume table. rc: %d\n", rc);
+ goto err;
+ }
+
+ rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ if (io->fp_out != stdout)
+ info_msg("Physical eraseblocks written: %8d\n", ebs_written);
+err:
+ free(vol_tab);
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs);
+ free_pdd_data(&pdd);
+ return rc;
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+open_io_handle(myargs *args, io_t io)
+{
+ /* set PDD input */
+ io->fp_pdd = fopen(args->f_in_pdd, "r");
+ if (io->fp_pdd == NULL) {
+ err_sys("Cannot open: %s", args->f_in_pdd);
+ }
+
+ /* set PFI input */
+ io->fp_pfi = fopen(args->f_in_pfi, "r");
+ if (io->fp_pfi == NULL) {
+ err_sys("Cannot open PFI input file: %s", args->f_in_pfi);
+ }
+
+ /* set output prefix */
+ if (strcmp(args->f_out,"") == 0)
+ io->fp_out = stdout;
+ else {
+ io->fp_out = fopen(args->f_out, "wb");
+ if (io->fp_out == NULL) {
+ err_sys("Cannot open output file: %s", args->f_out);
+ }
+ }
+}
+
+static void
+close_io_handle(io_t io)
+{
+ if (fclose(io->fp_pdd) != 0) {
+ err_sys("Cannot close PDD file.");
+ }
+ if (fclose(io->fp_pfi) != 0) {
+ err_sys("Cannot close PFI file.");
+ }
+ if (io->fp_out != stdout) {
+ if (fclose(io->fp_out) != 0) {
+ err_sys("Cannot close output file.");
+ }
+ }
+
+ io->fp_pdd = NULL;
+ io->fp_pfi = NULL;
+ io->fp_out = NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rc = 0;
+
+ ubigen_init();
+ init_crc32_table(crc32_table);
+
+ struct io io = {NULL, NULL, NULL};
+ myargs args = {
+ .action = ACT_RAW,
+ .verbose = 0,
+
+ .f_in_pfi = "",
+ .f_in_pdd = "",
+ .f_out = "",
+
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (strcmp(args.f_in_pfi, "") == 0) {
+ err_quit("No PFI input file specified!");
+ }
+
+ if (strcmp(args.f_in_pdd, "") == 0) {
+ err_quit("No PDD input file specified!");
+ }
+
+ open_io_handle(&args, &io);
+
+ info_msg("[ Creating RAW...");
+ rc = create_raw(&io);
+ if (rc != 0) {
+ err_msg("Creating RAW failed.");
+ goto err;
+ }
+
+err:
+ close_io_handle(&io);
+ if (rc != 0) {
+ remove(args.f_out);
+ }
+
+ return rc;
+}
diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c
new file mode 100644
index 0000000..754fe33
--- /dev/null
+++ b/ubi-utils/src/pfiflash.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ * Frank Haverkamp
+ *
+ * Process a PFI (partial flash image) and write the data to the
+ * specified UBI volumes. This tool is intended to be used for system
+ * update using PFI files.
+ *
+ * 1.1 fixed output to stderr and stdout in logfile mode.
+ * 1.2 updated.
+ * 1.3 removed argp parsing to be able to use uClib.
+ * 1.4 Minor cleanups.
+ * 1.5 Forgot to delete raw block before updating it.
+ * 1.6 Migrated to new libubi.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pfiflash.h>
+#undef DEBUG
+#include "error.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.6"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "pfiflash - a tool for updating a controller with PFI files.\n";
+
+static const char *optionsstr =
+" Standard options:\n"
+" -c, --copyright Print copyright information.\n"
+" -l, --logfile=<file> Write a logfile to <file>.\n"
+" -v, --verbose Be verbose during program execution.\n"
+"\n"
+" Process options:\n"
+" -C, --complete Execute a complete system update. Updates both\n"
+" sides.\n"
+" -p, --pdd-update=<type> Specify the pdd-update algorithm. <type> is either\n"
+" 'keep', 'merge' or 'overwrite'.\n"
+" -r, --raw-flash=<dev> Flash the raw data. Use the specified mtd device.\n"
+" -s, --side=<seqnum> Select the side which shall be updated.\n"
+" -x, --compare Only compare on-flash and pfi data, print info if\n"
+" an update is neccessary and return appropriate\n"
+" error code.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfiflash [-cvC?V] [-l <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n"
+" [--copyright] [--logfile=<file>] [--verbose] [--complete]\n"
+" [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\n"
+" [--compare] [--help] [--usage] [--version] [pfifile]\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' },
+ { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ int verbose;
+ const char *logfile;
+ const char *raw_dev;
+
+ pdd_handling_t pdd_handling;
+ int seqnum;
+ int compare;
+ int complete;
+
+ FILE* fp_in;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static pdd_handling_t
+get_pdd_handling(const char* str)
+{
+ if (strcmp(str, "keep") == 0) {
+ return PDD_KEEP;
+ }
+ if (strcmp(str, "merge") == 0) {
+ return PDD_MERGE;
+ }
+ if (strcmp(str, "overwrite") == 0) {
+ return PDD_OVERWRITE;
+ }
+
+ return -1;
+}
+
+static int
+get_update_seqnum(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cl:vCp:r:s:x?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* standard options */
+ case 'c':
+ err_msg("%s\n", copyright);
+ exit(0);
+ break;
+ case 'v':
+ args->verbose = 1;
+ break;
+ case 'l':
+ args->logfile = optarg;
+ break;
+ /* process options */
+ case 'C':
+ args->complete = 1;
+ break;
+ case 'p':
+ args->pdd_handling = get_pdd_handling(optarg);
+ if ((int)args->pdd_handling < 0) {
+ err_quit("Unknown PDD handling: %s.\n"
+ "Please use either "
+ "'keep', 'merge' or"
+ "'overwrite'.\n'");
+ }
+ break;
+ case 's':
+ args->seqnum = get_update_seqnum(optarg);
+ if (args->seqnum < 0) {
+ err_quit("Unsupported side: %s.\n"
+ "Supported sides are '0' "
+ "and '1'\n", optarg);
+ }
+ break;
+ case 'x':
+ args->compare = 1;
+ break;
+ case 'r':
+ args->raw_dev = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pfiflash [OPTION...] [pfifile]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "r");
+ if ((args->fp_in) == NULL) {
+ err_sys("Cannot open PFI file %s for input",
+ argv[optind]);
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = 0;
+ char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE];
+ memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE);
+
+ myargs args = {
+ .verbose = 0,
+ .seqnum = -1,
+ .compare = 0,
+ .complete = 0,
+ .logfile = NULL, /* "/tmp/pfiflash.log", */
+ .pdd_handling = PDD_KEEP,
+ .fp_in = stdin,
+ .raw_dev = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ error_initlog(args.logfile);
+
+ if (!args.fp_in) {
+ rc = -1;
+ snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE,
+ "No PFI input file specified!\n");
+ goto err;
+ }
+
+ rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum,
+ args.compare, args.pdd_handling, args.raw_dev, err_buf,
+ PFIFLASH_MAX_ERR_BUF_SIZE);
+ if (rc < 0) {
+ goto err_fp;
+ }
+
+ err_fp:
+ if (args.fp_in != stdin)
+ fclose(args.fp_in);
+ err:
+ if (rc != 0)
+ err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc);
+ return rc;
+}
diff --git a/ubi-utils/src/pfiflash.h b/ubi-utils/src/pfiflash.h
new file mode 100644
index 0000000..039705d
--- /dev/null
+++ b/ubi-utils/src/pfiflash.h
@@ -0,0 +1,76 @@
+#ifndef __PFIFLASH_H__
+#define __PFIFLASH_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ *
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ *
+ * @brief The pfiflash library offers an interface for using the
+ * pfiflash * utility.
+ */
+
+#include <stdio.h> /* FILE */
+
+#define PFIFLASH_MAX_ERR_BUF_SIZE 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum pdd_handling_t
+{
+ PDD_KEEP = 0,
+ PDD_MERGE,
+ PDD_OVERWRITE,
+ PDD_HANDLING_NUM, /* always the last item */
+} pdd_handling_t; /**< Possible PDD handle algorithms. */
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param compare [0|1] Compare contents.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param rawdev Device to use for raw flashing
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PFIFLASH_H__ */
diff --git a/ubi-utils/src/pfiflash_error.h b/ubi-utils/src/pfiflash_error.h
new file mode 100644
index 0000000..0f27f4a
--- /dev/null
+++ b/ubi-utils/src/pfiflash_error.h
@@ -0,0 +1,75 @@
+#ifndef __PFIFLASH_ERROR_H__
+#define __PFIFLASH_ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * Author: Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <arnez@de.ibm.com>
+ */
+
+enum pfiflash_err {
+ PFIFLASH_ERR_EOF = 1,
+ PFIFLASH_ERR_FIO,
+ PFIFLASH_ERR_UBI_OPEN,
+ PFIFLASH_ERR_UBI_CLOSE,
+ PFIFLASH_ERR_UBI_MKVOL,
+ PFIFLASH_ERR_UBI_RMVOL,
+ PFIFLASH_ERR_UBI_VOL_UPDATE,
+ PFIFLASH_ERR_UBI_VOL_FOPEN,
+ PFIFLASH_ERR_UBI_UNKNOWN,
+ PFIFLASH_ERR_UBI_VID_OOB,
+ PFIFLASH_ERR_BOOTENV_CREATE,
+ PFIFLASH_ERR_BOOTENV_READ,
+ PFIFLASH_ERR_BOOTENV_SIZE,
+ PFIFLASH_ERR_BOOTENV_WRITE,
+ PFIFLASH_ERR_PDD_UNKNOWN,
+ PFIFLASH_ERR_MTD_OPEN,
+ PFIFLASH_ERR_MTD_CLOSE,
+ PFIFLASH_ERR_CRC_CHECK,
+ PFIFLASH_ERR_MTD_ERASE,
+ PFIFLASH_ERR_COMPARE,
+ PFIFLASH_CMP_DIFF
+};
+
+const char *const PFIFLASH_ERRSTR[] = {
+ "",
+ "unexpected EOF",
+ "file I/O error",
+ "couldn't open UBI",
+ "couldn't close UBI",
+ "couldn't make UBI volume %d",
+ "couldn't remove UBI volume %d",
+ "couldn't update UBI volume %d",
+ "couldn't open UBI volume %d",
+ "unknown UBI operation",
+ "PFI data contains out of bounds UBI id %d",
+ "couldn't create bootenv%s",
+ "couldn't read bootenv",
+ "couldn't resize bootenv",
+ "couldn't write bootenv on ubi%d_%d",
+ "unknown PDD handling algorithm",
+ "couldn't open MTD device %s",
+ "couldn't close MTD device %s",
+ "CRC check failed: given=0x%08x, calculated=0x%08x",
+ "couldn't erase raw mtd region",
+ "couldn't compare volumes",
+ "on-flash data differ from pfi data, update is neccessary"
+};
+
+#endif /* __PFIFLASH_ERROR_H__ */
diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c
new file mode 100644
index 0000000..0ea8c6d
--- /dev/null
+++ b/ubi-utils/src/reader.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read in PFI (partial flash image) data and store it into internal
+ * data structures for further processing. Take also care about
+ * special handling if the data contains PDD (platform description
+ * data/boot-parameters).
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "bootenv.h"
+#include "reader.h"
+
+#define __unused __attribute__((unused))
+
+/* @FIXME hard coded offsets right now - get them from Artem? */
+#define NAND2048_DEFAULT_VID_HDR_OFF 1984
+#define NAND512_DEFAULT_VID_HDR_OFF 448
+#define NOR_DEFAULT_VID_HDR_OFF 64
+
+#define EBUF_PFI(fmt...) \
+ do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \
+ snprintf(err_buf + i, err_buf_size - i, fmt); \
+ } while (0)
+
+#define EBUF(fmt...) \
+ do { snprintf(err_buf, err_buf_size, fmt); } while (0)
+
+
+int
+read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ bootenv_t pdd = NULL;
+ pdd_data_t res = NULL;
+ const char* value;
+
+ res = (pdd_data_t) malloc(sizeof(struct pdd_data));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ rc = bootenv_create(&pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_read_txt(fp_pdd, pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_get(pdd, "flash_type", &value);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (strcmp(value, "NAND") == 0) {
+
+ rc = bootenv_get_num(pdd, "flash_page_size",
+ &(res->flash_page_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_page_size' from pdd.");
+ goto err;
+ }
+ res->flash_type = NAND_FLASH;
+
+ switch (res->flash_page_size) {
+ case 512:
+ res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF;
+ break;
+ case 2048:
+ res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF;
+ break;
+ default:
+ EBUF("Unsupported 'flash_page_size' %d.",
+ res->flash_page_size);
+ goto err;
+ }
+ }
+ else if (strcmp(value, "NOR") == 0){
+ res->flash_type = NOR_FLASH;
+ res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF;
+ }
+ else {
+ snprintf(err_buf, err_buf_size,
+ "Unkown flash type: %s", value);
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_eraseblock_size",
+ &(res->eb_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_eraseblock_size' from pdd.");
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_size",
+ &(res->flash_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_size' from pdd.");
+ goto err;
+ }
+
+ goto out;
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_destroy(&pdd);
+ *pdd_data = res;
+ return rc;
+}
+
+int
+read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw,
+ const char* label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t raw_start_list = NULL;
+ pfi_raw_t res;
+ size_t size;
+
+ res = (pfi_raw_t) malloc(sizeof(struct pfi_raw));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "raw_starts",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'raw_starts' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&raw_start_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(raw_start_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(raw_start_list,
+ &size, &(res->starts));
+ res->starts_size = size;
+
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_list_destroy(&raw_start_list);
+ *pfi_raw = res;
+ return rc;
+}
+
+int
+read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi,
+ const char *label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ const char** tmp_names = NULL;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t ubi_id_list = NULL;
+ bootenv_list_t ubi_name_list = NULL;
+ pfi_ubi_t res;
+ uint32_t i;
+ size_t size;
+
+ res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_ids' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&ubi_id_list);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_list_create(&ubi_name_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_id_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(ubi_id_list, &size,
+ &(res->ids));
+ res->ids_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->ids_size == 0) {
+ rc = -1;
+ EBUF_PFI("Sanity check failed: No ubi_ids specified.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_type",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_type' from PFI.");
+ goto err;
+ }
+ if (strcmp(tmp_str, "static") == 0)
+ res->type = pfi_ubi_static;
+ else if (strcmp(tmp_str, "dynamic") == 0)
+ res->type = pfi_ubi_dynamic;
+ else {
+ EBUF_PFI("Unknown ubi_type in PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_alignment' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_names",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_names' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_name_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+ rc = bootenv_list_to_vector(ubi_name_list, &size,
+ &(tmp_names));
+ res->names_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create string array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->names_size != res->ids_size) {
+ EBUF_PFI("Sanity check failed: ubi_ids list does not match "
+ "sizeof ubi_names list.");
+ rc = -1;
+ }
+
+ /* copy tmp_names to own structure */
+ res->names = (char**) calloc(1, res->names_size * sizeof (char*));
+ if (res->names == NULL)
+ goto err;
+
+ for (i = 0; i < res->names_size; i++) {
+ res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char));
+ if (res->names[i] == NULL)
+ goto err;
+ strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1);
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ if (res->names) {
+ for (i = 0; i < res->names_size; i++) {
+ if (res->names[i]) {
+ free(res->names[i]);
+ }
+ }
+ free(res->names);
+ }
+ if (res->ids) {
+ free(res->ids);
+ }
+ free(res);
+ res = NULL;
+ }
+
+ out:
+ bootenv_list_destroy(&ubi_id_list);
+ bootenv_list_destroy(&ubi_name_list);
+ if (tmp_names != NULL)
+ free(tmp_names);
+ *pfi_ubi = res;
+ return rc;
+}
+
+
+int
+free_pdd_data(pdd_data_t* pdd_data)
+{
+ if (*pdd_data) {
+ free(*pdd_data);
+ }
+ *pdd_data = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_raw(pfi_raw_t* pfi_raw)
+{
+ pfi_raw_t tmp = *pfi_raw;
+ if (tmp) {
+ if (tmp->starts)
+ free(tmp->starts);
+ free(tmp);
+ }
+ *pfi_raw = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_ubi(pfi_ubi_t* pfi_ubi)
+{
+ size_t i;
+ pfi_ubi_t tmp = *pfi_ubi;
+ if (tmp) {
+ if (tmp->ids)
+ free(tmp->ids);
+ if (tmp->names) {
+ for (i = 0; i < tmp->names_size; i++) {
+ if (tmp->names[i]) {
+ free(tmp->names[i]);
+ }
+ }
+ free(tmp->names);
+ }
+ free(tmp);
+ }
+ *pfi_ubi = NULL;
+
+ return 0;
+}
+
+
+int
+read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char mode[PFI_KEYWORD_LEN];
+ char label[PFI_LABEL_LEN];
+
+ *pfi_raws = mk_empty(); pfi_raw_t raw = NULL;
+ *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL;
+ pfi_header pfi_header = NULL;
+
+ /* read all headers from PFI and store them in lists */
+ rc = pfi_header_init(&pfi_header);
+ if (rc != 0) {
+ EBUF("Cannot initialize pfi header.");
+ goto err;
+ }
+ while ((rc == 0) && !feof(fp_pfi)) {
+ rc = pfi_header_read(fp_pfi, pfi_header);
+ if (rc != 0) {
+ if (rc == PFI_DATA_START) {
+ rc = 0;
+ break; /* data section starts,
+ all headers read */
+ }
+ else {
+ goto err;
+ }
+ }
+ rc = pfi_header_getstring(pfi_header, "label", label,
+ PFI_LABEL_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'label' from PFI.");
+ goto err;
+ }
+ rc = pfi_header_getstring(pfi_header, "mode", mode,
+ PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'mode' from PFI.");
+ goto err;
+ }
+ if (strcmp(mode, "ubi") == 0) {
+ rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_ubis = append_elem(ubi, *pfi_ubis);
+ }
+ else if (strcmp(mode, "raw") == 0) {
+ rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_raws = append_elem(raw, *pfi_raws);
+ }
+ else {
+ EBUF("Recvieved unknown mode from PFI: %s", mode);
+ goto err;
+ }
+ }
+ goto out;
+
+ err:
+ *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws);
+ *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis);
+ out:
+ pfi_header_destroy(&pfi_header);
+ return rc;
+
+}
diff --git a/ubi-utils/src/reader.h b/ubi-utils/src/reader.h
new file mode 100644
index 0000000..715e464
--- /dev/null
+++ b/ubi-utils/src/reader.h
@@ -0,0 +1,87 @@
+#ifndef __READER_H__
+#define __READER_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read Platform Description Data (PDD).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "pfi.h"
+#include "bootenv.h"
+#include "list.h"
+
+typedef enum flash_type_t {
+ NAND_FLASH = 0,
+ NOR_FLASH,
+} flash_type_t;
+
+typedef struct pdd_data *pdd_data_t;
+typedef struct pfi_raw *pfi_raw_t;
+typedef struct pfi_ubi *pfi_ubi_t;
+
+struct pdd_data {
+ uint32_t flash_size;
+ uint32_t flash_page_size;
+ uint32_t eb_size;
+ uint32_t vid_hdr_offset;
+ flash_type_t flash_type;
+};
+
+struct pfi_raw {
+ uint32_t data_size;
+ uint32_t *starts;
+ uint32_t starts_size;
+ uint32_t crc;
+};
+
+struct pfi_ubi {
+ uint32_t data_size;
+ uint32_t alignment;
+ uint32_t *ids;
+ uint32_t ids_size;
+ char **names;
+ uint32_t names_size;
+ uint32_t size;
+ enum { pfi_ubi_dynamic, pfi_ubi_static } type;
+ int curr_seqnum; /* specifies the seqnum taken in an update,
+ default: 0 (used by pfiflash, ubimirror) */
+ uint32_t crc;
+};
+
+int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data,
+ char *err_buf, size_t err_buf_size);
+int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw,
+ const char *label, char *err_buf, size_t err_buf_size);
+int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi,
+ const char *label, char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Reads all pfi headers into list structures, separated by
+ * RAW and UBI sections.
+ */
+int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size);
+int free_pdd_data(pdd_data_t *pdd_data);
+int free_pfi_raw(pfi_raw_t *raw_pfi);
+int free_pfi_ubi(pfi_ubi_t *pfi_ubi);
+
+#endif /* __READER_H__ */
diff --git a/ubi-utils/src/ubiattach.c b/ubi-utils/src/ubiattach.c
deleted file mode 100644
index b3d768a..0000000
--- a/ubi-utils/src/ubiattach.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2007 Nokia Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * 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., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * An utility to attach MTD devices to UBI.
- *
- * Author: Artem Bityutskiy
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libubi.h>
-#include "common.h"
-
-#define PROGRAM_VERSION "1.0"
-#define PROGRAM_NAME "ubiattach"
-
-/* The variables below are set by command line arguments */
-struct args {
- int devn;
- int mtdn;
- int vidoffs;
- const char *node;
-};
-
-static struct args args = {
- .devn = UBI_DEV_NUM_AUTO,
- .mtdn = -1,
- .vidoffs = 0,
- .node = NULL,
-};
-
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to attach MTD device to UBI.";
-
-static const char *optionsstr =
-"-d, --devn=<UBI device number> the number to assign to the newly created UBI device\n"
-" (the number is assigned automatically if this is not\n"
-" specified\n"
-"-m, --mtdn=<MTD device number> MTD device number to attach\n"
-"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n"
-" really know what you do and the optimal defaults will\n"
-" be used)\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
-
-static const char *usage =
-"Usage: " PROGRAM_NAME "<UBI control device node file name> [-m <MTD device number>] [-d <UBI device number>]\n"
-"\t\t[--mtdn=<MTD device number>] [--devn <UBI device number>]\n"
-"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n"
-"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n"
-" and create UBI device number 3 (ubi3)";
-
-static const struct option long_options[] = {
- { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
- { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
- { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0},
-};
-
-static int parse_opt(int argc, char * const argv[])
-{
- while (1) {
- int key;
- char *endp;
-
- key = getopt_long(argc, argv, "m:d:OhV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 'd':
- args.devn = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.devn < 0)
- return errmsg("bad UBI device number: \"%s\"", optarg);
-
- break;
-
- case 'm':
- args.mtdn = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.mtdn < 0)
- return errmsg("bad MTD device number: \"%s\"", optarg);
-
- break;
-
- case 'o':
- args.vidoffs = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.vidoffs <= 0)
- return errmsg("bad VID header offset: \"%s\"", optarg);
-
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- case ':':
- return errmsg("parameter is missing");
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
- }
-
- if (optind == argc)
- return errmsg("UBI control device name was not specified (use -h for help)");
- else if (optind != argc - 1)
- return errmsg("more then one UBI control device specified (use -h for help)");
-
- if (args.mtdn == -1)
- return errmsg("MTD device number was not specified (use -h for help)");
-
- args.node = argv[optind];
- return 0;
-}
-
-int main(int argc, char * const argv[])
-{
- int err;
- libubi_t libubi;
- struct ubi_info ubi_info;
- struct ubi_dev_info dev_info;
- struct ubi_attach_request req;
-
- err = parse_opt(argc, argv);
- if (err)
- return -1;
-
- libubi = libubi_open();
- if (libubi == NULL)
- return sys_errmsg("cannot open libubi");
-
- /*
- * Make sure the kernel is fresh enough and this feature is supported.
- */
- err = ubi_get_info(libubi, &ubi_info);
- if (err) {
- sys_errmsg("cannot get UBI information");
- goto out_libubi;
- }
-
- if (ubi_info.ctrl_major == -1) {
- errmsg("MTD attach/detach feature is not supported by your kernel");
- goto out_libubi;
- }
-
- req.dev_num = args.devn;
- req.mtd_num = args.mtdn;
- req.vid_hdr_offset = args.vidoffs;
-
- err = ubi_attach_mtd(libubi, args.node, &req);
- if (err) {
- sys_errmsg("cannot attach mtd%d", args.mtdn);
- goto out_libubi;
- }
-
- /* Print some information about the new UBI device */
- err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info);
- if (err) {
- sys_errmsg("cannot get information about newly created UBI device");
- goto out_libubi;
- }
-
- printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs);
- ubiutils_print_bytes(dev_info.total_bytes, 0);
- printf("), available %d LEBs (", dev_info.avail_lebs);
- ubiutils_print_bytes(dev_info.avail_bytes, 0);
- printf("), LEB size ");
- ubiutils_print_bytes(dev_info.leb_size, 1);
- printf("\n");
-
- libubi_close(libubi);
- return 0;
-
-out_libubi:
- libubi_close(libubi);
- return -1;
-}
diff --git a/ubi-utils/src/ubicrc32.c b/ubi-utils/src/ubicrc32.c
index 5c7a8ba..7e3f045 100644
--- a/ubi-utils/src/ubicrc32.c
+++ b/ubi-utils/src/ubicrc32.c
@@ -14,12 +14,10 @@
* 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.
- */
-
-/*
- * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image.
*
* Author: Oliver Lohmann
+ *
+ * Calculate CRC32 with UBI start value for a given binary image.
*/
#include <stdio.h>
@@ -28,98 +26,116 @@
#include <getopt.h>
#include <argp.h>
#include <unistd.h>
+#include <errno.h>
#include <mtd/ubi-header.h>
+#include "config.h"
#include "crc32.h"
-#include "common.h"
#define BUFSIZE 4096
-#define PROGRAM_VERSION "1.2"
-#define PROGRAM_NAME "ubicrc32"
+const char *argp_program_version = PACKAGE_VERSION;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+static char doc[] = "\nVersion: " PACKAGE_VERSION "\n"
+ "ubicrc32 - calculates the UBI CRC32 value and prints it to stdout.\n";
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)";
+static const char copyright [] __attribute__((unused)) =
+ "FIXME: insert license type"; /* FIXME */
-static const char *optionsstr =
-"-h, --help print help message\n"
-"-V, --version print program version";
-static const char *usage =
-"Usage: " PROGRAM_NAME " <file to calculate CRC32 for> [-h] [--help]";
+static struct argp_option options[] = {
+ { name: "copyright", key: 'c', arg: NULL, flags: 0,
+ doc: "Print copyright information.",
+ group: 1 },
-static const struct option long_options[] = {
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0},
+ { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 },
};
-static int parse_opt(int argc, char * const argv[])
+typedef struct myargs {
+ FILE* fp_in;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static error_t
+parse_opt(int key, char *arg, struct argp_state *state)
{
- while (1) {
- int key;
-
- key = getopt_long(argc, argv, "hV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- case ':':
- return errmsg("parameter is missing");
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
+ int err = 0;
+
+ myargs *args = state->input;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case ARGP_KEY_ARG:
+ args->fp_in = fopen(arg, "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr,
+ "Cannot open file %s for input\n", arg);
+ exit(1);
+ }
+ args->arg1 = arg;
+ args->options = &state->argv[state->next];
+ state->next = state->argc;
+ break;
+ case ARGP_KEY_END:
+ if (err) {
+ fprintf(stderr, "\n");
+ argp_usage(state);
+ exit(1);
}
+ break;
+ default:
+ return(ARGP_ERR_UNKNOWN);
}
return 0;
}
-int main(int argc, char * const argv[])
-{
- int err = 0;
- uint32_t crc = UBI_CRC32_INIT;
- char buf[BUFSIZE];
- FILE *fp;
-
- if (argc > 1) {
- fp = fopen(argv[1], "r");
- if (!fp)
- return sys_errmsg("cannot open \"%s\"", argv[1]);
- } else
- fp = stdin;
-
- err = parse_opt(argc, argv);
- if (err)
- return err;
-
- while (!feof(fp)) {
- size_t read;
-
- read = fread(buf, 1, BUFSIZE, fp);
- if (ferror(fp)) {
- sys_errmsg("cannot read input file");
- err = -1;
- goto out_close;
+static struct argp argp = {
+ options: options,
+ parser: parse_opt,
+ args_doc: "[file]",
+ doc: doc,
+ children: NULL,
+ help_filter: NULL,
+ argp_domain: NULL,
+};
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ uint32_t crc32_table[256];
+ uint8_t buf[BUFSIZE];
+ size_t read;
+ uint32_t crc32;
+
+ myargs args = {
+ .fp_in = stdin,
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args);
+
+ init_crc32_table(crc32_table);
+ crc32 = UBI_CRC32_INIT;
+ while (!feof(args.fp_in)) {
+ read = fread(buf, 1, BUFSIZE, args.fp_in);
+ if (ferror(args.fp_in)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
}
- crc = crc32(crc, buf, read);
+ crc32 = clc_crc32(crc32_table, crc32, buf, read);
}
- printf("0x%08x\n", crc);
+ if (args.fp_in != stdin) {
+ fclose(args.fp_in);
+ }
-out_close:
- if (fp != stdin)
- fclose(fp);
- return err;
+ fprintf(stdout, "0x%08x\n", crc32);
+ return rc;
}
diff --git a/ubi-utils/src/ubidetach.c b/ubi-utils/src/ubidetach.c
deleted file mode 100644
index cd48368..0000000
--- a/ubi-utils/src/ubidetach.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2007 Nokia Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * 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., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * An utility to delete UBI devices (detach MTD devices from UBI).
- *
- * Author: Artem Bityutskiy
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libubi.h>
-#include "common.h"
-
-#define PROGRAM_VERSION "1.0"
-#define PROGRAM_NAME "ubidetach"
-
-/* The variables below are set by command line arguments */
-struct args {
- int devn;
- int mtdn;
- const char *node;
-};
-
-static struct args args = {
- .devn = UBI_DEV_NUM_AUTO,
- .mtdn = -1,
- .node = NULL,
-};
-
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
-" - a tool to remove UBI devices (detach MTD devices from UBI)";
-
-static const char *optionsstr =
-"-d, --devn=<UBI device number> UBI device number to delete\n"
-"-m, --mtdn=<MTD device number> or altrnatively, MTD device number to detach -\n"
-" this will delete corresponding UBI device\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
-
-static const char *usage =
-"Usage: " PROGRAM_NAME "<UBI control device node file name> [-d <UBI device number>] [-m <MTD device number>]\n"
-"\t\t[--devn <UBI device number>] [--mtdn=<MTD device number>]\n"
-"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n"
-"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)";
-
-static const struct option long_options[] = {
- { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
- { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0},
-};
-
-static int parse_opt(int argc, char * const argv[])
-{
- while (1) {
- int key;
- char *endp;
-
- key = getopt_long(argc, argv, "m:d:hV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 'd':
- args.devn = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.devn < 0)
- return errmsg("bad UBI device number: \"%s\"", optarg);
-
- break;
-
- case 'm':
- args.mtdn = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.mtdn < 0)
- return errmsg("bad MTD device number: \"%s\"", optarg);
-
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- case ':':
- return errmsg("parameter is missing");
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
- }
-
- if (optind == argc)
- return errmsg("UBI control device name was not specified (use -h for help)");
- else if (optind != argc - 1)
- return errmsg("more then one UBI control device specified (use -h for help)");
-
- if (args.mtdn == -1 && args.devn == -1)
- return errmsg("neither MTD nor UBI devices were specified (use -h for help)");
-
- if (args.mtdn != -1 && args.devn != -1)
- return errmsg("specify either MTD or UBI device (use -h for help)");
-
- args.node = argv[optind];
- return 0;
-}
-
-int main(int argc, char * const argv[])
-{
- int err;
- libubi_t libubi;
- struct ubi_info ubi_info;
-
- err = parse_opt(argc, argv);
- if (err)
- return -1;
-
- libubi = libubi_open();
- if (libubi == NULL)
- return sys_errmsg("cannot open libubi");
-
- /*
- * Make sure the kernel is fresh enough and this feature is supported.
- */
- err = ubi_get_info(libubi, &ubi_info);
- if (err) {
- sys_errmsg("cannot get UBI information");
- goto out_libubi;
- }
-
- if (ubi_info.ctrl_major == -1) {
- errmsg("MTD detach/detach feature is not supported by your kernel");
- goto out_libubi;
- }
-
- if (args.devn != -1) {
- err = ubi_remove_dev(libubi, args.node, args.devn);
- if (err) {
- sys_errmsg("cannot remove ubi%d", args.devn);
- goto out_libubi;
- }
- } else {
- err = ubi_detach_mtd(libubi, args.node, args.mtdn);
- if (err) {
- sys_errmsg("cannot detach mtd%d", args.mtdn);
- goto out_libubi;
- }
- }
-
- libubi_close(libubi);
- return 0;
-
-out_libubi:
- libubi_close(libubi);
- return -1;
-}
-
diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c
new file mode 100644
index 0000000..9fcafab
--- /dev/null
+++ b/ubi-utils/src/ubigen.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Tool to add UBI headers to binary images.
+ *
+ * 1.0 Initial version
+ * 1.1 Different CRC32 start value
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <mtd/ubi-header.h>
+
+#include "ubigen.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.3"
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+ ACT_BROKEN_UPDATE = 0x00000002,
+} action_t;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubigen - a tool for adding UBI information to a binary input file.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright Print copyright information.\n"
+" -d, --debug\n"
+" -v, --verbose Print more progress information.\n"
+"\n"
+" UBI Settings:\n"
+" -A, --alignment=<num> Set the alignment size to <num> (default 1).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -B, --blocksize=<num> Set the eraseblock size to <num> (default 128\n"
+" KiB).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -E, --erasecount=<num> Set the erase count to <num> (default 0)\n"
+" -I, --id=<num> The UBI volume id.\n"
+" -O, --offset=<num> Offset from start of an erase block to the UBI\n"
+" volume header.\n"
+" -T, --type=<num> The UBI volume type:\n"
+" 1 = dynamic, 2 = static\n"
+" -X, --setver=<num> Set UBI version number to <num> (default 1)\n"
+"\n"
+" Input/Output:\n"
+" -i, --infile=<filename> Read input from file.\n"
+" -o, --outfile=<filename> Write output to file (default is stdout).\n"
+"\n"
+" Special options:\n"
+" -U, --broken-update=<leb> Create an ubi image which simulates a broken\n"
+" update.\n"
+" <leb> specifies the logical eraseblock number to\n"
+" update.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubigen [-cdv?V] [-A <num>] [-B <num>] [-E <num>] [-I <num>]\n"
+" [-O <num>] [-T <num>] [-X <num>] [-i <filename>] [-o <filename>]\n"
+" [-U <leb>] [--copyright] [--debug] [--verbose] [--alignment=<num>]\n"
+" [--blocksize=<num>] [--erasecount=<num>] [--id=<num>]\n"
+" [--offset=<num>] [--type=<num>] [--setver=<num>]\n"
+" [--infile=<filename>] [--outfile=<filename>]\n"
+" [--broken-update=<leb>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' },
+ { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' },
+ { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' },
+ { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' },
+ { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' },
+ { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+
+ int32_t id;
+ uint8_t type;
+ uint32_t eb_size;
+ uint64_t ec;
+ uint8_t version;
+ uint32_t hdr_offset;
+ uint32_t update_block;
+ uint32_t alignment;
+
+ FILE* fp_in;
+ FILE* fp_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = strtoul(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ result *= 1024;
+ case 'M':
+ result *= 1024;
+ case 'k':
+ case 'K':
+ result *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return result;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int err = 0;
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o': /* output */
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'i': /* input */
+ args->fp_in = fopen(optarg, "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for input\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'v': /* verbose */
+ args->verbose = 1;
+ break;
+
+ case 'B': /* eb_size */
+ args->eb_size =
+ (uint32_t)ustrtoul(optarg, &endp, 0);
+ CHECK_ENDP("B", endp);
+ break;
+ case 'E': /* erasecount */
+ args->ec = (uint64_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("E", endp);
+ break;
+ case 'I': /* id */
+ args->id = (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("I", endp);
+ break;
+ case 'T': /* type */
+ args->type =
+ (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("T", endp);
+ break;
+ case 'X': /* versionnr */
+ args->version =
+ (uint8_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("X", endp);
+ break;
+ case 'O': /* offset for volume hdr */
+ args->hdr_offset =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("O", endp);
+ break;
+
+ case 'U': /* broken update */
+ args->action = ACT_BROKEN_UPDATE;
+ args->update_block =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("U", endp);
+ break;
+
+ case '?': /* help */
+ fprintf(stderr, "Usage: ubigen [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ if (!args->fp_in) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for "
+ "input\n", argv[optind]);
+ exit(1);
+ }
+ }
+ }
+ if (args->id < 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume ID.\n");
+ }
+ if (args->type == 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume type.\n");
+ }
+ if (err) {
+ fprintf(stderr, "%s", usage);
+ exit(1);
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int rc = 0;
+ ubi_info_t u;
+ struct stat file_info;
+ off_t input_len = 0; /* only used in static volumes */
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .verbose = 0,
+
+ .id = -1,
+ .type = 0,
+ .eb_size = 0,
+ .update_block = 0,
+ .ec = 0,
+ .version = 0,
+ .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE),
+ .alignment = 1,
+
+ .fp_in = NULL,
+ .fp_out = stdout,
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ ubigen_init(); /* Init CRC32 table in ubigen */
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (fstat(fileno(args.fp_in), &file_info) != 0) {
+ fprintf(stderr, "Cannot fetch file size "
+ "from input file.\n");
+ }
+ input_len = file_info.st_size;
+
+ rc = ubigen_create(&u, (uint32_t)args.id, args.type,
+ args.eb_size, args.ec, args.alignment,
+ args.version, args.hdr_offset, 0 ,input_len,
+ args.fp_in, args.fp_out);
+
+ if (rc != 0) {
+ fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!args.fp_in || !args.fp_out) {
+ fprintf(stderr, "Input/Output error.\n");
+ exit(EXIT_FAILURE);
+
+ }
+
+ if (args.action & ACT_NORMAL) {
+ rc = ubigen_write_complete(u);
+ }
+ else if (args.action & ACT_BROKEN_UPDATE) {
+ rc = ubigen_write_broken_update(u, args.update_block);
+ }
+ if (rc != 0) {
+ fprintf(stderr, "Error converting input data.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubigen_destroy(&u);
+ return rc;
+}
diff --git a/ubi-utils/src/ubigen.h b/ubi-utils/src/ubigen.h
new file mode 100644
index 0000000..0f43a46
--- /dev/null
+++ b/ubi-utils/src/ubigen.h
@@ -0,0 +1,149 @@
+#ifndef __UBIGEN_H__
+#define __UBIGEN_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to update UBI volumes.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_BLOCKSIZE (128 * 1024)
+#define DEFAULT_PAGESIZE (2*1024)
+
+#define EUBIGEN_INVALID_TYPE 1
+#define EUBIGEN_INVALID_HDR_OFFSET 2
+#define EUBIGEN_INVALID_ALIGNMENT 3
+#define EUBIGEN_TOO_SMALL_EB 4
+#define EUBIGEN_MAX_ERROR 5
+
+
+typedef enum action {
+ NO_ERROR = 0x00000000,
+ BROKEN_HDR_CRC = 0x00000001,
+ BROKEN_DATA_CRC = 0x00000002,
+ BROKEN_DATA_SIZE = 0x00000004,
+ BROKEN_OMIT_BLK = 0x00000008,
+ MARK_AS_UPDATE = 0x00000010,
+} ubigen_action_t;
+
+typedef struct ubi_info *ubi_info_t;
+
+/**
+ * @brief Initialize the internal CRC32 table.
+ * @note Necessary because of the used crc32 function in UBI.
+ * A usage of CRC32, from e.g. zlib will fail.
+ */
+void ubigen_init(void);
+
+/**
+ * @brief Create an ubigen handle.
+ * @param ...
+ * @return 0 On sucess.
+ * else Error.
+ * @note This parameterlist is ugly. But we have to use
+ * two big structs and meta information internally,
+ * filling them would be even uglier.
+ */
+int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset,
+ uint8_t compat_flag, size_t data_size,
+ FILE* fp_in, FILE* fp_out);
+
+/**
+ * @brief Destroy an ubigen handle.
+ * @param u Handle to free.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_destroy(ubi_info_t *u);
+
+/**
+ * @brief Get number of total logical EBs, necessary for the
+ * complete storage of data in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_total(ubi_info_t u, size_t* total);
+
+/**
+ * @brief Get the size in bytes of one logical EB in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_size(ubi_info_t u, size_t* size);
+
+
+/**
+ * @brief Write a logical EB (fits exactly into 1 physical EB).
+ * @param u Handle which holds all necessary data.
+ * @param action Additional operations which shall be applied on this
+ * logical eraseblock. Mostly injecting artifical errors.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_leb(ubi_info_t u, ubigen_action_t action);
+
+/**
+ * @brief Write a complete array of logical eraseblocks at once.
+ * @param u Handle which holds all necessary data.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_complete(ubi_info_t u);
+
+/**
+ * @brief Write a single block which is extracted from the
+ * binary input data.
+ * @param u Handle which holds all necessary data.
+ * @param blk Logical eraseblock which shall hold a inc. copy entry
+ * and a bad data crc.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_broken_update(ubi_info_t u, uint32_t blk);
+
+/**
+ * @brief Use the current ubi_info data and some additional data
+ * to set an UBI volume table entry from it.
+ * @param u Handle which holds some of the necessary data.
+ * @param res_bytes Number of reserved bytes which is stored in the volume
+ * table entry.
+ * @param name A string which shall be used as a volume label.
+ * @param lvol_r A pointer to a volume table entry.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* name, struct ubi_vtbl_record *lvol_rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBIGEN_H__ */
diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c
new file mode 100644
index 0000000..2cc4596
--- /dev/null
+++ b/ubi-utils/src/ubimirror.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ * 1.4 Migrated to new libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.4"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+#define VOL_ARGS_MAX 2
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubimirror - mirrors ubi volumes.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright information.\n"
+" -s, --side=<seqnum> Use the side <seqnum> as source.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubimirror [-c?V] [-s <seqnum>] [--copyright] [--side=<seqnum>]\n"
+" [--help] [--usage] [--version] <source> <destination>\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "(C) IBM Coorporation 2007";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ action_t action;
+ int side;
+ int vol_no; /* index of current volume */
+ /* @FIXME replace by bootenv_list, makes live easier */
+ /* @FIXME remove the constraint of two entries in the array */
+ const char* vol[VOL_ARGS_MAX]; /* comma separated list of src/dst
+ volumes */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cs:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s", copyright);
+ ABORT_ARGP;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %s.\n"
+ "Supported seqnums are '0' "
+ "and '1'\n", optarg);
+ ERR_ARGP;
+ }
+ break;
+ case '?': /* help */
+ err_msg("Usage: ubimirror [OPTION...] "
+ "<source> <destination>\n");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ while (optind < argc) {
+ /* only two entries allowed */
+ if (args->vol_no >= VOL_ARGS_MAX) {
+ err_msg("%s", usage);
+ ERR_ARGP;
+ }
+ args->vol[(args->vol_no)++] = argv[optind++];
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ unsigned int ids[VOL_ARGS_MAX];
+ char err_buf[1024];
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .side = -1,
+ .vol_no = 0,
+ .vol = {"", ""},
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = 127;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 126;
+ goto out;
+ }
+ if (args.vol_no < VOL_ARGS_MAX) {
+ fprintf(stderr, "missing volume number for %s\n",
+ args.vol_no == 0 ? "source and target" : "target");
+ rc = 125;
+ goto out;
+ }
+ for( rc = 0; rc < args.vol_no; ++rc){
+ char *endp;
+ ids[rc] = strtoul(args.vol[rc], &endp, 0);
+ if( *endp != '\0' ){
+ fprintf(stderr, "invalid volume number %s\n",
+ args.vol[rc]);
+ rc = 125;
+ goto out;
+ }
+ }
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no,
+ err_buf, sizeof(err_buf));
+ if( rc ){
+ err_buf[sizeof err_buf - 1] = '\0';
+ fprintf(stderr, err_buf);
+ if( rc < 0 )
+ rc = -rc;
+ }
+ out:
+ err:
+ return rc;
+}
diff --git a/ubi-utils/src/ubimirror.h b/ubi-utils/src/ubimirror.h
new file mode 100644
index 0000000..d7ae2ad
--- /dev/null
+++ b/ubi-utils/src/ubimirror.h
@@ -0,0 +1,66 @@
+#ifndef __UBIMIRROR_H__
+#define __UBIMIRROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * An utility to mirror UBI volumes.
+ */
+
+#include <stdint.h>
+
+/**
+ * @def EUBIMIRROR_SRC_EQ_DST
+ * @brief Given source volume is also in the set of destination volumes.
+ */
+#define EUBIMIRROR_SRC_EQ_DST 20
+
+/**
+ * @def EUBIMIRROR_NO_SRC
+ * @brief The given source volume does not exist.
+ */
+#define EUBIMIRROR_NO_SRC 21
+
+/**
+ * @def EUBIMIRROR_NO_DST
+ * @brief One of the given destination volumes does not exist.
+ */
+#define EUBIMIRROR_NO_DST 22
+
+/**
+ * @brief Mirrors UBI devices from a source device (specified by seqnum)
+ * to n target devices.
+ * @param devno Device number used by the UBI operations.
+ * @param seqnum An index into ids (defines the src_id).
+ * @param ids An array of ids.
+ * @param ids_size The number of entries in the ids array.
+ * @param err_buf A buffer to store verbose error messages.
+ * @param err_buf_size The size of the error buffer.
+ *
+ * @note A seqnum of value < 0 defaults to a seqnum of 0.
+ * @note A seqnum exceeding the range of ids_size defaults to 0.
+ * @note An empty ids list results in a empty stmt.
+ * @pre The UBI volume which shall be used as source volume exists.
+ * @pre The UBI volumes which are defined as destination volumes exist.
+ * @post The content of the UBI volume which was defined as source volume
+ * equals the content of the volumes which were defined as destination.
+ */
+int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size);
+
+#endif /* __UBIMIRROR_H__ */
diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c
index 75c180d..bff6068 100644
--- a/ubi-utils/src/ubimkvol.c
+++ b/ubi-utils/src/ubimkvol.c
@@ -19,8 +19,16 @@
/*
* An utility to create UBI volumes.
*
- * Authors: Artem Bityutskiy <dedekind@infradead.org>
- * Frank Haverkamp <haver@vnet.ibm.com>
+ * Author: Artem B. Bityutskiy <dedekind@linutronix.de>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ *
+ * 1.0 Initial release
+ * 1.1 Does not support erase blocks anymore. This is replaced by
+ * the number of bytes.
+ * 1.2 Reworked the user-interface to use argp.
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ * 1.5 Use a different libubi
*/
#include <stdio.h>
@@ -28,257 +36,315 @@
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <config.h>
#include <libubi.h>
-#include "common.h"
-#define PROGRAM_VERSION "1.6"
-#define PROGRAM_NAME "ubimkvol"
+#define PROGRAM_VERSION "1.5"
-/* The variables below are set by command line arguments */
+/*
+ * The variables below are set by command line arguments.
+ */
struct args {
+ int devn;
int vol_id;
int vol_type;
long long bytes;
- int lebs;
int alignment;
- const char *name;
+ char *name;
int nlen;
- const char *node;
+ char node[256];
int maxavs;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
};
-static struct args args = {
+static struct args myargs = {
.vol_type = UBI_DYNAMIC_VOLUME,
- .bytes = -1,
- .lebs = -1,
+ .devn = -1,
+ .bytes = 0,
.alignment = 1,
.vol_id = UBI_VOL_NUM_AUTO,
.name = NULL,
.nlen = 0,
- .node = NULL,
.maxavs = 0,
};
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to create UBI volumes.";
+static int param_sanity_check(struct args *args, libubi_t libubi);
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubinkvol - make UBI Volume.\n";
static const char *optionsstr =
-"-a, --alignment=<alignment> volume alignment (default is 1)\n"
-"-n, --vol_id=<volume ID> UBI volume ID, if not specified, the volume ID\n"
-" will be assigned automatically\n"
-"-N, --name=<name> volume name\n"
-"-s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB)\n"
-" or megabytes (MiB)\n"
-"-S, --lebs=<LEBs count> alternative way to give volume size in logical\n"
-" eraseblocks\n"
-"-m, --maxavsize set volume size to maximum available size\n"
-"-t, --type=<static|dynamic> volume type (dynamic, static), default is dynamic\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
+" -a, --alignment=<alignment> volume alignment (default is 1)\n"
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id, if not specified, the volume ID\n"
+" will be assigned automatically\n"
+" -N, --name=<name> volume name\n"
+" -s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB)\n"
+" or megabytes (MiB)\n"
+" -m, --maxavsize set volume size to maximum available size\n"
+" -t, --type=<static|dynamic> volume type (dynamic, static), default is\n"
+" dynamic\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
static const char *usage =
-"Usage: " PROGRAM_NAME " <UBI device node file name> [-h] [-a <alignment>] [-n <volume ID>] [-N <name>]\n"
-"\t\t\t[-s <bytes>] [-S <LEBs>] [-t <static|dynamic>] [-V] [-m]\n"
-"\t\t\t[--alignment=<alignment>][--vol_id=<volume ID>] [--name=<name>]\n"
-"\t\t\t[--size=<bytes>] [--lebs=<LEBs>] [--type=<static|dynamic>] [--help]\n"
-"\t\t\t[--version] [--maxavsize]\n\n"
-"Example: " PROGRAM_NAME "/dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n"
-" named \"config_data\" on UBI device /dev/ubi0.";
-
-static const struct option long_options[] = {
+"Usage: ubimkvol [-?V] [-a <alignment>] [-d <devn>] [-n <volume id>]\n"
+" [-N <name>] [-s <bytes>] [-t <static|dynamic>] [-m]\n"
+" [--alignment=<alignment>] [--devn=<devn>] [--vol_id=<volume id>]\n"
+" [--name=<name>] [--size=<bytes>] [--type=<static|dynamic>] [--help]\n"
+" [--usage] [--version] [--maxavsize]\n";
+
+struct option long_options[] = {
{ .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' },
- { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
- { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
- { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
- { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' },
- { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
+ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' },
+ { .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 = 'V' },
{ .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' },
- { NULL, 0, NULL, 0},
+ { NULL, 0, NULL, 0}
};
-static int param_sanity_check(void)
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The list of arguments
+ * @param args Pointer to argument structure
+ *
+ * @return error
+ *
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
{
- int len;
-
- if (args.bytes == -1 && !args.maxavs && args.lebs == -1)
- return errmsg("volume size was not specified (use -h for help)");
-
- if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) ||
- (args.lebs != -1 && (args.maxavs || args.bytes != -1)) ||
- (args.maxavs && (args.bytes != -1 || args.lebs != -1)))
- return errmsg("size specified with more then one option");
-
- if (args.name == NULL)
- return errmsg("volume name was not specified (use -h for help)");
-
- len = strlen(args.name);
- if (len > UBI_MAX_VOLUME_NAME)
- return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME);
-
- return 0;
-}
+ char *endp;
-static int parse_opt(int argc, char * const argv[])
-{
while (1) {
int key;
- char *endp;
- key = getopt_long(argc, argv, "a:n:N:s:S:t:hVm", long_options, NULL);
+ key = getopt_long(argc, argv, "a:d:n:N:s:t:?Vm", long_options, NULL);
if (key == -1)
break;
switch (key) {
- case 't':
- if (!strcmp(optarg, "dynamic"))
- args.vol_type = UBI_DYNAMIC_VOLUME;
- else if (!strcmp(optarg, "static"))
- args.vol_type = UBI_STATIC_VOLUME;
- else
- return errmsg("bad volume type: \"%s\"", optarg);
- break;
-
- case 's':
- args.bytes = ubiutils_get_bytes(optarg);
- if (args.bytes <= 0)
- return errmsg("bad volume size: \"%s\"", optarg);
- break;
-
- case 'S':
- args.lebs = strtoull(optarg, &endp, 0);
- if (endp == optarg || args.lebs <= 0 || *endp != '\0')
- return errmsg("bad LEB count: \"%s\"", optarg);
- break;
-
- case 'a':
- args.alignment = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.alignment <= 0)
- return errmsg("bad volume alignment: \"%s\"", optarg);
- break;
-
- case 'n':
- args.vol_id = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.vol_id < 0)
- return errmsg("bad volume ID: " "\"%s\"", optarg);
- break;
-
- case 'N':
- args.name = optarg;
- args.nlen = strlen(args.name);
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
+ case 't':
+ if (!strcmp(optarg, "dynamic"))
+ args->vol_type = UBI_DYNAMIC_VOLUME;
+ else if (!strcmp(optarg, "static"))
+ args->vol_type = UBI_STATIC_VOLUME;
+ else {
+ fprintf(stderr,
+ "Bad volume type: \"%s\"\n",
+ optarg);
+ goto out;
+ }
+ break;
+ case 's':
+ args->bytes = strtoull(optarg, &endp, 0);
+ if (endp == optarg || args->bytes < 0) {
+ fprintf(stderr,
+ "Bad volume size: \"%s\"\n",
+ optarg);
+ goto out;
+ }
+ if (endp != '\0') {
+ if (strcmp(endp, "KiB") == 0)
+ args->bytes *= 1024;
+ else if (strcmp(endp, "MiB") == 0)
+ args->bytes *= 1024*1024;
+ }
+ break;
+ case 'a':
+ args->alignment = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->alignment <= 0) {
+ fprintf(stderr, "Bad volume alignment: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case 'd': /* --devn=<device number> */
+ args->devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->devn < 0) {
+ fprintf(stderr,
+ "Bad UBI device number: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ sprintf(args->node, "/dev/ubi%d", args->devn);
+ break;
+ case 'n': /* --volid=<volume id> */
+ args->vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' ||
+ endp == optarg ||
+ (args->vol_id < 0 &&
+ args->vol_id != UBI_DYNAMIC_VOLUME)) {
+ fprintf(stderr, "Bad volume ID: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case 'N':
+ args->name = optarg;
+ args->nlen = strlen(args->name);
+ break;
+
+ case ':':
+ fprintf(stderr, "Parameter is missing\n");
+ goto out;
+
+ case '?': /* help */
+ fprintf(stderr,
+ "Usage: ubimkvol [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ case 'm':
+ args->maxavs = 1;
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
+ return 0;
+ out:
+ return -1;
+}
- case 'm':
- args.maxavs = 1;
- break;
+static int param_sanity_check(struct args *args, libubi_t libubi)
+{
+ int err, len;
+ struct ubi_info ubi;
- case ':':
- return errmsg("parameter is missing");
+ if (args->bytes == 0 && !args->maxavs) {
+ fprintf(stderr, "Volume size was not specified\n");
+ goto out;
+ }
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
+ if (args->name == NULL) {
+ fprintf(stderr, "Volume name was not specified\n");
+ goto out;
}
- if (optind == argc)
- return errmsg("UBI device name was not specified (use -h for help)");
- else if (optind != argc - 1)
- return errmsg("more then one UBI device specified (use -h for help)");
+ err = ubi_get_info(libubi, &ubi);
+ if (err)
+ return -1;
- args.node = argv[optind];
+ if (args->devn >= (int)ubi.dev_count) {
+ fprintf(stderr, "Device %d does not exist\n", args->devn);
+ goto out;
+ }
- if (param_sanity_check())
- return -1;
+ len = strlen(args->name);
+ if (len > UBI_MAX_VOLUME_NAME) {
+ fprintf(stderr, "Too long name (%d symbols), max is %d\n",
+ len, UBI_MAX_VOLUME_NAME);
+ goto out;
+ }
return 0;
+out:
+ errno = EINVAL;
+ return -1;
}
int main(int argc, char * const argv[])
{
int err;
libubi_t libubi;
- struct ubi_dev_info dev_info;
- struct ubi_vol_info vol_info;
struct ubi_mkvol_request req;
- err = parse_opt(argc, argv);
- if (err)
- return err;
+ err = parse_opt(argc, (char **)argv, &myargs);
+ if (err) {
+ fprintf(stderr, "Wrong options ...\n");
+ return err == 1 ? 0 : -1;
+ }
- libubi = libubi_open();
- if (!libubi)
- return sys_errmsg("cannot open libubi");
+ if (myargs.devn == -1) {
+ fprintf(stderr, "Device number was not specified\n");
+ fprintf(stderr, "Use -h option for help\n");
+ return -1;
+ }
- err = ubi_node_type(libubi, args.node);
- if (err == 2) {
- errmsg("\"%s\" is an UBI volume node, not an UBI device node",
- args.node);
- goto out_libubi;
- } else if (err < 0) {
- errmsg("\"%s\" is not an UBI device node", args.node);
- goto out_libubi;
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
}
- err = ubi_get_dev_info(libubi, args.node, &dev_info);
+ err = param_sanity_check(&myargs, libubi);
if (err) {
- sys_errmsg("cannot get information about UBI device \"%s\"",
- args.node);
+ perror("Input parameters check");
+ fprintf(stderr, "Use -h option for help\n");
goto out_libubi;
}
- if (args.maxavs) {
- args.bytes = dev_info.avail_bytes;
- printf("Set volume size to %lld\n", req.bytes);
- }
+ req.vol_id = myargs.vol_id;
+ req.alignment = myargs.alignment;
- if (args.lebs != -1) {
- args.bytes = dev_info.leb_size;
- args.bytes -= dev_info.leb_size % args.alignment;
- args.bytes *= args.lebs;
- }
+ if (myargs.maxavs) {
+ struct ubi_dev_info ubi_dev;
+
+ err = ubi_get_dev_info1(libubi, myargs.devn, &ubi_dev);
+ if (err) {
+ perror("Can't get UBI device info");
+ goto out_libubi;
+ }
+ req.bytes = ubi_dev.avail_bytes;
+ if (!req.bytes) {
+ fprintf(stderr, "There is no available free space on device!\n");
+ goto out_libubi;
+ }
+ printf("Setting the volume size to %lld\n", req.bytes);
+ } else
+ req.bytes = myargs.bytes;
- req.vol_id = args.vol_id;
- req.alignment = args.alignment;
- req.bytes = args.bytes;
- req.vol_type = args.vol_type;
- req.name = args.name;
+ req.vol_type = myargs.vol_type;
+ req.name = myargs.name;
- err = ubi_mkvol(libubi, args.node, &req);
+ err = ubi_mkvol(libubi, myargs.node, &req);
if (err < 0) {
- sys_errmsg("cannot UBI create volume");
+ perror("Cannot create volume");
+ fprintf(stderr, " err=%d\n", err);
goto out_libubi;
}
- args.vol_id = req.vol_id;
-
- /* Print information about the created device */
- err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info);
- if (err) {
- sys_errmsg("cannot get information about newly created UBI volume");
- goto out_libubi;
+ /*
+ * This is hacky, but we want to wait until udev has created device
+ * nodes. There is probably better way do do this, though.
+ */
+ if (system("udevsettle")) {
+ /* Well, this is to keep GCC silent */
}
- printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs);
- ubiutils_print_bytes(vol_info.rsvd_bytes, 0);
- printf("), LEB size ");
- ubiutils_print_bytes(vol_info.leb_size, 1);
- printf(", %s, name \"%s\", alignment %d\n",
- req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static",
- vol_info.name, vol_info.alignment);
+ /* printf("Created volume %d, %lld bytes, type %s, name %s\n",
+ vol_id, bytes, vol_type == UBI_DYNAMIC_VOLUME ?
+ "dynamic" : "static", name); */
+ myargs.vol_id = err;
libubi_close(libubi);
return 0;
diff --git a/ubi-utils/src/ubinfo.c b/ubi-utils/src/ubinfo.c
deleted file mode 100644
index 185caae..0000000
--- a/ubi-utils/src/ubinfo.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 2007 Nokia Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * 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., 51
- * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * An utility to get UBI information.
- *
- * Author: Artem Bityutskiy
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libubi.h>
-#include "common.h"
-
-#define PROGRAM_VERSION "1.0"
-#define PROGRAM_NAME "ubinfo"
-
-/* The variables below are set by command line arguments */
-struct args {
- int devn;
- int vol_id;
- int all;
- const char *node;
-};
-
-static struct args args = {
- .vol_id = -1,
- .devn = -1,
- .all = 0,
- .node = NULL,
-};
-
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to print UBI information.";
-
-static const char *optionsstr =
-"-d, --devn=<UBI device number> UBI device number to get information about\n"
-"-n, --vol_id=<volume ID> ID of UBI volume to print information about\n"
-"-a, --all print information about all devices and volumes,\n"
-" or about all volumes if the UBI device was\n"
-" specified\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
-
-static const char *usage =
-"Usage 1: " PROGRAM_NAME " [-d <UBI device number>] [-n <volume ID>] [-a] [-h] [-V] [--vol_id=<volume ID>]\n"
-"\t\t[--devn <UBI device number>] [--all] [--help] [--version]\n"
-"Usage 2: " PROGRAM_NAME " <UBI device node file name> [-a] [-h] [-V] [--all] [--help] [--version]\n"
-"Usage 3: " PROGRAM_NAME " <UBI volume node file name> [-h] [-V] [--help] [--version]\n\n"
-"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n"
-"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n"
-"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n"
-" device /dev/ubi0\n"
-"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n"
-"Example 5: " PROGRAM_NAME " -a - print all information\n";
-
-static const struct option long_options[] = {
- { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
- { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
- { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0},
-};
-
-static int parse_opt(int argc, char * const argv[])
-{
- while (1) {
- int key;
- char *endp;
-
- key = getopt_long(argc, argv, "an:d:hV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 'a':
- args.all = 1;
- break;
-
- case 'n':
- args.vol_id = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.vol_id < 0)
- return errmsg("bad volume ID: " "\"%s\"", optarg);
- break;
-
- case 'd':
- args.devn = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.devn < 0)
- return errmsg("bad UBI device number: \"%s\"", optarg);
-
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- case ':':
- return errmsg("parameter is missing");
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
- }
-
- if (optind == argc - 1)
- args.node = argv[optind];
- else if (optind < argc)
- return errmsg("more then one UBI devices specified (use -h for help)");
-
- return 0;
-}
-
-static int translate_dev(libubi_t libubi, const char *node)
-{
- int err;
-
- err = ubi_node_type(libubi, node);
- if (err == -1) {
- if (errno)
- return errmsg("unrecognized device node \"%s\"", node);
- return errmsg("\"%s\" does not correspond to any UBI device or volume", node);
- }
-
- if (err == 1) {
- struct ubi_dev_info dev_info;
-
- err = ubi_get_dev_info(libubi, node, &dev_info);
- if (err)
- return sys_errmsg("cannot get information about UBI device \"%s\"", node);
-
- args.devn = dev_info.dev_num;
- } else {
- struct ubi_vol_info vol_info;
-
- err = ubi_get_vol_info(libubi, node, &vol_info);
- if (err)
- return sys_errmsg("cannot get information about UBI volume \"%s\"", node);
-
- if (args.vol_id != -1)
- return errmsg("both volume character device node (\"%s\") and "
- "volume ID (%d) are specify, use only one of them"
- "(use -h for help)", node, args.vol_id);
-
- args.devn = vol_info.dev_num;
- args.vol_id = vol_info.vol_id;
- }
-
- return 0;
-}
-
-static int print_vol_info(libubi_t libubi, int dev_num, int vol_id)
-{
- int err;
- struct ubi_vol_info vol_info;
-
- err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info);
- if (err)
- return sys_errmsg("cannot get information about UBI volume %d on ubi%d",
- vol_id, dev_num);
-
- printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num);
- printf("Type: %s\n",
- vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static");
- printf("Alignment: %d\n", vol_info.alignment);
-
- printf("Size: %d LEBs (", vol_info.rsvd_lebs);
- ubiutils_print_bytes(vol_info.rsvd_bytes, 0);
- printf(")\n");
-
- if (vol_info.type == UBI_STATIC_VOLUME) {
- printf("Data bytes: ");
- ubiutils_print_bytes(vol_info.data_bytes, 1);
- }
- printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK");
- printf("Name: %s\n", vol_info.name);
- printf("Character device major/minor: %d:%d\n",
- vol_info.major, vol_info.minor);
-
- return 0;
-}
-
-static int print_dev_info(libubi_t libubi, int dev_num, int all)
-{
- int i, err, first = 1;
- struct ubi_dev_info dev_info;
- struct ubi_vol_info vol_info;
-
- err = ubi_get_dev_info1(libubi, dev_num, &dev_info);
- if (err)
- return sys_errmsg("cannot get information about UBI device %d", dev_num);
-
- printf("ubi%d:\n", dev_info.dev_num);
- printf("Volumes count: %d\n", dev_info.vol_count);
- printf("Logical eraseblock size: %d\n", dev_info.leb_size);
-
- printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs);
- ubiutils_print_bytes(dev_info.total_bytes, 0);
- printf(")\n");
-
- printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs);
- ubiutils_print_bytes(dev_info.avail_bytes, 0);
- printf(")\n");
-
- printf("Maximum count of volumes %d\n", dev_info.max_vol_count);
- printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count);
- printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd);
- printf("Current maximum erase counter value: %lld\n", dev_info.max_ec);
- printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size);
- printf("Character device major/minor: %d:%d\n",
- dev_info.major, dev_info.minor);
-
- if (dev_info.vol_count == 0)
- return 0;
-
- printf("Present volumes: ");
- for (i = dev_info.lowest_vol_num;
- i <= dev_info.highest_vol_num; i++) {
- err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
- if (err == -1) {
- if (errno == ENOENT)
- continue;
-
- return sys_errmsg("libubi failed to probe volume %d on ubi%d",
- i, dev_info.dev_num);
- }
-
- if (!first)
- printf(", %d", i);
- else {
- printf("%d", i);
- first = 0;
- }
- }
- printf("\n");
-
- if (!all)
- return 0;
-
- first = 1;
- printf("\n");
-
- for (i = dev_info.lowest_vol_num;
- i <= dev_info.highest_vol_num; i++) {
- if(!first)
- printf("-----------------------------------\n");
- err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
- if (err == -1) {
- if (errno == ENOENT)
- continue;
-
- return sys_errmsg("libubi failed to probe volume %d on ubi%d",
- i, dev_info.dev_num);
- }
- first = 0;
-
- err = print_vol_info(libubi, dev_info.dev_num, i);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int print_general_info(libubi_t libubi, int all)
-{
- int i, err, first = 1;
- struct ubi_info ubi_info;
- struct ubi_dev_info dev_info;
-
- err = ubi_get_info(libubi, &ubi_info);
- if (err)
- return sys_errmsg("cannot get UBI information");
-
- printf("UBI version: %d\n", ubi_info.version);
- printf("Count of UBI devices: %d\n", ubi_info.dev_count);
- if (ubi_info.ctrl_major != -1)
- printf("UBI control device major/minor: %d:%d\n",
- ubi_info.ctrl_major, ubi_info.ctrl_minor);
- else
- printf("UBI control device is not supported by this kernel\n");
-
- if (ubi_info.dev_count == 0)
- return 0;
-
- printf("Present UBI devices: ");
- for (i = ubi_info.lowest_dev_num;
- i <= ubi_info.highest_dev_num; i++) {
- err = ubi_get_dev_info1(libubi, i, &dev_info);
- if (err == -1) {
- if (errno == ENOENT)
- continue;
-
- return sys_errmsg("libubi failed to probe UBI device %d", i);
- }
-
- if (!first)
- printf(", ubi%d", i);
- else {
- printf("ubi%d", i);
- first = 0;
- }
- }
- printf("\n");
-
- if (!all)
- return 0;
-
- first = 1;
- printf("\n");
-
- for (i = ubi_info.lowest_dev_num;
- i <= ubi_info.highest_dev_num; i++) {
- if(!first)
- printf("\n===================================\n\n");
- err = ubi_get_dev_info1(libubi, i, &dev_info);
- if (err == -1) {
- if (errno == ENOENT)
- continue;
-
- return sys_errmsg("libubi failed to probe UBI device %d", i);
- }
- first = 0;
-
- err = print_dev_info(libubi, i, all);
- if (err)
- return err;
- }
- return 0;
-}
-
-int main(int argc, char * const argv[])
-{
- int err;
- libubi_t libubi;
-
- err = parse_opt(argc, argv);
- if (err)
- return -1;
-
- if (!args.node && args.devn != -1)
- return errmsg("specify either device number or node file (use -h for help)");
-
- libubi = libubi_open();
- if (libubi == NULL)
- return sys_errmsg("cannot open libubi");
-
- if (args.node) {
- /*
- * A character device was specified, translate this into UBI
- * device number and volume ID.
- */
- err = translate_dev(libubi, args.node);
- if (err)
- goto out_libubi;
- }
-
- if (args.vol_id != -1 && args.devn == -1) {
- errmsg("volume ID is specified, but UBI device number is not "
- "(use -h for help)\n");
- goto out_libubi;
- }
-
- if (args.devn != -1 && args.vol_id != -1) {
- print_vol_info(libubi, args.devn, args.vol_id);
- goto out;
- }
-
- if (args.devn == -1 && args.vol_id == -1)
- err = print_general_info(libubi, args.all);
- else if (args.devn != -1 && args.vol_id == -1)
- err = print_dev_info(libubi, args.devn, args.all);
-
- if (err)
- goto out_libubi;
-
-out:
- libubi_close(libubi);
- return 0;
-
-out_libubi:
- libubi_close(libubi);
- return -1;
-}
diff --git a/ubi-utils/src/ubinize.c b/ubi-utils/src/ubinize.c
deleted file mode 100644
index 47dc571..0000000
--- a/ubi-utils/src/ubinize.c
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * Copyright (C) 2008 Nokia Corporation
- * Copyright (c) International Business Machines Corp., 2006
- *
- * 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.
- */
-
-/*
- * Generate UBI images.
- *
- * Authors: Artem Bityutskiy
- * Oliver Lohmann
- */
-
-#include <stdlib.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#include <mtd/ubi-header.h>
-#include <libubigen.h>
-#include <libiniparser.h>
-#include "common.h"
-
-#define PROGRAM_VERSION "1.5"
-#define PROGRAM_NAME "ubinize"
-
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
-" - a tool to generate UBI images. An UBI image may contain one or more UBI "
-"volumes which have to be defined in the input configuration ini-file. The "
-"ini file defines all the UBI volumes - their characteristics and the and the "
-"contents, but it does not define the characteristics of the flash the UBI "
-"image is generated for. Instead, the flash characteristics are defined via "
-"the command-line options. Note, if not sure about some of the command-line "
-"parameters, do not specify them and let the utility to use default values.";
-
-static const char *optionsstr =
-"-o, --output=<file name> output file name (default is stdout)\n"
-"-p, --peb-size=<bytes> size of the physical eraseblock of the flash\n"
-" this UBI image is created for in bytes,\n"
-" kilobytes (KiB), or megabytes (MiB)\n"
-" (mandatory parameter)\n"
-"-m, --min-io-size=<bytes> minimum input/output unit size of the flash\n"
-" in bytes\n"
-"-s, --sub-page-size=<bytes> minimum input/output unit used for UBI\n"
-" headers, e.g. sub-page size in case of NAND\n"
-" flash (equivalent to the minimum input/output\n"
-" unit size by default)\n"
-"-O, --vid-hdr-offset=<num> offset if the VID header from start of the\n"
-" physical eraseblock (default is the second\n"
-" minimum I/O unit or sub-page, if it was\n"
-" specified)\n"
-"-e, --erase-counter=<num> the erase counter value to put to EC headers\n"
-" (default is 0)\n"
-"-x, --ubi-ver=<num> UBI version number to put to EC headers\n"
-" (default is 1)\n"
-"-v --verbose be verbose\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
-
-static const char *usage =
-"Usage: " PROGRAM_NAME " [-o filename] [-h] [-V] [--output=<filename>] [--help]\n"
-"\t\t[--version] ini-file\n"
-"Example: " PROGRAM_NAME "-o fs.raw cfg.ini";
-
-static const char *ini_doc = "INI-file format.\n"
-"The input configuration ini-file describes all the volumes which have to\n"
-"be included to the output UBI image. Each volume is described in its own\n"
-"section which may be named arbitrarily. The section consists on\n"
-"\"key=value\" pairs, for example:\n\n"
-"[jffs2-volume]\n"
-"mode=ubi\n"
-"image=../jffs2.img\n"
-"vol_id=1\n"
-"vol_size=30MiB\n"
-"vol_type=dynamic\n"
-"vol_name=jffs2_volume\n"
-"vol_flags=autoresize\n"
-"vol_alignment=1\n\n"
-"This example configuration file tells the utility to create an UBI image\n"
-"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n"
-"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n"
-"\"image=../jffs2.img\" line tells the utility to take the contents of the\n"
-"volume from the \"../jffs2.img\" file. The size of the image file has to be\n"
-"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n"
-"mandatory and just tells that the section describes an UBI volume - other\n"
-"section modes may be added in the future.\n"
-"Notes:\n"
-" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n"
-" gigabytes (GiB) or bytes (no modifier);\n"
-" * if \"vol_size\" key is absent, the volume size is assumed to be\n"
-" equivalent to the size of the image file (defined by \"image\" key);\n"
-" * if the \"image\" is absent, the volume is assumed to be empty;\n"
-" * volume alignment must not be greater than the logical eraseblock size;\n"
-" * one ini file may contain arbitrary number of sections, the utility will\n"
-" put all the volumes which are described by these section to the output\n"
-" UBI image file.";
-
-struct option long_options[] = {
- { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
- { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' },
- { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' },
- { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' },
- { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' },
- { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' },
- { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' },
- { .name = "verbose", .has_arg = 1, .flag = NULL, .val = 'v' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0}
-};
-
-struct args {
- const char *f_in;
- const char *f_out;
- FILE *fp_out;
- int peb_size;
- int min_io_size;
- int subpage_size;
- int vid_hdr_offs;
- int ec;
- int ubi_ver;
- int verbose;
- dictionary *dict;
-};
-
-static struct args args = {
- .f_out = NULL,
- .peb_size = -1,
- .min_io_size = -1,
- .subpage_size = -1,
- .vid_hdr_offs = 0,
- .ec = 0,
- .ubi_ver = 1,
- .verbose = 0,
-};
-
-static int parse_opt(int argc, char * const argv[])
-{
- while (1) {
- int key;
- char *endp;
-
- key = getopt_long(argc, argv, "o:p:m:s:O:e:x:vhV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 'o':
- args.fp_out = fopen(optarg, "wb");
- if (!args.fp_out)
- return errmsg("cannot open file \"%s\"", optarg);
- args.f_out = optarg;
- break;
-
- case 'p':
- args.peb_size = ubiutils_get_bytes(optarg);
- if (args.peb_size <= 0)
- return errmsg("bad physical eraseblock size: \"%s\"", optarg);
- break;
-
- case 'm':
- args.min_io_size = ubiutils_get_bytes(optarg);
- if (args.min_io_size <= 0)
- return errmsg("bad min. I/O unit size: \"%s\"", optarg);
- break;
-
- case 's':
- args.subpage_size = ubiutils_get_bytes(optarg);
- if (args.subpage_size <= 0)
- return errmsg("bad sub-page size: \"%s\"", optarg);
- break;
-
- case 'O':
- args.vid_hdr_offs = strtoul(optarg, &endp, 0);
- if (endp == optarg || args.vid_hdr_offs < 0)
- return errmsg("bad VID header offset: \"%s\"", optarg);
- break;
-
- case 'e':
- args.ec = strtoul(optarg, &endp, 0);
- if (endp == optarg || args.ec < 0)
- return errmsg("bad erase counter value: \"%s\"", optarg);
- break;
-
- case 'x':
- args.ubi_ver = strtoul(optarg, &endp, 0);
- if (endp == optarg || args.ubi_ver < 0)
- return errmsg("bad UBI version: \"%s\"", optarg);
- break;
-
- case 'v':
- args.verbose = 1;
- break;
-
- case 'h':
- ubiutils_print_text(stderr, doc, 80);
- fprintf(stderr, "\n%s\n\n", ini_doc);
- fprintf(stderr, "%s\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
- }
-
- if (optind == argc)
- return errmsg("input PFI file was not specified (use -h for help)");
-
- if (optind != argc - 1)
- return errmsg("more then one input PFI file was specified (use -h for help)");
-
- args.f_in = argv[optind];
-
- if (args.peb_size < 0)
- return errmsg("physical eraseblock size was not specified (use -h for help)");
-
- if (args.min_io_size < 0)
- return errmsg("min. I/O unit size was not specified (use -h for help)");
-
- if (args.subpage_size < 0)
- args.subpage_size = args.min_io_size;
-
- if (!args.f_out) {
- args.f_out = "stdout";
- args.fp_out = stdout;
- }
-
- return 0;
-}
-
-int read_section(const char *sname, struct ubigen_vol_info *vi,
- const char **img)
-{
- char buf[256];
- const char *p;
-
- *img = NULL;
-
- if (strlen(sname) > 128)
- return errmsg("too long section name \"%s\"", sname);
-
- /* Make sure mode is UBI, otherwise ignore this section */
- sprintf(buf, "%s:mode", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (!p) {
- errmsg("\"mode\" key not found in section \"%s\"", sname);
- errmsg("the \"mode\" key is mandatory and has to be "
- "\"mode=ubi\" if the section describes an UBI volume");
- return -1;
- }
-
- /* If mode is not UBI, skip this section */
- if (strcmp(p, "ubi")) {
- verbose(args.verbose, "skip non-ubi section \"%s\"", sname);
- return 1;
- }
-
- verbose(args.verbose, "mode=ubi, keep parsing");
-
- /* Fetch the name of the volume image file */
- sprintf(buf, "%s:image", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (p)
- *img = p;
-
- /* Fetch volume id */
- sprintf(buf, "%s:vol_id", sname);
- vi->id = iniparser_getint(args.dict, buf, -1);
- if (vi->id == -1)
- return errmsg("\"vol_id\" key not found in section \"%s\"", sname);
-
- if (vi->id < 0)
- return errmsg("negative volume ID %d", vi->id);
-
- if (vi->id >= UBI_MAX_VOLUMES)
- return errmsg("too high volume ID %d, max. is %d", vi->id, UBI_MAX_VOLUMES);
-
- verbose(args.verbose, "volume ID: %d", vi->id);
-
- /* Fetch volume size */
- sprintf(buf, "%s:vol_size", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (p) {
- vi->bytes = ubiutils_get_bytes(p);
- if (vi->bytes <= 0)
- return errmsg("bad \"vol_size\" key: \"%s\"", p);
-
- verbose(args.verbose, "volume size: %lld bytes", vi->bytes);
- } else {
- struct stat st;
-
- if (!*img)
- return errmsg("neither image file (\"image=\") nor volume size (\"vol_size=\") specified");
-
- if (stat(*img, &st))
- return sys_errmsg("cannot stat \"%s\"", *img);
-
- vi->bytes = st.st_size;
-
- if (vi->bytes == 0)
- return errmsg("file \"%s\" referred from section \"%s\" is empty", *img, sname);
-
- printf(PROGRAM_NAME ": volume size was not specified in"
- "section \"%s\", assume ", sname);
- ubiutils_print_bytes(vi->bytes, 1);
- printf("\n");
- }
-
- /* Fetch volume type */
- sprintf(buf, "%s:vol_type", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (!p) {
- normsg(": volume type was not specified in "
- "section \"%s\", assume \"dynamic\"\n", sname);
- vi->type = UBI_VID_DYNAMIC;
- } else {
- if (!strcmp(p, "static"))
- vi->type = UBI_VID_STATIC;
- else if (!strcmp(p, "dynamic"))
- vi->type = UBI_VID_DYNAMIC;
- else
- return errmsg("invalid volume type \"%s\"", p);
- }
-
- verbose(args.verbose, "volume type: %s",
- vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static");
-
- /* Fetch volume name */
- sprintf(buf, "%s:vol_name", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (!p)
- return errmsg("\"vol_name\" key not found in section \"%s\"", sname);
-
- vi->name = p;
- vi->name_len = strlen(p);
- if (vi->name_len > UBI_VOL_NAME_MAX)
- return errmsg("too long volume name in section \"%s\", max. is %d characters",
- vi->name, UBI_VOL_NAME_MAX);
-
- verbose(args.verbose, "volume name: %s", p);
-
- /* Fetch volume alignment */
- sprintf(buf, "%s:vol_alignment", sname);
- vi->alignment = iniparser_getint(args.dict, buf, -1);
- if (vi->alignment == -1) {
- normsg("volume alignment was not specified in section "
- "\"%s\", assume 1", sname);
- vi->alignment = 1;
- } else if (vi->id < 0)
- return errmsg("negative volume alignement %d", vi->alignment);
-
- verbose(args.verbose, "volume alignment: %d", vi->alignment);
-
- /* Fetch volume flags */
- sprintf(buf, "%s:vol_flags", sname);
- p = iniparser_getstring(args.dict, buf, NULL);
- if (p) {
- if (!strcmp(p, "autoresize")) {
- verbose(args.verbose, "autoresize flags found");
- vi->flags |= UBI_VTBL_AUTORESIZE_FLG;
- } else {
- return errmsg("unknown flags \"%s\" in section \"%s\"", p, sname);
- }
- }
-
- return 0;
-}
-
-static void init_vol_info(const struct ubigen_info *ui,
- struct ubigen_vol_info *vi)
-{
- vi->data_pad = ui->leb_size % vi->alignment;
- vi->usable_leb_size = ui->leb_size - vi->data_pad;
- vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size;
- vi->compat = 0;
-}
-
-int main(int argc, char * const argv[])
-{
- int err = -1, sects, i, volumes;
- struct ubigen_info ui;
- struct ubi_vtbl_record *vtbl;
-
- err = parse_opt(argc, argv);
- if (err)
- return -1;
-
- ubigen_info_init(&ui, args.peb_size, args.min_io_size,
- args.subpage_size, args.vid_hdr_offs,
- args.ubi_ver, args.ec);
-
- verbose(args.verbose, "LEB size: %d", ui.leb_size);
- verbose(args.verbose, "PEB size: %d", ui.peb_size);
- verbose(args.verbose, "min_io_size: %d", ui.min_io_size);
- verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs);
-
- vtbl = ubigen_create_empty_vtbl(&ui);
- if (!vtbl)
- goto out;
-
- args.dict = iniparser_load(args.f_in);
- if (!args.dict) {
- errmsg("cannot load the input ini file \"%s\"", args.f_in);
- goto out_vtbl;
- }
-
- verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in);
-
- /* Each section describes one volume */
- sects = iniparser_getnsec(args.dict);
- if (sects == -1) {
- errmsg("ini-file parsing error (iniparser_getnsec)");
- goto out_dict;
- }
-
- verbose(args.verbose, "count of sections: %d", sects);
- if (sects == 0) {
- errmsg("no sections found the ini-file \"%s\"", args.f_in);
- goto out_dict;
- }
-
- /*
- * Skip 2 PEBs at the beginning of the file for the volume table which
- * will be written later.
- */
- if (fseek(args.fp_out, ui.peb_size * 2, SEEK_SET) == -1) {
- errmsg("cannot seek file \"%s\"", args.f_out);
- goto out_dict;
- }
-
- for (i = 0; i < sects; i++) {
- const char *sname = iniparser_getsecname(args.dict, i);
- struct ubigen_vol_info vi;
- const char *img = NULL;
- struct stat st;
- FILE *f;
-
- if (!sname) {
- errmsg("ini-file parsing error (iniparser_getsecname)");
- goto out_dict;
- }
-
- if (args.verbose)
- printf("\n");
- verbose(args.verbose, "parsing section \"%s\"", sname);
-
- err = read_section(sname, &vi, &img);
- if (err == -1)
- goto out_dict;
- if (!err)
- volumes += 1;
- init_vol_info(&ui, &vi);
-
- verbose(args.verbose, "adding volume %d", vi.id);
-
- err = ubigen_add_volume(&ui, &vi, vtbl);
- if (err) {
- errmsg("cannot add volume for section \"%s\"", sname);
- goto out_dict;
- }
-
- if (!img)
- continue;
-
- if (stat(img, &st)) {
- sys_errmsg("cannot stat \"%s\"", img);
- goto out_dict;
- }
-
- /*
- * Make sure the image size is not larger then the volume size.
- */
- if (st.st_size > vi.bytes) {
- errmsg("error in section \"%s\": size of the image file \"%s\" "
- "is %lld, which is larger then the volume size %lld",
- sname, img, (long long)st.st_size, vi.bytes);
- goto out_dict;
- }
-
- f = fopen(img, "r");
- if (!f) {
- sys_errmsg("cannot open \"%s\"", img);
- goto out_dict;
- }
-
- verbose(args.verbose, "writing volume %d", vi.id);
- verbose(args.verbose, "image file: %s", img);
-
- err = ubigen_write_volume(&ui, &vi, st.st_size, f, args.fp_out);
- fclose(f);
- if (err) {
- errmsg("cannot write volume for section \"%s\"", sname);
- goto out_dict;
- }
-
- if (args.verbose)
- printf("\n");
- }
-
- verbose(args.verbose, "writing layout volume");
-
- if (fseek(args.fp_out, 0, SEEK_SET) == -1) {
- errmsg("cannot seek file \"%s\"", args.f_out);
- goto out_dict;
- }
-
- err = ubigen_write_layout_vol(&ui, vtbl, args.fp_out);
- if (err) {
- errmsg("cannot write layout volume");
- goto out_dict;
- }
-
- verbose(args.verbose, "done");
-
- iniparser_freedict(args.dict);
- free(vtbl);
- fclose(args.fp_out);
- return 0;
-
-out_dict:
- iniparser_freedict(args.dict);
-out_vtbl:
- free(vtbl);
-out:
- fclose(args.fp_out);
- remove(args.f_out);
- return err;
-}
diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c
index c30446a..6dd16ba 100644
--- a/ubi-utils/src/ubirmvol.c
+++ b/ubi-utils/src/ubirmvol.c
@@ -19,8 +19,13 @@
/*
* An utility to remove UBI volumes.
*
- * Authors: Artem Bityutskiy <dedekind@infradead.org>
- * Frank Haverkamp <haver@vnet.ibm.com>
+ * Author: Artem B. Bityutskiy <dedekind@linutronix.de>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ *
+ * 1.1 Reworked the userinterface to use argp.
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ * 1.4 Use a different libubi
*/
#include <stdio.h>
@@ -28,136 +33,190 @@
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <config.h>
#include <libubi.h>
-#include "common.h"
-#define PROGRAM_VERSION "1.5"
-#define PROGRAM_NAME "ubirmvol"
+#define PROGRAM_VERSION "1.4"
-/* The variables below are set by command line arguments */
+/*
+ * The below variables are set by command line options.
+ */
struct args {
+ int devn;
int vol_id;
- const char *node;
+ char node[256];
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
};
-static struct args args = {
+static struct args myargs = {
+ .devn = -1,
.vol_id = -1,
- .node = NULL,
+
+ .arg1 = NULL,
+ .options = NULL,
};
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to remove UBI volumes.";
+static int param_sanity_check(struct args *args, libubi_t libubi);
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubirmvol - make UBI Volume.\n";
static const char *optionsstr =
-" -n, --vol_id=<volume id> volume ID to remove\n"
-" -h, --help print help message\n"
-" -V, --version print program version";
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id, if not specified, the volume ID\n"
+" will be assigned automatically\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
static const char *usage =
-"Usage: " PROGRAM_NAME " <UBI device node file name> [-n <volume id>] [--vol_id=<volume id>] [-h] [--help]\n\n"
-"Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n"
-" to the node file /dev/ubi0.";
-
-static const struct option long_options[] = {
- { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+"Usage: ubirmvol [-?V] [-d <devn>] [-n <volume id>] [--devn=<devn>]\n"
+" [--vol_id=<volume id>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .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 = 'V' },
- { NULL, 0, NULL, 0},
+ { NULL, 0, NULL, 0}
};
-static int param_sanity_check(void)
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The list of arguments
+ * @param args Pointer to argument structure
+ *
+ * @return error
+ *
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
{
- if (args.vol_id == -1) {
- errmsg("volume ID is was not specified");
- return -1;
- }
-
- return 0;
-}
+ char *endp;
-static int parse_opt(int argc, char * const argv[])
-{
while (1) {
int key;
- char *endp;
- key = getopt_long(argc, argv, "n:hV", long_options, NULL);
+ key = getopt_long(argc, argv, "d:n:?V", long_options, NULL);
if (key == -1)
break;
switch (key) {
+ case 'd': /* --devn=<device number> */
+ args->devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->devn < 0) {
+ fprintf(stderr,
+ "Bad UBI device number: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ sprintf(args->node, "/dev/ubi%d", args->devn);
+ break;
+ case 'n': /* --volid=<volume id> */
+ args->vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ (args->vol_id < 0 &&
+ args->vol_id != UBI_DYNAMIC_VOLUME)) {
+ fprintf(stderr, "Bad volume ID: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case ':':
+ fprintf(stderr, "Parameter is missing\n");
+ goto out;
+ case '?': /* help */
+ fprintf(stderr,
+ "Usage: ubirmvol [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
- case 'n':
- args.vol_id = strtoul(optarg, &endp, 0);
- if (*endp != '\0' || endp == optarg || args.vol_id < 0) {
- errmsg("bad volume ID: " "\"%s\"", optarg);
- return -1;
- }
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
+ return 0;
+ out:
+ return -1;
+}
- case ':':
- errmsg("parameter is missing");
- return -1;
+static int param_sanity_check(struct args *args, libubi_t libubi)
+{
+ int err;
+ struct ubi_info ubi;
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
+ if (args->vol_id == -1) {
+ fprintf(stderr, "Volume ID was not specified\n");
+ goto out;
}
- if (optind == argc) {
- errmsg("UBI device name was not specified (use -h for help)");
- return -1;
- } else if (optind != argc - 1) {
- errmsg("more then one UBI device specified (use -h for help)");
+ err = ubi_get_info(libubi, &ubi);
+ if (err)
return -1;
- }
- args.node = argv[optind];
-
- if (param_sanity_check())
- return -1;
+ if (args->devn >= (int)ubi.dev_count) {
+ fprintf(stderr, "Device %d does not exist\n", args->devn);
+ goto out;
+ }
return 0;
+
+out:
+ errno = EINVAL;
+ return -1;
}
int main(int argc, char * const argv[])
{
- int err;
+ int err, old_errno;
libubi_t libubi;
- err = parse_opt(argc, argv);
+ err = parse_opt(argc, (char **)argv, &myargs);
if (err)
+ return err == 1 ? 0 : -1;
+
+ if (myargs.devn == -1) {
+ fprintf(stderr, "Device number was not specified\n");
+ fprintf(stderr, "Use -h option for help\n");
return -1;
+ }
libubi = libubi_open();
- if (libubi == NULL)
- return sys_errmsg("cannot open libubi");
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
+ }
- err = ubi_node_type(libubi, args.node);
- if (err == 2) {
- errmsg("\"%s\" is an UBI volume node, not an UBI device node",
- args.node);
- goto out_libubi;
- } else if (err < 0) {
- errmsg("\"%s\" is not an UBI device node", args.node);
+ err = param_sanity_check(&myargs, libubi);
+ if (err) {
+ perror("Input parameters check");
+ fprintf(stderr, "Use -h option for help\n");
goto out_libubi;
}
- err = ubi_rmvol(libubi, args.node, args.vol_id);
- if (err) {
- sys_errmsg("cannot UBI remove volume");
+ err = ubi_rmvol(libubi, myargs.node, myargs.vol_id);
+ old_errno = errno;
+ if (err < 0) {
+ perror("Cannot remove volume");
+ fprintf(stderr, " err=%d errno=%d\n", err, old_errno);
goto out_libubi;
}
diff --git a/ubi-utils/src/ubiupdate.c b/ubi-utils/src/ubiupdate.c
deleted file mode 100644
index c17b3c5..0000000
--- a/ubi-utils/src/ubiupdate.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (c) International Business Machines Corp., 2006
- *
- * 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.
- */
-
-/*
- * An utility to update UBI volumes.
- *
- * Authors: Frank Haverkamp
- * Joshua W. Boyer
- * Artem Bityutskiy
- */
-
-#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 <sys/stat.h>
-
-#include <libubi.h>
-#include "common.h"
-
-#define PROGRAM_VERSION "1.3"
-#define PROGRAM_NAME "ubiupdate"
-
-struct args {
- int truncate;
- const char *node;
- const char *img;
-};
-
-static struct args args = {
- .truncate = 0,
- .node = NULL,
- .img = NULL,
-};
-
-static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
- " - a tool to write data to UBI volumes.";
-
-static const char *optionsstr =
-"-n, --vol_id=<volume id> ID of UBI volume to update\n"
-"-t, --truncate truncate volume (wipe it out)\n"
-"-h, --help print help message\n"
-"-V, --version print program version";
-
-static const char *usage =
-"Usage: " PROGRAM_NAME " <UBI volume node file name> [-t] [-h] [-V] [--truncate] [--help]\n"
-"\t\t[--version] <image file>\n\n"
-"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n"
-"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1";
-
-struct option long_options[] = {
- { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
- { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
- { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
- { NULL, 0, NULL, 0}
-};
-
-static int parse_opt(int argc, char * const argv[])
-{
- while (1) {
- int key;
-
- key = getopt_long(argc, argv, "n:thV", long_options, NULL);
- if (key == -1)
- break;
-
- switch (key) {
- case 't':
- args.truncate = 1;
- break;
-
- case 'h':
- fprintf(stderr, "%s\n\n", doc);
- fprintf(stderr, "%s\n\n", usage);
- fprintf(stderr, "%s\n", optionsstr);
- exit(EXIT_SUCCESS);
-
- case 'V':
- fprintf(stderr, "%s\n", PROGRAM_VERSION);
- exit(EXIT_SUCCESS);
-
- case ':':
- return errmsg("parameter is missing");
-
- default:
- fprintf(stderr, "Use -h for help\n");
- return -1;
- }
- }
-
- if (optind == argc)
- return errmsg("UBI device name was not specified (use -h for help)");
- else if (optind != argc - 2)
- return errmsg("specify UBI device name and image file name as first 2 "
- "parameters (use -h for help)");
-
- args.node = argv[optind];
- args.img = argv[optind + 1];
-
- return 0;
-}
-
-static int truncate_volume(libubi_t libubi)
-{
- int err, fd;
-
- fd = open(args.node, O_RDWR);
- if (fd == -1)
- return sys_errmsg("cannot open \"%s\"", args.node);
-
- err = ubi_update_start(libubi, fd, 0);
- if (err) {
- sys_errmsg("cannot truncate volume \"%s\"", args.node);
- close(fd);
- return -1;
- }
-
- close(fd);
- return 0;
-}
-
-static int ubi_write(int fd, const void *buf, int len)
-{
- int ret;
-
- while (len) {
- ret = write(fd, buf, len);
- if (ret < 0) {
- if (errno == EINTR) {
- warnmsg("do not interrupt me!");
- continue;
- }
- return sys_errmsg("cannot write %d bytes to volume \"%s\"",
- len, args.node);
- }
-
- if (ret == 0)
- return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node);
-
- len -= ret;
- buf += ret;
- }
-
- return 0;
-}
-
-static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info)
-{
- int err, fd, ifd;
- long long bytes;
- struct stat st;
- char *buf;
-
- buf = malloc(vol_info->leb_size);
- if (!buf)
- return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size);
-
- err = stat(args.img, &st);
- if (err < 0) {
- errmsg("stat failed on \"%s\"", args.node);
- goto out_free;
- }
-
- bytes = st.st_size;
- if (bytes > vol_info->rsvd_bytes) {
- errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)",
- args.img, bytes, args.node, vol_info->rsvd_bytes);
- goto out_free;
- }
-
- fd = open(args.node, O_RDWR);
- if (fd == -1) {
- sys_errmsg("cannot open UBI volume \"%s\"", args.node);
- goto out_free;
- }
-
- ifd = open(args.img, O_RDONLY);
- if (ifd == -1) {
- sys_errmsg("cannot open \"%s\"", args.img);
- goto out_close1;
- }
-
- err = ubi_update_start(libubi, fd, bytes);
- if (err) {
- sys_errmsg("cannot start volume \"%s\" update", args.node);
- goto out_close;
- }
-
- while (bytes) {
- int tocopy = vol_info->leb_size;
-
- if (tocopy > bytes)
- tocopy = bytes;
-
- err = read(ifd, buf, tocopy);
- if (err != tocopy) {
- if (errno == EINTR) {
- warnmsg("do not interrupt me!");
- continue;
- } else {
- sys_errmsg("cannot read %d bytes from \"%s\"",
- tocopy, args.img);
- goto out_close;
- }
- }
-
- err = ubi_write(fd, buf, tocopy);
- if (err)
- goto out_close;
- bytes -= tocopy;
- }
-
- close(ifd);
- close(fd);
- free(buf);
- return 0;
-
-out_close:
- close(ifd);
-out_close1:
- close(fd);
-out_free:
- free(buf);
- return -1;
-}
-
-int main(int argc, char * const argv[])
-{
- int err;
- libubi_t libubi;
- struct ubi_vol_info vol_info;
-
- err = parse_opt(argc, argv);
- if (err)
- return -1;
-
- if (!args.img && !args.truncate)
- return errmsg("incorrect arguments, use -h for help");
-
- libubi = libubi_open();
- if (libubi == NULL) {
- sys_errmsg("cannot open libubi");
- goto out_libubi;
- }
-
- err = ubi_node_type(libubi, args.node);
- if (err == 1) {
- errmsg("\"%s\" is an UBI device node, not an UBI volume node",
- args.node);
- goto out_libubi;
- } else if (err < 0) {
- errmsg("\"%s\" is not an UBI volume node", args.node);
- goto out_libubi;
- }
-
- err = ubi_get_vol_info(libubi, args.node, &vol_info);
- if (err) {
- sys_errmsg("cannot get information about UBI volume \"%s\"",
- args.node);
- goto out_libubi;
- }
-
- if (args.truncate)
- err = truncate_volume(libubi);
- else
- err = update_volume(libubi, &vol_info);
- if (err)
- goto out_libubi;
-
- libubi_close(libubi);
- return 0;
-
-out_libubi:
- libubi_close(libubi);
- return -1;
-}
diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c
new file mode 100644
index 0000000..807b961
--- /dev/null
+++ b/ubi-utils/src/ubiupdatevol.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * An utility to update UBI volumes.
+ *
+ * Author: Frank Haverkamp
+ * Joshua W. Boyer
+ *
+ * 1.0 Reworked the userinterface to use argp.
+ * 1.1 Removed argp parsing because we want to use uClib.
+ * 1.2 Minor cleanups
+ * 1.3 Use a different libubi
+ */
+
+#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 <sys/stat.h>
+
+#include <config.h>
+#include <libubi.h>
+
+#define PROGRAM_VERSION "1.3"
+
+#define MAXPATH 1024
+#define BUFSIZE 128 * 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ int devn;
+ int vol_id;
+ int truncate;
+ int broken_update;
+ int bufsize;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .devn = -1,
+ .vol_id = -1,
+ .truncate = 0,
+ .broken_update = 0,
+ .bufsize = BUFSIZE,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static int verbose = 0;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "ubiupdatevol - write to UBI Volume.\n";
+
+static const char *optionsstr =
+" -B, --broken-update broken update, this is for testing\n"
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id\n"
+" -t, --truncate truncate volume\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubiupdatevol [-Bt?V] [-d <devn>] [-n <volume id>] [--broken-update]\n"
+" [--devn=<devn>] [--vol_id=<volume id>] [--truncate] [--help]\n"
+" [--usage] [--version] <image file>\n";
+
+struct option long_options[] = {
+ { .name = "broken-update", .has_arg = 0, .flag = NULL, .val = 'B' },
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
+ { .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 = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "Bd:n:t?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'v': /* --verbose=<level> */
+ verbose = strtoul(optarg, (char **)NULL, 0);
+ break;
+
+ case 'n': /* --vol_id=<volume id> */
+ args->vol_id = strtol(optarg, (char **)NULL, 0);
+ break;
+
+ case 'd': /* --devn=<device number> */
+ args->devn = strtol(optarg, (char **)NULL, 0);
+ break;
+
+ case 'b': /* --bufsize=<bufsize> */
+ args->bufsize = strtol(optarg, (char **)NULL, 0);
+ if (args->bufsize <= 0)
+ args->bufsize = BUFSIZE;
+ break;
+
+ case 't': /* --truncate */
+ args->truncate = 1;
+ break;
+
+ case 'B': /* --broken-update */
+ args->broken_update = 1;
+ break;
+
+ case '?': /* help */
+ fprintf(stderr, "Usage: "
+ "ubiupdatevol [OPTION...] <image file>\n%s%s"
+ "\nReport bugs to %s\n",
+ doc, optionsstr, PACKAGE_BUGREPORT);
+ exit(EXIT_SUCCESS);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind < argc) {
+ /* only one additional argument required */
+ args->arg1 = argv[optind++];
+ }
+ return 0;
+}
+
+/**
+ * @bytes bytes must be always 0, if not 0 this is a testcase for a
+ * broken volume update where data is promissed to be written, but for
+ * some reason nothing is written. The volume is unusable after this.
+ */
+static int
+ubi_truncate_volume(struct args *args, int64_t bytes,libubi_t libubi)
+{
+ int rc, ofd;
+ char path[MAXPATH];
+ int old_errno;
+
+ snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id);
+ path[MAXPATH-1] = '\0';
+
+ ofd = open(path, O_RDWR);
+ if (ofd < 0) {
+ fprintf(stderr, "Cannot open volume %s\n", path);
+ exit(EXIT_FAILURE);
+ }
+ rc = ubi_update_start(libubi, ofd, bytes);
+ old_errno = errno;
+ if (rc < 0) {
+ perror("UBI volume update ioctl");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+ close(ofd);
+ return 0;
+}
+
+static ssize_t ubi_write(int fd, const void *buf, size_t count)
+{
+ int rc;
+ int len = count;
+
+ while (len) {
+ rc = write(fd, buf, len);
+ if (rc == -1) {
+ if (errno == EINTR)
+ continue; /* try again */
+ perror("write error");
+ return rc;
+ }
+
+ len -= rc;
+ buf += rc;
+ }
+ return count;
+}
+
+static int
+ubi_update_volume(struct args *args, libubi_t libubi)
+{
+ int rc, ofd;
+ FILE *ifp = NULL;
+ struct stat st;
+ int size = 0;
+ char *fname = args->arg1;
+ char path[MAXPATH];
+ char *buf;
+ int64_t bytes = 0;
+ int old_errno;
+
+ buf = malloc(args->bufsize);
+ if (!buf) {
+ perror("Out of memory");
+ exit(EXIT_FAILURE);
+ }
+
+ if (fname == NULL) {
+ fprintf(stderr, "Please specify an existing image file.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = stat(fname, &st);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot stat input file %s\n", fname);
+ exit(EXIT_FAILURE);
+ }
+ bytes = size = st.st_size;
+
+ ifp = fopen(fname, "r");
+ if (!ifp)
+ exit(EXIT_FAILURE);
+
+ snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id);
+ path[MAXPATH-1] = '\0';
+
+ ofd = open(path, O_RDWR);
+ if (ofd < 0) {
+ fprintf(stderr, "Cannot open UBI volume %s\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubi_update_start(libubi, ofd, bytes);
+ old_errno = errno;
+ if (rc < 0) {
+ perror("UBI volume update ioctl");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+
+ while (size > 0) {
+ ssize_t tocopy = MIN(args->bufsize, size);
+
+ rc = fread(buf, tocopy, 1, ifp);
+ if (rc != 1) {
+ perror("Could not read everything.");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubi_write(ofd, buf, tocopy);
+ old_errno = errno;
+ if (rc != tocopy) {
+ perror("Could not write to device");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+ size -= tocopy;
+ }
+
+ free(buf);
+ fclose(ifp);
+ rc = close(ofd);
+ if (rc != 0) {
+ perror("UBI volume close failed");
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rc;
+ libubi_t libubi;
+
+ parse_opt(argc, argv, &myargs);
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
+ }
+
+ if (myargs.truncate) {
+ rc = ubi_truncate_volume(&myargs, 0LL, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ }
+ else if (myargs.broken_update) {
+ rc = ubi_truncate_volume(&myargs, 1LL, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ } else {
+ rc = ubi_update_volume(&myargs, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c
new file mode 100644
index 0000000..8a022a3
--- /dev/null
+++ b/ubi-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-header.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_%04u" /* split volume */
+#define FN_VOLWH "%s/volume%03u" /* whole volume */
+#define FN_VITBL "%s/vol_info_table%u" /* 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, "%u\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];
+ 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;
+}
diff --git a/ubi-utils/src/unubi_analyze.c b/ubi-utils/src/unubi_analyze.c
new file mode 100644
index 0000000..182100e
--- /dev/null
+++ b/ubi-utils/src/unubi_analyze.c
@@ -0,0 +1,463 @@
+/*
+ * 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
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ *
+ * unubi uses the following functions to generate analysis output based on
+ * the header information in a raw-UBI image
+ */
+
+/*
+ * TODO: use OOB data to check for eraseblock validity in NAND images
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mtd_swab.h>
+
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define EC_X_INT 50
+
+/**
+ * intcmp - function needed by qsort to order integers
+ **/
+int intcmp(const void *a, const void *b)
+{
+ int A = *(int *)a;
+ int B = *(int *)b;
+ return A - B;
+}
+
+int longcmp(const void *a, const void *b)
+{
+ long long A = *(long long *)a;
+ long long B = *(long long *)b;
+ return A - B;
+}
+
+
+/**
+ * unubi_analyze_group_index - finds the normalized index in an array
+ * item: look for this item in the array
+ * array: array to search through
+ * size: length of the array
+ * array should be sorted for this algorithm to perform properly;
+ * if the item is not found returns -1, otherwise return value is the
+ * index in the array (note this contricts the array size to 2^32-1);
+ **/
+int
+norm_index(uint32_t item, uint32_t *array, size_t length)
+{
+ size_t i, index;
+
+ for (index = 0, i = 0; i < length; i++) {
+ if ((i != 0) && (array[i] != array[i - 1]))
+ index++;
+
+ if (item == array[i])
+ return index;
+ }
+
+ return -1;
+}
+
+
+/**
+ * unubi_analyze_ec_hdr - generate data table and plot script
+ * first: head of simple linked list
+ * path: folder to write into
+ * generates a data file containing the eraseblock index in the image
+ * and the erase counter found in its ec header;
+ * if the crc check fails, the line is commented out in the data file;
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_ec_hdr(struct eb_info *first, const char *path)
+{
+ char filename[PATH_MAX + 1];
+ size_t count, eraseblocks;
+ uint32_t crc, crc32_table[256];
+ uint64_t *erase_counts;
+ FILE* fpdata;
+ FILE* fpplot;
+ struct eb_info *cur;
+
+ if (first == NULL)
+ return -1;
+
+ /* crc check still needed for `first' linked list */
+ init_crc32_table(crc32_table);
+
+ /* prepare output files */
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL)
+ return -1;
+
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL) {
+ fclose(fpdata);
+ return -1;
+ }
+
+ /* make executable */
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = first;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ eraseblocks = count;
+
+ erase_counts = malloc(eraseblocks * sizeof(*erase_counts));
+ if (!erase_counts) {
+ perror("out of memory");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts));
+
+ /* second run: populate array to sort */
+ count = 0;
+ cur = first;
+ while (cur != NULL) {
+ erase_counts[count] = be64_to_cpu(cur->ec.ec);
+ cur = cur->next;
+ count++;
+ }
+ qsort(erase_counts, eraseblocks, sizeof(*erase_counts),
+ (void *)longcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = first;
+ fprintf(fpdata, "# eraseblock_no actual_erase_count "
+ "sorted_erase_count\n");
+ while (cur != NULL) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec,
+ UBI_EC_HDR_SIZE_CRC);
+
+ if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) ||
+ (crc != be32_to_cpu(cur->ec.hdr_crc)))
+ fprintf(fpdata, "# ");
+
+ fprintf(fpdata, "%u %llu %llu", count,
+ be64_to_cpu(cur->ec.ec),
+ erase_counts[count]);
+
+ if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC)
+ fprintf(fpdata, " ## bad magic: %08x",
+ be32_to_cpu(cur->ec.magic));
+
+ if (crc != be32_to_cpu(cur->ec.hdr_crc))
+ fprintf(fpdata, " ## CRC mismatch: given=%08x, "
+ "calc=%08x", be32_to_cpu(cur->ec.hdr_crc),
+ crc);
+
+ fprintf(fpdata, "\n");
+
+ cur = cur->next;
+ count++;
+ }
+ fclose(fpdata);
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"eraseblock\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ cur = first;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if ((count % EC_X_INT) == 0) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ fprintf(fpplot, "%d", count);
+ }
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ fprintf(fpplot, "set ylabel \"erase count\"\n");
+ fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1);
+ fprintf(fpplot, "# set yrange [-1:%llu]\n",
+ erase_counts[eraseblocks - 1] + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ fclose(fpplot);
+
+ return 0;
+}
+
+
+/**
+ * unubi_analyze_vid_hdr - generate data table and plot script
+ * head: head of complex linked list (eb_chain)
+ * path: folder to write into
+ * generates a data file containing the volume id, logical number, leb version,
+ * and data size from the vid header;
+ * all eraseblocks listed in the eb_chain are valid (checked in unubi);
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_vid_hdr(struct eb_info **head, const char *path)
+{
+ char filename[PATH_MAX + 1];
+ int rc, y1, y2;
+ size_t count, step, breadth;
+ uint32_t *leb_versions, *data_sizes;
+ FILE* fpdata;
+ FILE* fpplot;
+ struct eb_info *cur;
+
+ if (head == NULL || *head == NULL)
+ return -1;
+
+ rc = 0;
+ fpdata = NULL;
+ fpplot = NULL;
+ data_sizes = NULL;
+ leb_versions = NULL;
+
+ /* prepare output files */
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ memset(filename, 0, PATH_MAX + 1);
+ snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL) {
+ rc = -1;
+ goto exit;
+ }
+
+ /* make executable */
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ breadth = count;
+
+ leb_versions = malloc(breadth * sizeof(uint32_t));
+ if (leb_versions == NULL) {
+ rc = -1;
+ goto exit;
+ }
+ memset(leb_versions, 0, breadth * sizeof(uint32_t));
+
+ data_sizes = malloc(breadth * sizeof(uint32_t));
+ if (data_sizes == NULL) {
+ rc = -1;
+ goto exit;
+ }
+ memset(data_sizes, 0, breadth * sizeof(*data_sizes));
+
+ /* second run: populate arrays to sort */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ leb_versions[count] = be32_to_cpu(cur->vid.leb_ver);
+ data_sizes[count] = be32_to_cpu(cur->vid.data_size);
+ cur = cur->next;
+ count++;
+ }
+ qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp);
+ qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = *head;
+ fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver "
+ "y2_axis data_size\n");
+ while (cur != NULL) {
+ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
+ breadth);
+ y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes,
+ breadth);
+
+ if ((y1 == -1) || (y2 == -1)) {
+ rc = -1;
+ goto exit;
+ }
+
+ fprintf(fpdata, "%u %u %u %u %u %u %u\n",
+ count,
+ be32_to_cpu(cur->vid.vol_id),
+ be32_to_cpu(cur->vid.lnum),
+ y1,
+ be32_to_cpu(cur->vid.leb_ver),
+ y2,
+ be32_to_cpu(cur->vid.data_size));
+ cur = cur->next;
+ count++;
+ }
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"volume\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ step = 0;
+ cur = *head;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ if (step != be32_to_cpu(cur->vid.vol_id)) {
+ step = be32_to_cpu(cur->vid.vol_id);
+ fprintf(fpplot, "\"%d\" %d 0", step, count);
+ }
+ else
+ fprintf(fpplot, "\"%d\" %d 1",
+ be32_to_cpu(cur->vid.lnum), count);
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+ fprintf(fpplot, "set nox2tics\n");
+
+ /* fifth run: generate plot file ytics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set ylabel \"leb version\"\n");
+ fprintf(fpplot, "set ytics (");
+ while (cur->next != NULL) {
+ y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
+ breadth);
+
+ if (y1 == -1) {
+ rc = -1;
+ goto exit;
+ }
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver),
+ y1);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ /* sixth run: generate plot file y2tics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set y2label \"data size\"\n");
+ fprintf(fpplot, "set y2tics (");
+ while (cur != NULL) {
+ y2 = norm_index(be32_to_cpu(cur->vid.data_size),
+ data_sizes, breadth);
+
+ if (y2 == -1) {
+ rc = -1;
+ goto exit;
+ }
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size),
+ y2);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth);
+ y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth);
+ fprintf(fpplot, "set xrange [-1:%u]\n", count + 1);
+ fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1);
+ fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" "
+ "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" "
+ "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ exit:
+ if (fpdata != NULL)
+ fclose(fpdata);
+ if (fpplot != NULL)
+ fclose(fpplot);
+ if (data_sizes != NULL)
+ free(data_sizes);
+ if (leb_versions != NULL)
+ free(leb_versions);
+
+ return rc;
+}
+
+
+/**
+ * unubi_analyze - run all analyses
+ * head: eb_chain head
+ * first: simple linked list of eraseblock headers (use .next)
+ * path: directory (without trailing slash) to output to
+ * returns 0 upon successful completion, or -1 otherwise
+ **/
+int
+unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path)
+{
+ int ec_rc, vid_rc;
+
+ if (path == NULL)
+ return -1;
+
+ ec_rc = unubi_analyze_ec_hdr(first, path);
+ vid_rc = unubi_analyze_vid_hdr(head, path);
+ if (ec_rc < 0 || vid_rc < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/ubi-utils/src/unubi_analyze.h b/ubi-utils/src/unubi_analyze.h
new file mode 100644
index 0000000..8588219
--- /dev/null
+++ b/ubi-utils/src/unubi_analyze.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef __UNUBI_ANALYZE_H__
+#define __UNUBI_ANALYZE_H__
+
+/*
+ * Author: Drake Dowsett
+ * Contact: Andreas Arnez (arnez@de.ibm.com)
+ *
+ * Eraseblock Chain
+ *
+ * A linked list structure to order eraseblocks by volume and logical number
+ * and to update by version number. Doesn't contain actual eraseblock data
+ * but rather the erasecounter and volume id headers as well as a position
+ * indicator.
+ *
+ * Diagram Example:
+ *
+ * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL
+ * | | | | | |
+ * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0]
+ * | | | |
+ * [V1.1v0] NULL [V2.0v0] NULL
+ * | |
+ * NULL NULL
+ *
+ * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A,
+ * lnum B and leb_ver C
+ * -> represents the `next' pointer
+ * | represents the `older' pointer
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+#define FN_EH_STAT "analysis_blocks.txt"
+#define FN_EH_DATA "analysis_ec_hdr.data"
+#define FN_EH_PLOT "analysis_ec_hdr.plot"
+#define FN_VH_DATA "analysis_vid_hdr.data"
+#define FN_VH_PLOT "analysis_vid_hdr.plot"
+
+struct eb_info {
+ struct ubi_ec_hdr ec;
+ struct ubi_vid_hdr vid;
+
+ fpos_t eb_top;
+ uint32_t linear;
+ int ec_crc_ok;
+ int vid_crc_ok;
+ int data_crc_ok;
+ uint32_t phys_addr;
+ int phys_block;
+
+ struct eb_info *next;
+ struct eb_info *older;
+};
+
+int eb_chain_insert(struct eb_info **head, struct eb_info *item);
+
+int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+ struct eb_info **pos);
+
+int eb_chain_print(FILE *stream, struct eb_info *head);
+
+int eb_chain_destroy(struct eb_info **head);
+
+int unubi_analyze(struct eb_info **head, struct eb_info *first,
+ const char *path);
+
+#endif /* __UNUBI_ANALYZE_H__ */