diff options
Diffstat (limited to 'ubi-utils/sort-me-out')
| -rw-r--r-- | ubi-utils/sort-me-out/README | 5 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/error.c | 240 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/error.h | 84 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/libpfiflash.c | 1325 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/libubimirror.c | 237 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/ubimirror.c | 213 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/ubimirror.h | 66 | 
7 files changed, 2170 insertions, 0 deletions
| diff --git a/ubi-utils/sort-me-out/README b/ubi-utils/sort-me-out/README index ddeee73..fa09c96 100644 --- a/ubi-utils/sort-me-out/README +++ b/ubi-utils/sort-me-out/README @@ -58,3 +58,8 @@ This directory contains various stuff that has to be cleaned up and sorted out.    better.  * pddcustomize: not sure what is this for, seems to be IBM-specific. + +* ubimirror: not exactly sure what is this for - if it is neede, it should be +  cleaned-up and moved to src/ + +* error.[ch]: strange and rather IBM-specific error messages output infrastructure. diff --git a/ubi-utils/sort-me-out/error.c b/ubi-utils/sort-me-out/error.c new file mode 100644 index 0000000..4aaedad --- /dev/null +++ b/ubi-utils/sort-me-out/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/sort-me-out/error.h b/ubi-utils/sort-me-out/error.h new file mode 100644 index 0000000..05d8078 --- /dev/null +++ b/ubi-utils/sort-me-out/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/sort-me-out/libpfiflash.c b/ubi-utils/sort-me-out/libpfiflash.c new file mode 100644 index 0000000..7e3d3b3 --- /dev/null +++ b/ubi-utils/sort-me-out/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/sort-me-out/libubimirror.c b/ubi-utils/sort-me-out/libubimirror.c new file mode 100644 index 0000000..d06770e --- /dev/null +++ b/ubi-utils/sort-me-out/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/sort-me-out/ubimirror.c b/ubi-utils/sort-me-out/ubimirror.c new file mode 100644 index 0000000..2cc4596 --- /dev/null +++ b/ubi-utils/sort-me-out/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/sort-me-out/ubimirror.h b/ubi-utils/sort-me-out/ubimirror.h new file mode 100644 index 0000000..d7ae2ad --- /dev/null +++ b/ubi-utils/sort-me-out/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__ */ | 
