/*
 *  dumpjffs2.c
 *
 *  Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Overview:
 *   This utility dumps the contents of a binary JFFS2 image
 *
 *
 * Bug/ToDo:
 */

#define PROGRAM_NAME "jffs2dump"

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <asm/types.h>
#include <dirent.h>
#include <mtd/jffs2-user.h>
#include <endian.h>
#include <byteswap.h>
#include <getopt.h>
#include <crc32.h>
#include "summary.h"
#include "common.h"

#define PAD(x) (((x)+3)&~3)

/* For outputting a byte-swapped version of the input image. */
#define cnv_e32(x) ((jint32_t){bswap_32(x.v32)})
#define cnv_e16(x) ((jint16_t){bswap_16(x.v16)})

#define t32_backwards(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?bswap_32(__b):__b; })
#define cpu_to_e32(x) ((jint32_t){t32_backwards(x)})

// Global variables
long	imglen;		// length of image
char	*data;		// image data

static void display_help (int error)
{
	printf("Usage: %s [OPTION]... INPUTFILE\n"
	       "Dump the contents of a binary JFFS2 image.\n\n"
	       " -h, --help                   display this help and exit\n"
	       " -V, --version                display version information and exit\n"
	       " -b, --bigendian              image is big endian\n"
	       " -l, --littleendian           image is little endian\n"
	       " -c, --content                dump image contents\n"
	       " -e, --endianconvert=FNAME    convert image endianness, output to file fname\n"
	       " -r, --recalccrc              recalc name and data crc on endian conversion\n"
	       " -d, --datsize=LEN            size of data chunks, when oob data in binary image (NAND only)\n"
	       " -o, --oobsize=LEN            size of oob data chunk in binary image (NAND only)\n"
	       " -v, --verbose                verbose output\n",
	       PROGRAM_NAME);
	exit(error ? EXIT_FAILURE : EXIT_SUCCESS);
}

static void display_version (void)
{
	common_print_version();
	printf("Copyright (C) 2003 Thomas Gleixner \n"
			"\n"
			"%1$s comes with NO WARRANTY\n"
			"to the extent permitted by law.\n"
			"\n"
			"You may redistribute copies of %1$s\n"
			"under the terms of the GNU General Public Licence.\n"
			"See the file `COPYING' for more information.\n",
			PROGRAM_NAME);
	exit(0);
}

// Option variables

int 	verbose;		// verbose output
char 	*img;			// filename of image
int	dumpcontent;		// dump image content
int	target_endian = __BYTE_ORDER;	// image endianess
int	convertendian;		// convert endianness
int	recalccrc;		// recalc name and data crc's on endian conversion
char	cnvfile[256];		// filename for conversion output
int	datsize;		// Size of data chunks, when oob data is inside the binary image
int	oobsize;		// Size of oob chunks, when oob data is inside the binary image

static void process_options (int argc, char *argv[])
{
	int error = 0;

	for (;;) {
		int option_index = 0;
		static const char *short_options = "blce:rd:o:vVh";
		static const struct option long_options[] = {
			{"help", no_argument, 0, 'h'},
			{"version", no_argument, 0, 'V'},
			{"bigendian", no_argument, 0, 'b'},
			{"littleendian", no_argument, 0, 'l'},
			{"content", no_argument, 0, 'c'},
			{"endianconvert", required_argument, 0, 'e'},
			{"datsize", required_argument, 0, 'd'},
			{"oobsize", required_argument, 0, 'o'},
			{"recalccrc", required_argument, 0, 'r'},
			{"verbose", no_argument, 0, 'v'},
			{0, 0, 0, 0},
		};

		int c = getopt_long(argc, argv, short_options,
				long_options, &option_index);
		if (c == EOF) {
			break;
		}

		switch (c) {
			case 'h':
				display_help(0);
				break;
			case 'V':
				display_version();
				break;
			case 'v':
				verbose = 1;
				break;
			case 'b':
				target_endian = __BIG_ENDIAN;
				break;
			case 'l':
				target_endian = __LITTLE_ENDIAN;
				break;
			case 'c':
				dumpcontent = 1;
				break;
			case 'd':
				datsize = atoi(optarg);
				break;
			case 'o':
				oobsize = atoi(optarg);
				break;
			case 'e':
				convertendian = 1;
				strncpy (cnvfile, optarg, sizeof(cnvfile) - 1);
				cnvfile[sizeof(cnvfile) - 1] = '\0';
				break;
			case 'r':
				recalccrc = 1;
				break;
			case '?':
				error = 1;
				break;
		}
	}

	if ((argc - optind) != 1 || error)
		display_help (error);

	img = argv[optind];
}


