view mpffs/common.c @ 33:660b0ea739f3

mpffs-dbgls implemented
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Sun, 30 Jun 2013 07:47:49 +0000
parents 3cca8070ef0f
children 9bc7f0e03fa8
line wrap: on
line source

/*
 * This module contains the code that will be common between mpffs-cat,
 * mpffs-ls and mpffs-xtr.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <endian.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include "types.h"
#include "struct.h"

u8 mpffs_header[6] = {'F', 'f', 's', '#', 0x10, 0x02};
u8 blank_flash_line[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

char *imgfile;
u32 eraseblk_size;
int total_blocks;
u32 total_ffs_size;
u8 *image, *indexblk;
int index_blk_num = -1, root_node_no;
struct objinfo root;
int verbose;

/*
 * The following function is used to verify that the specified or computed
 * flash erase block size is a power of 2.
 */
count_ones(word)
	u32 word;
{
	int count;

	for (count = 0; word; word >>= 1)
		count += word & 1;
	return count;
}

eraseblk_size_reasonable()
{
	if (count_ones(eraseblk_size) != 1 || eraseblk_size < 16384 ||
	    eraseblk_size > 0x100000) {
		fprintf(stderr, "0x%lx is an unreasonable erase block size\n",
			(u_long) eraseblk_size);
		exit(1);
	}
}

parse_cmdline_options(argc, argv)
	char **argv;
{
	extern char *optarg;
	int c;

	while ((c = getopt(argc, argv, "a:e:mn:pr:v")) != EOF)
		switch (c) {
		case 'a':
			index_blk_num = atoi(optarg);
			continue;
		case 'e':
			eraseblk_size = strtoul(optarg, 0, 0);
			eraseblk_size_reasonable();
			continue;
		case 'm':	/* "moko" */
			eraseblk_size = 0x10000;
			total_blocks = 7;
			continue;
		case 'n':
			total_blocks = atoi(optarg);
			if (total_blocks < 1) {
				fprintf(stderr, "invalid -n value\n");
				exit(1);
			}
			continue;
		case 'p':	/* Pirelli */
			eraseblk_size = 0x40000;
			total_blocks = 18;
			continue;
		case 'r':
			root_node_no = atoi(optarg);
			continue;
		case 'v':
			verbose++;
			continue;
		default:
			usage();
			exit(1);
		}

	return(0);
}

read_img_file()
{
	int fd;
	struct stat st;

	fd = open(imgfile, O_RDONLY);
	if (fd < 0) {
		perror(imgfile);
		exit(1);
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "%s is not a regular file\n", imgfile);
		exit(1);
	}
	if (!eraseblk_size && !total_blocks) {
		switch (st.st_size) {
		case 0x70000:
			/* assume Closedmoko */
			eraseblk_size = 0x10000;
			total_blocks = 7;
			break;
		case 0x480000:
			/* assume Pirelli */
			eraseblk_size = 0x40000;
			total_blocks = 18;
			break;
		default:
noauto:			fprintf(stderr,
	"cannot intuit the flash parameters of %s: use -e and -n options\n",
				imgfile);
			exit(1);
		}
	} else if (!total_blocks) {
		total_blocks = st.st_size / eraseblk_size;
		if (!total_blocks || total_blocks * eraseblk_size != st.st_size)
			goto noauto;
	} else if (!eraseblk_size) {
		if (total_blocks * 0x10000 == st.st_size)
			eraseblk_size = 0x10000;
		else if (total_blocks * 0x40000 == st.st_size)
			eraseblk_size = 0x40000;
		else
			goto noauto;
	}
	total_ffs_size = eraseblk_size * total_blocks;
	if (st.st_size < total_ffs_size) {
		fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile,
			total_ffs_size);
		exit(1);
	}
	image = malloc(total_ffs_size);
	if (!image) {
		perror("malloc");
		exit(1);
	}
	read(fd, image, total_ffs_size);
	close(fd);
}

find_index_block()
{
	int i, abcnt;
	u8 *ptr;

	if (index_blk_num >= 0) {
		if (index_blk_num >= total_blocks) {
			fprintf(stderr,
				"invalid block # given with the -a option\n");
			exit(1);
		}
		ptr = image + index_blk_num * eraseblk_size;
		if (bcmp(ptr, mpffs_header, 6) || ptr[8] != 0xAB) {
			fprintf(stderr,
			"no MPFFS index found at the specified block #%d\n",
				index_blk_num);
			exit(1);
		}
		indexblk = ptr;
		return(0);
	}
	abcnt = 0;
	for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) {
		if (bcmp(ptr, mpffs_header, 6)) {
			fprintf(stderr,
		"warning: no MPFFS signature in erase block #%d (offset %x)\n",
				i, ptr - image);
			continue;
		}
		switch (ptr[8]) {
		case 0xAB:
			if (verbose)
				printf(
			"Found AB index in erase block #%d (offset %x)\n",
					i, ptr - image);
			index_blk_num = i;
			indexblk = ptr;
			abcnt++;
			continue;
		case 0xBD:
		case 0xBF:
			continue;
		}
		fprintf(stderr,
		"warning: non-understood block type %02X at offset %x\n",
			ptr[8], ptr - image);
	}
	if (!indexblk) {
		fprintf(stderr, "could not find an MPFFS index block in %s\n",
			imgfile);
		exit(1);
	}
	if (abcnt > 1) {
		fprintf(stderr, "found more than one AB block; use -a\n");
		exit(1);
	}
	return(0);
}

