| /* |
| * 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; |
| } |