/*
 *	Dump image contents
 */
static void do_dumpcontent (void)
{
	char			*p = data, *p_free_begin;
	union jffs2_node_union 	*node;
	int			empty = 0, dirty = 0;
	char			name[256];
	uint32_t		crc;
	uint16_t		type;
	int			bitchbitmask = 0;
	int			obsolete;

	p_free_begin = NULL;
	while ( p < (data + imglen)) {
		node = (union jffs2_node_union*) p;

		/* Skip empty space */
		if (!p_free_begin)
			p_free_begin = p;
		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
			p += 4;
			empty += 4;
			continue;
		}

		if (p != p_free_begin)
			printf("Empty space found from 0x%08zx to 0x%08zx\n", p_free_begin-data, p-data);
		p_free_begin = NULL;

		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{
			if (!bitchbitmask++)
				printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic));
			p += 4;
			dirty += 4;
			continue;
		}
		bitchbitmask = 0;

		type = je16_to_cpu(node->u.nodetype);
		if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) {
			obsolete = 1;
			type |= JFFS2_NODE_ACCURATE;
		} else
			obsolete = 0;
		/* Set accurate for CRC check */
		node->u.nodetype = cpu_to_je16(type);

		crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
		if (crc != je32_to_cpu (node->u.hdr_crc)) {
			printf ("Wrong hdr_crc  at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc);
			p += 4;
			dirty += 4;
			continue;
		}

		switch(je16_to_cpu(node->u.nodetype)) {

			case JFFS2_NODETYPE_INODE:
				printf ("%8s Inode      node at 0x%08zx, totlen 0x%08x, #ino  %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n",
						obsolete ? "Obsolete" : "",
						p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino),
						je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize),
						je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset));

				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8);
				if (crc != je32_to_cpu (node->i.node_crc)) {
					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc);
					p += PAD(je32_to_cpu (node->i.totlen));
					dirty += PAD(je32_to_cpu (node->i.totlen));;
					continue;
				}

				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize));
				if (crc != je32_to_cpu(node->i.data_crc)) {
					printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc);
					p += PAD(je32_to_cpu (node->i.totlen));
					dirty += PAD(je32_to_cpu (node->i.totlen));;
					continue;
				}

				p += PAD(je32_to_cpu (node->i.totlen));
				break;

			case JFFS2_NODETYPE_DIRENT:
				memcpy (name, node->d.name, node->d.nsize);
				name [node->d.nsize] = 0x0;
				printf ("%8s Dirent     node at 0x%08zx, totlen 0x%08x, #pino %5d, version %5d, #ino  %8d, nsize %8d, name %s\n",
						obsolete ? "Obsolete" : "",
						p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino),
						je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino),
						node->d.nsize, name);

				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8);
				if (crc != je32_to_cpu (node->d.node_crc)) {
					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc);
					p += PAD(je32_to_cpu (node->d.totlen));
					dirty += PAD(je32_to_cpu (node->d.totlen));;
					continue;
				}

				crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize);
				if (crc != je32_to_cpu(node->d.name_crc)) {
					printf ("Wrong name_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc);
					p += PAD(je32_to_cpu (node->d.totlen));
					dirty += PAD(je32_to_cpu (node->d.totlen));;
					continue;
				}

				p += PAD(je32_to_cpu (node->d.totlen));
				break;

			case JFFS2_NODETYPE_XATTR:
				memcpy(name, node->x.data, node->x.name_len);
				name[node->x.name_len] = '\x00';
				printf ("%8s Xattr      node at 0x%08zx, totlen 0x%08x, xid   %5d, version %5d, name_len   %3d, name %s\n",
						obsolete ? "Obsolete" : "",
						p - data,
						je32_to_cpu (node->x.totlen),
						je32_to_cpu (node->x.xid),
						je32_to_cpu (node->x.version),
						node->x.name_len,
						name);

				crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_xattr) - sizeof (node->x.node_crc));
				if (crc != je32_to_cpu (node->x.node_crc)) {
					printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.node_crc), crc);
					p += PAD(je32_to_cpu (node->x.totlen));
					dirty += PAD(je32_to_cpu (node->x.totlen));
					continue;
				}

				crc = mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1);
				if (crc != je32_to_cpu (node->x.data_crc)) {
					printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->x.data_crc), crc);
					p += PAD(je32_to_cpu (node->x.totlen));
					dirty += PAD(je32_to_cpu (node->x.totlen));
					continue;
				}
				p += PAD(je32_to_cpu (node->x.totlen));
				break;

			case JFFS2_NODETYPE_XREF:
				printf ("%8s Xref       node at 0x%08zx, totlen 0x%08x, xid   %5d, xseqno  %5d, #ino  %8d\n",
						obsolete ? "Obsolete" : "",
						p - data,
						je32_to_cpu (node->r.totlen),
						je32_to_cpu (node->r.xid),
						je32_to_cpu (node->r.xseqno),
						je32_to_cpu (node->r.ino));
				p += PAD(je32_to_cpu (node->r.totlen));
				break;

			case JFFS2_NODETYPE_SUMMARY: {

											 int i;
											 struct jffs2_sum_marker * sm;

											 printf("%8s Inode Sum  node at 0x%08zx, totlen 0x%08x, sum_num  %5d, cleanmarker size %5d\n",
													 obsolete ? "Obsolete" : "",
													 p - data,
													 je32_to_cpu (node->s.totlen),
													 je32_to_cpu (node->s.sum_num),
													 je32_to_cpu (node->s.cln_mkr));

											 crc = mtd_crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8);
											 if (crc != je32_to_cpu (node->s.node_crc)) {
												 printf ("Wrong node_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc);
												 p += PAD(je32_to_cpu (node->s.totlen));
												 dirty += PAD(je32_to_cpu (node->s.totlen));;
												 continue;
											 }

											 crc = mtd_crc32(0, p + sizeof (struct jffs2_raw_summary),  je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary));
											 if (crc != je32_to_cpu(node->s.sum_crc)) {
												 printf ("Wrong data_crc at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc);
												 p += PAD(je32_to_cpu (node->s.totlen));
												 dirty += PAD(je32_to_cpu (node->s.totlen));;
												 continue;
											 }

											 if (verbose) {
												 void *sp;
												 sp = (p + sizeof(struct jffs2_raw_summary));

												 for(i=0; i<je32_to_cpu(node->s.sum_num); i++) {

													 switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
														 case JFFS2_NODETYPE_INODE : {

																						 struct jffs2_sum_inode_flash *spi;
																						 spi = sp;

																						 printf ("%14s #ino  %5d,  version %5d, offset 0x%08x, totlen 0x%08x\n",
																								 "",
																								 je32_to_cpu (spi->inode),
																								 je32_to_cpu (spi->version),
																								 je32_to_cpu (spi->offset),
																								 je32_to_cpu (spi->totlen));

																						 sp += JFFS2_SUMMARY_INODE_SIZE;
																						 break;
																					 }

														 case JFFS2_NODETYPE_DIRENT : {

																						  char name[255];
																						  struct jffs2_sum_dirent_flash *spd;
																						  spd = sp;

																						  memcpy(name,spd->name,spd->nsize);
																						  name [spd->nsize] = 0x0;

																						  printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino  %5d,  version %5d, #ino  %8d, nsize %8d, name %s \n",
																								  "",
																								  je32_to_cpu (spd->offset),
																								  je32_to_cpu (spd->totlen),
																								  je32_to_cpu (spd->pino),
																								  je32_to_cpu (spd->version),
																								  je32_to_cpu (spd->ino),
																								  spd->nsize,
																								  name);

																						  sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
																						  break;
																					  }

														 case JFFS2_NODETYPE_XATTR : {
																						  struct jffs2_sum_xattr_flash *spx;
																						  spx = sp;
																						  printf ("%14s Xattr  offset 0x%08x, totlen 0x%08x, version %5d, #xid %8d\n",
																								  "",
																								  je32_to_cpu (spx->offset),
																								  je32_to_cpu (spx->totlen),
																								  je32_to_cpu (spx->version),
																								  je32_to_cpu (spx->xid));
																						  sp += JFFS2_SUMMARY_XATTR_SIZE;
																						  break;
																					  }

														 case JFFS2_NODETYPE_XREF : {
																						  struct jffs2_sum_xref_flash *spr;
																						  spr = sp;
																						  printf ("%14s Xref   offset 0x%08x\n",
																								  "",
																								  je32_to_cpu (spr->offset));
																						  sp += JFFS2_SUMMARY_XREF_SIZE;
																						  break;
																					  }

														 default :
																					  printf("Unknown summary node!\n");
																					  break;
													 }
												 }

												 sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker));

												 printf("%14s Sum Node Offset  0x%08x, Magic 0x%08x, Padded size 0x%08x\n",
														 "",
														 je32_to_cpu(sm->offset),
														 je32_to_cpu(sm->magic),
														 je32_to_cpu(node->s.padded));
											 }

											 p += PAD(je32_to_cpu (node->s.totlen));
											 break;
										 }

			case JFFS2_NODETYPE_CLEANMARKER:
										 if (verbose) {
											 printf ("%8s Cleanmarker     at 0x%08zx, totlen 0x%08x\n",
													 obsolete ? "Obsolete" : "",
													 p - data, je32_to_cpu (node->u.totlen));
										 }
										 p += PAD(je32_to_cpu (node->u.totlen));
										 break;

			case JFFS2_NODETYPE_PADDING:
										 if (verbose) {
											 printf ("%8s Padding    node at 0x%08zx, totlen 0x%08x\n",
													 obsolete ? "Obsolete" : "",
													 p - data, je32_to_cpu (node->u.totlen));
										 }
										 p += PAD(je32_to_cpu (node->u.totlen));
										 break;

			case 0xffff:
										 p += 4;
										 empty += 4;
										 break;

			default:
										 if (verbose) {
											 printf ("%8s Unknown    node at 0x%08zx, totlen 0x%08x\n",
													 obsolete ? "Obsolete" : "",
													 p - data, je32_to_cpu (node->u.totlen));
										 }
										 p += PAD(je32_to_cpu (node->u.totlen));
										 dirty += PAD(je32_to_cpu (node->u.totlen));

		}
	}

	if (verbose)
		printf ("Empty space: %d, dirty space: %d\n", empty, dirty);
}

