diff options
Diffstat (limited to 'ubi-utils/src/libpfi')
| -rw-r--r-- | ubi-utils/src/libpfi/pfi.c | 461 | 
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; +}  | 
