aboutsummaryrefslogtreecommitdiff
path: root/ubi-utils/src/libpfi
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/src/libpfi')
-rw-r--r--ubi-utils/src/libpfi/pfi.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/ubi-utils/src/libpfi/pfi.c b/ubi-utils/src/libpfi/pfi.c
new file mode 100644
index 0000000..c8d5ee4
--- /dev/null
+++ b/ubi-utils/src/libpfi/pfi.c
@@ -0,0 +1,461 @@
+/*
+ * 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 */
+};
+
+static const char* modes[] = {"raw", "ubi", NULL}; /* 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)
+{
+ const char* ptr = modes[0];
+ int i = 0;
+ while (ptr != NULL) {
+ if(strcmp(ptr, mode) == 0) {
+ return i;
+ }
+ ptr++;
+ 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 ((uint32_t)value == (uint32_t)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;
+}