view mysteryffs/dump2.c @ 312:6cba849e3332

frbl/reconst/inc: import from TCS211 surviving source
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 04 Mar 2020 23:06:07 +0000
parents 9f3a7b014e63
children
line wrap: on
line source

/*
 * This program attempts to traverse the FFS directory tree
 * from the root down, following the descendant and sibling
 * pointers, and dumps everything it encounters.
 *
 * The objective is to understand how to extract the precise
 * content of data files.
 */

#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>

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

u8 mysteryffs_hdr[6] = {'F', 'f', 's', '#', 0x10, 0x02};

struct index_entry {
	u16	len;
	u8	unknown_b1;
	u8	type;
	u16	descend;
	u16	sibling;
	u32	dataptr;
	u16	unknown_w1;
	u16	unknown_w2;
};

char *imgfile;
u32 eraseblk_size;
int total_blocks;
u32 total_img_size;
u8 *image, *indexblk;

char workpath[512];

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 (st.st_size < total_img_size) {
		fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile,
			total_img_size);
		exit(1);
	}
	image = malloc(total_img_size);
	if (!image) {
		perror("malloc");
		exit(1);
	}
	read(fd, image, total_img_size);
	close(fd);
}

find_index_block()
{
	int i;
	u8 *ptr;

	for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) {
		if (bcmp(ptr, mysteryffs_hdr, 6))
			continue;
		if (ptr[8] != 0xAB)
			continue;
		printf("Found index in erase block #%d (offset %x)\n", i,
			ptr - image);
		indexblk = ptr;
		return(0);
	}
	fprintf(stderr, "could not find a MysteryFFS index block in %s\n",
		imgfile);
	exit(1);
}

get_index_entry(num, host)
	int num;
	struct index_entry *host;
{
	struct index_entry *le;

	le = (struct index_entry *) indexblk + num;
	host->len = le16toh(le->len);
	host->unknown_b1 = le->unknown_b1;
	host->type = le->type;
	host->descend = le16toh(le->descend);
	host->sibling = le16toh(le->sibling);
	host->dataptr = le32toh(le->dataptr);
	host->unknown_w1 = le16toh(le->unknown_w1);
	host->unknown_w2 = le16toh(le->unknown_w2);
}

is_namestr_ok(s)
	char *s;
{
	int cnt;

	for (cnt = 0; *s; s++, cnt++) {
		if (cnt >= 32)
			return(0);
		if (!isprint(*s))
			return(0);
	}
	if (cnt)
		return(1);
	else
		return(0);
}

char *
get_name(dptr)
	u32 dptr;
{
	u8 *name;

	if (dptr > 0x0FFFFFFF)
		return(0);
	dptr <<= 4;
	if (dptr >= total_img_size - 32)
		return(0);
	name = image + dptr;
	if (is_namestr_ok(name))
		return(name);
	else
		return(0);
}

dump_common(idx, rec, path_prefix, typestr, newprefix)
	int idx, path_prefix, *newprefix;
	struct index_entry *rec;
	char *typestr;
{
	u8 *name;

	name = get_name(rec->dataptr);
	if (!name) {
		printf("entry #%x has an invalid name pointer!\n", idx);
		return(-1);
	}
	if (sizeof(workpath) - path_prefix < strlen(name) + 2) {
		printf("entry #%x: pathname buffer overflow!\n", idx);
		return(-1);
	}
	path_prefix += sprintf(workpath + path_prefix, "/%s", name);
	printf("\n%s (%s)\n", workpath, typestr);
	printf("len=%x, unknown fields: %02X %04X %04X\n", rec->len,
		rec->unknown_b1, rec->unknown_w1, rec->unknown_w2);
	if (newprefix)
		*newprefix = path_prefix;
	return(0);
}

dump_data_frag(entryidx, rec)
	struct index_entry *rec;
{
	u32 dptr, endptr;
	int i, c;