get_index_entry(oi)
	struct objinfo *oi;
{
	struct mpffs_index *le;

	if (oi->entryno >= (eraseblk_size >> 4)) {
		fprintf(stderr,
		"error: index block pointer %x past the erase block size!\n",
			oi->entryno);
		exit(1);
	}
	le = (struct mpffs_index *) indexblk + oi->entryno;
	oi->idxrec = le;
	oi->len = le16toh(le->len);
	oi->type = le->type;
	oi->descend = le16toh(le->descend);
	oi->sibling = le16toh(le->sibling);
	return(0);
}

validate_chunk(oi)
	struct objinfo *oi;
{
	u32 dptr;

	if (oi->len & 0xF || !oi->len) {
		fprintf(stderr, "index entry #%x: invalid chunk length\n",
			oi->entryno);
		exit(1);
	}
	dptr = le32toh(oi->idxrec->dataptr);
	if (dptr > 0x0FFFFFFF) {
invdptr:	fprintf(stderr, "index entry #%x: invalid data pointer\n",
			oi->entryno);
		exit(1);
	}
	dptr <<= 4;
	if (dptr > total_ffs_size - oi->len)
		goto invdptr;
	oi->offset = dptr;
	oi->dataptr = image + dptr;
	return(0);
}

validate_obj_name(oi)
	struct objinfo *oi;
{
	u8 *cp;
	int cnt;

	for (cp = oi->dataptr, cnt = 0; ; cp++, cnt++) {
		if (cnt >= oi->len) {
			fprintf(stderr,
		"object at index %x: name expected at %x: length overrun\n",
				oi->entryno, oi->offset);
			exit(1);
		}
		if (!*cp)
			break;
		if (*cp < '!' || *cp > '~') {
			fprintf(stderr,
		"object at index %x: name expected at %x: bad character\n",
				oi->entryno, oi->offset);
			exit(1);
		}
	}
	if (!cnt) {
		fprintf(stderr,
		"object at index %x: name expected at %x: null string\n",
			oi->entryno, oi->offset);
		exit(1);
	}
	return(0);
}

u8 *
find_end_of_chunk(ch)
	struct objinfo *ch;
{
	u8 *p;
	int i;

	p = ch->dataptr + ch->len;
	for (i = 1; i <= 16; i++) {
		if (!p[-i])
			return(p - i);
		if (p[-1] != 0xFF)
			break;
	}
	fprintf(stderr,
	"chunk starting at %x (index entry %x): no valid termination found\n",
		ch->offset, ch->entryno);
	exit(1);
}

size_head_chunk(ch, chi)
	struct objinfo *ch;
	struct chunkinfo *chi;
{
	chi->start = (u8 *) index((char *)ch->dataptr, '\0') + 1;
	chi->end = find_end_of_chunk(ch);
	if (chi->start >= chi->end) {
		chi->len = 0;
		return(0);
	} else {
		chi->len = chi->end - chi->start;
		return(1);
	}
}

size_extra_chunk(ch, chi)
	struct objinfo *ch;
	struct chunkinfo *chi;
{
	chi->start = ch->dataptr;
	chi->end = find_end_of_chunk(ch);
	chi->len = chi->end - chi->start;
}

find_root_node()
{
	struct objinfo obj;
	u16 lim;

	lim = (eraseblk_size >> 4) - 1;
	if (root_node_no) {
		if (root_node_no < 1 || root_node_no > lim) {
			fprintf(stderr,
				"root node # given with -r is invalid\n");
			exit(1);
		}
		return(1);
	}
	for (obj.entryno = 1; obj.entryno <= lim; obj.entryno++) {
		get_index_entry(&obj);
		if (!bcmp(obj.idxrec, blank_flash_line, 16))
			break;
		if (obj.type != 0xF2)
			continue;
		validate_chunk(&obj);
		validate_obj_name(&obj);
		if (*obj.dataptr != '/')
			continue;
		root_node_no = obj.entryno;
		if (verbose)
			printf("Found root node at index #%x\n", root_node_no);
		return(0);
	}
	fprintf(stderr, "error: no root node found (try -r)\n");
	exit(1);
}

validate_root_node()
{
	root.entryno = root_node_no;
	get_index_entry(&root);
	validate_chunk(&root);
	validate_obj_name(&root);
	if (verbose)
		printf("Root node name: %s\n", (char *)root.dataptr);
	if (root.type != 0xF2) {
		fprintf(stderr,
	"error: index entry #%x (expected root dir) is not a directory\n",
			root_node_no);
		exit(1);
	}
	if (root.sibling != 0xFFFF)
		fprintf(stderr,
			"warning: root entry has a non-nil sibling pointer\n");
	return(0);
}

preliminaries()
{
	read_img_file();
	find_index_block();
	find_root_node();
	validate_root_node();
}