/*
 *	Convert endianess
 */
static void do_endianconvert (void)
{
	char			*p = data;
	union jffs2_node_union 	*node, newnode;
	int			fd, len;
	jint32_t		mode;
	uint32_t		crc;

	fd = open (cnvfile, O_WRONLY | O_CREAT, 0644);
	if (fd < 0) {
		fprintf (stderr, "Cannot open / create file: %s\n", cnvfile);
		return;
	}

	while ( p < (data + imglen)) {
		node = (union jffs2_node_union*) p;

		/* Skip empty space */
		if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
			write_nocheck (fd, p, 4);
			p += 4;
			continue;
		}

		if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK)	{
			printf ("Wrong bitmask  at  0x%08zx, 0x%04x\n", p - data, je16_to_cpu (node->u.magic));
			newnode.u.magic = cnv_e16 (node->u.magic);
			newnode.u.nodetype = cnv_e16 (node->u.nodetype);
			write_nocheck (fd, &newnode, 4);
			p += 4;
			continue;
		}

		crc = mtd_crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
		if (crc != je32_to_cpu (node->u.hdr_crc)) {
			printf ("Wrong hdr_crc  at  0x%08zx, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc);
		}

		switch(je16_to_cpu(node->u.nodetype)) {

			case JFFS2_NODETYPE_INODE:

				newnode.i.magic = cnv_e16 (node->i.magic);
				newnode.i.nodetype = cnv_e16 (node->i.nodetype);
				newnode.i.totlen = cnv_e32 (node->i.totlen);
				newnode.i.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
				newnode.i.ino = cnv_e32 (node->i.ino);
				newnode.i.version = cnv_e32 (node->i.version);
				mode.v32 = node->i.mode.m;
				mode = cnv_e32 (mode);
				newnode.i.mode.m = mode.v32;
				newnode.i.uid = cnv_e16 (node->i.uid);
				newnode.i.gid = cnv_e16 (node->i.gid);
				newnode.i.isize = cnv_e32 (node->i.isize);
				newnode.i.atime = cnv_e32 (node->i.atime);
				newnode.i.mtime = cnv_e32 (node->i.mtime);
				newnode.i.ctime = cnv_e32 (node->i.ctime);
				newnode.i.offset = cnv_e32 (node->i.offset);
				newnode.i.csize = cnv_e32 (node->i.csize);
				newnode.i.dsize = cnv_e32 (node->i.dsize);
				newnode.i.compr = node->i.compr;
				newnode.i.usercompr = node->i.usercompr;
				newnode.i.flags = cnv_e16 (node->i.flags);
				if (recalccrc) {
					len = je32_to_cpu(node->i.csize);
					newnode.i.data_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_inode), len));
				} else
					newnode.i.data_crc = cnv_e32 (node->i.data_crc);

				newnode.i.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8));

				write_nocheck (fd, &newnode, sizeof (struct jffs2_raw_inode));
				write_nocheck (fd, p + sizeof (struct jffs2_raw_inode), PAD (je32_to_cpu (node->i.totlen) -  sizeof (struct jffs2_raw_inode)));

				p += PAD(je32_to_cpu (node->i.totlen));
				break;

			case JFFS2_NODETYPE_DIRENT:
				newnode.d.magic = cnv_e16 (node->d.magic);
				newnode.d.nodetype = cnv_e16 (node->d.nodetype);
				newnode.d.totlen = cnv_e32 (node->d.totlen);
				newnode.d.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
				newnode.d.pino = cnv_e32 (node->d.pino);
				newnode.d.version = cnv_e32 (node->d.version);
				newnode.d.ino = cnv_e32 (node->d.ino);
				newnode.d.mctime = cnv_e32 (node->d.mctime);
				newnode.d.nsize = node->d.nsize;
				newnode.d.type = node->d.type;
				newnode.d.unused[0] = node->d.unused[0];
				newnode.d.unused[1] = node->d.unused[1];
				newnode.d.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8));
				if (recalccrc)
					newnode.d.name_crc = cpu_to_e32 ( mtd_crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize));
				else
					newnode.d.name_crc = cnv_e32 (node->d.name_crc);

				write_nocheck (fd, &newnode, sizeof (struct jffs2_raw_dirent));
				write_nocheck (fd, p + sizeof (struct jffs2_raw_dirent), PAD (je32_to_cpu (node->d.totlen) -  sizeof (struct jffs2_raw_dirent)));
				p += PAD(je32_to_cpu (node->d.totlen));
				break;

			case JFFS2_NODETYPE_XATTR:
				newnode.x.magic = cnv_e16 (node->x.magic);
				newnode.x.nodetype = cnv_e16 (node->x.nodetype);
				newnode.x.totlen = cnv_e32 (node->x.totlen);
				newnode.x.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
				newnode.x.xid = cnv_e32 (node->x.xid);
				newnode.x.version = cnv_e32 (node->x.version);
				newnode.x.xprefix = node->x.xprefix;
				newnode.x.name_len = node->x.name_len;
				newnode.x.value_len = cnv_e16 (node->x.value_len);
				if (recalccrc)
					newnode.x.data_crc = cpu_to_e32 (mtd_crc32 (0, p + sizeof (struct jffs2_raw_xattr), node->x.name_len + je16_to_cpu (node->x.value_len) + 1));
				else
					newnode.x.data_crc = cnv_e32 (node->x.data_crc);
				newnode.x.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xattr) - sizeof (newnode.x.node_crc)));

				write_nocheck (fd, &newnode, sizeof (struct jffs2_raw_xattr));
				write_nocheck (fd, p + sizeof (struct jffs2_raw_xattr), PAD (je32_to_cpu (node->d.totlen) -  sizeof (struct jffs2_raw_xattr)));
				p += PAD(je32_to_cpu (node->x.totlen));
				break;

			case JFFS2_NODETYPE_XREF:
				newnode.r.magic = cnv_e16 (node->r.magic);
				newnode.r.nodetype = cnv_e16 (node->r.nodetype);
				newnode.r.totlen = cnv_e32 (node->r.totlen);
				newnode.r.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - sizeof (newnode.r.hdr_crc)));
				newnode.r.ino = cnv_e32 (node->r.ino);
				newnode.r.xid = cnv_e32 (node->r.xid);
				newnode.r.xseqno = cnv_e32 (node->r.xseqno);
				newnode.r.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_xref) - sizeof (newnode.r.node_crc)));
				p += PAD(je32_to_cpu (node->x.totlen));
				break;

			case JFFS2_NODETYPE_CLEANMARKER:
			case JFFS2_NODETYPE_PADDING:
				newnode.u.magic = cnv_e16 (node->u.magic);
				newnode.u.nodetype = cnv_e16 (node->u.nodetype);
				newnode.u.totlen = cnv_e32 (node->u.totlen);
				newnode.u.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));

				write_nocheck (fd, &newnode, sizeof (struct jffs2_unknown_node));
				len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node));
				if (len > 0)
					write_nocheck (fd, p + sizeof (struct jffs2_unknown_node), len);

				p += PAD(je32_to_cpu (node->u.totlen));
				break;

			case JFFS2_NODETYPE_SUMMARY : {
											  struct jffs2_sum_marker *sm_ptr;
											  int i,sum_len;
											  int counter = 0;

											  newnode.s.magic = cnv_e16 (node->s.magic);
											  newnode.s.nodetype = cnv_e16 (node->s.nodetype);
											  newnode.s.totlen = cnv_e32 (node->s.totlen);
											  newnode.s.hdr_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
											  newnode.s.sum_num = cnv_e32 (node->s.sum_num);
											  newnode.s.cln_mkr = cnv_e32 (node->s.cln_mkr);
											  newnode.s.padded = cnv_e32 (node->s.padded);

											  newnode.s.node_crc = cpu_to_e32 (mtd_crc32 (0, &newnode, sizeof (struct jffs2_raw_summary) - 8));

											  // summary header
											  p += sizeof (struct jffs2_raw_summary);

											  // summary data
											  sum_len = je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary) - sizeof (struct jffs2_sum_marker);

											  for (i=0; i<je32_to_cpu (node->s.sum_num); i++) {
												  union jffs2_sum_flash *fl_ptr;

												  fl_ptr = (union jffs2_sum_flash *) p;

												  switch (je16_to_cpu (fl_ptr->u.nodetype)) {
													  case JFFS2_NODETYPE_INODE:

														  fl_ptr->i.nodetype = cnv_e16 (fl_ptr->i.nodetype);
														  fl_ptr->i.inode = cnv_e32 (fl_ptr->i.inode);
														  fl_ptr->i.version = cnv_e32 (fl_ptr->i.version);
														  fl_ptr->i.offset = cnv_e32 (fl_ptr->i.offset);
														  fl_ptr->i.totlen = cnv_e32 (fl_ptr->i.totlen);
														  p += sizeof (struct jffs2_sum_inode_flash);
														  counter += sizeof (struct jffs2_sum_inode_flash);
														  break;

													  case JFFS2_NODETYPE_DIRENT:
														  fl_ptr->d.nodetype = cnv_e16 (fl_ptr->d.nodetype);
														  fl_ptr->d.totlen = cnv_e32 (fl_ptr->d.totlen);
														  fl_ptr->d.offset = cnv_e32 (fl_ptr->d.offset);
														  fl_ptr->d.pino = cnv_e32 (fl_ptr->d.pino);
														  fl_ptr->d.version = cnv_e32 (fl_ptr->d.version);
														  fl_ptr->d.ino = cnv_e32 (fl_ptr->d.ino);
														  p += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize;
														  counter += sizeof (struct jffs2_sum_dirent_flash) + fl_ptr->d.nsize;
														  break;

													  case JFFS2_NODETYPE_XATTR:
														  fl_ptr->x.nodetype = cnv_e16 (fl_ptr->x.nodetype);
														  fl_ptr->x.xid = cnv_e32 (fl_ptr->x.xid);
														  fl_ptr->x.version = cnv_e32 (fl_ptr->x.version);
														  fl_ptr->x.offset = cnv_e32 (fl_ptr->x.offset);
														  fl_ptr->x.totlen = cnv_e32 (fl_ptr->x.totlen);
														  p += sizeof (struct jffs2_sum_xattr_flash);
														  counter += sizeof (struct jffs2_sum_xattr_flash);
														  break;

													  case JFFS2_NODETYPE_XREF:
														  fl_ptr->r.nodetype = cnv_e16 (fl_ptr->r.nodetype);
														  fl_ptr->r.offset = cnv_e32 (fl_ptr->r.offset);
														  p += sizeof (struct jffs2_sum_xref_flash);
														  counter += sizeof (struct jffs2_sum_xref_flash);
														  break;

													  default :
														  printf("Unknown node in summary information!!! nodetype(%x)\n", je16_to_cpu (fl_ptr->u.nodetype));
														  exit(EXIT_FAILURE);
														  break;
												  }

											  }

											  //pad
											  p += sum_len - counter;

											  // summary marker
											  sm_ptr = (struct jffs2_sum_marker *) p;
											  sm_ptr->offset = cnv_e32 (sm_ptr->offset);
											  sm_ptr->magic = cnv_e32 (sm_ptr->magic);
											  p += sizeof (struct jffs2_sum_marker);

											  // generate new crc on sum data
											  newnode.s.sum_crc = cpu_to_e32 ( mtd_crc32(0, ((char *) node) + sizeof (struct jffs2_raw_summary),
														  je32_to_cpu (node->s.totlen) - sizeof (struct jffs2_raw_summary)));

											  // write out new node header
											  write_nocheck(fd, &newnode, sizeof (struct jffs2_raw_summary));
											  // write out new summary data
											  write_nocheck(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker));

											  break;
										  }

			case 0xffff:
										  write_nocheck (fd, p, 4);
										  p += 4;
										  break;

			default:
										  printf ("Unknown node type: 0x%04x at 0x%08zx, totlen 0x%08x\n", je16_to_cpu (node->u.nodetype), p - data, je32_to_cpu (node->u.totlen));
										  p += PAD(je32_to_cpu (node->u.totlen));

		}
	}

	close (fd);

}