	if (rec->len & 0xF || !rec->len) {
	    printf("entry #%x: don't know how to dump fragment of length %x\n",
			entryidx, rec->len);
	    return(-1);
	}
	dptr = rec->dataptr;
	if (dptr > 0x0FFFFFFF) {
inv:		printf("entry #%x: invalid data pointer\n", entryidx);
		return(-1);
	}
	dptr <<= 4;
	if (dptr > total_img_size - rec->len)
		goto inv;
	for (endptr = dptr + rec->len; dptr < endptr; dptr += 0x10) {
		printf("%08X:  ", dptr);
		for (i = 0; i < 16; i++) {
			printf("%02X ", image[dptr + i]);
			if (i == 7 || i == 15)
				putchar(' ');
		}
		for (i = 0; i < 16; i++) {
			c = image[dptr + i];
			if (!isprint(c))
				c = '.';
			putchar(c);
		}
		putchar('\n');
	}
	return(0);
}

dump_file_chain(firstent)
{
	struct index_entry rec;
	int ent;

	for (ent = firstent; ent != 0xFFFF; ent = rec.descend) {
		get_index_entry(ent, &rec);
		if (rec.type == 0xF4) {
			printf("\nfile continuation (entry #%x)\n", ent);
			printf("len=%x, unknown fields: %02X %04X %04X\n",
				rec.len, rec.unknown_b1,
				rec.unknown_w1, rec.unknown_w2);
			dump_data_frag(ent, &rec);
		} else {
			printf("\ncontinuation entry at %x: type %02X != F4\n",
				ent, rec.type);
			printf("len=%x, unknown fields: %02X %04X %04X\n",
				rec.len, rec.unknown_b1,
				rec.unknown_w1, rec.unknown_w2);
		}
		if (rec.sibling != 0xFFFF)
			printf("warning: non-nil sibling pointer\n");
	}
}

dump_dir(firstent, path_prefix)
{
	struct index_entry rec;
	int ent;
	int subprefix;

	for (ent = firstent; ent != 0xFFFF; ent = rec.sibling) {
		get_index_entry(ent, &rec);
		switch (rec.type) {
		case 0x00:
			/* deleted object - skip it */
			continue;
		case 0xF2:
			/* subdirectory */
			if (dump_common(ent, &rec, path_prefix, "directory",
					&subprefix) < 0)
				continue;
			dump_dir(rec.descend, subprefix);
			continue;
		case 0xF1:
			/* regular file */
			if (dump_common(ent, &rec, path_prefix, "file", 0) < 0)
				continue;
			dump_data_frag(ent, &rec);
			dump_file_chain(rec.descend);
			continue;
		case 0xE1:
			/* special .journal file */
			if (dump_common(ent, &rec, path_prefix, "E1 file", 0)<0)
				continue;
			dump_data_frag(ent, &rec);
			dump_file_chain(rec.descend);
			continue;
		default:
			printf("entry #%x: unexpected type %02X\n", ent,
				rec.type);
		}
	}
}

dump_root()
{
	struct index_entry rec;
	char *name;

	get_index_entry(1, &rec);
	if (rec.type != 0xF2) {
		fprintf(stderr,
		"error: entry #1 (expected root dir) is not a directory\n");
		exit(1);
	}
	name = get_name(rec.dataptr);
	if (!name) {
		fprintf(stderr, "root entry has an invalid name pointer!\n");
		exit(1);
	}
	printf("Root node name: %s\n", name);
	printf("len=%x, unknown fields: %02X %04X %04X\n", rec.len,
		rec.unknown_b1, rec.unknown_w1, rec.unknown_w2);
	if (rec.sibling != 0xFFFF)
		printf("warning: root entry has a non-nil sibling pointer\n");
	dump_dir(rec.descend, 0);
}

main(argc, argv)
	char **argv;
{
	if (argc != 4) {
		fprintf(stderr, "usage: %s imgfile blksize nblocks\n", argv[0]);
		exit(1);
	}
	imgfile = argv[1];
	eraseblk_size = strtoul(argv[2], 0, 0);
	total_blocks = strtoul(argv[3], 0, 0);
	total_img_size = eraseblk_size * total_blocks;
	read_img_file();
	find_index_block();
	dump_root();
	exit(0);
}