/*
 * Main program
 */
int main(int argc, char **argv)
{
	int fd;

	process_options(argc, argv);

	/* Open the input file */
	if ((fd = open(img, O_RDONLY)) == -1) {
		perror("open input file");
		exit(EXIT_FAILURE);
	}

	// get image length
	imglen = lseek(fd, 0, SEEK_END);
	if (imglen < 0) {
		perror(img);
		close(fd);
		exit(EXIT_FAILURE);
	}

	lseek (fd, 0, SEEK_SET);

	data = malloc (imglen);
	if (!data) {
		perror("out of memory");
		close (fd);
		exit(EXIT_FAILURE);
	}

	if (datsize < 0 || oobsize < 0 || datsize > imglen || (long)datsize + oobsize < 0) {
		fprintf(stderr, "Error: invalid datsize/oobsize.\n");
		free(data);
		close (fd);
		exit(EXIT_FAILURE);
	}

	if (datsize && oobsize) {
		int  idx = 0;
		long len = imglen;
		uint8_t oob[oobsize];
		printf ("Peeling data out of combined data/oob image\n");
		while (len) {
			// read image data
			read_nocheck (fd, &data[idx], datsize);
			read_nocheck (fd, oob, oobsize);
			idx += datsize;
			imglen -= oobsize;
			len -= (long)datsize + oobsize;
		}

	} else {
		// read image data
		read_nocheck (fd, data, imglen);
	}
	// Close the input file
	close(fd);

	if (dumpcontent)
		do_dumpcontent ();

	if (convertendian)
		do_endianconvert ();

	// free memory
	free (data);

	// Return happy
	exit (EXIT_SUCCESS);
}
