blob: 28026823e044143212cd142de2499c7e02b92fd7 [file] [log] [blame]
/*
* 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:
*/
#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"
#define PROGRAM "jffs2dump"
#define VERSION "$Revision: 1.10 $"
#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
void display_help (void)
{
printf("Usage: " PROGRAM " [OPTION]... INPUTFILE\n"
"Dump the contents of a binary JFFS2 image.\n\n"
" --help display this help and exit\n"
" --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");
exit(0);
}
void display_version (void)
{
printf(PROGRAM " " VERSION "\n"
"\n"
"Copyright (C) 2003 Thomas Gleixner \n"
"\n"
PROGRAM " comes with NO WARRANTY\n"
"to the extent permitted by law.\n"
"\n"
"You may redistribute copies of " PROGRAM "\n"
"under the terms of the GNU General Public Licence.\n"
"See the file `COPYING' for more information.\n");
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
void process_options (int argc, char *argv[])
{
int error = 0;
for (;;) {
int option_index = 0;
static const char *short_options = "blce:rd:o:v";
static const struct option long_options[] = {
{"help", no_argument, 0, 0},
{"version", no_argument, 0, 0},
{"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 0:
switch (option_index) {
case 0:
display_help();
break;
case 1:
display_version();
break;
}
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;
strcpy (cnvfile, optarg);
break;
case 'r':
recalccrc = 1;
break;
case '?':
error = 1;
break;
}
}
if ((argc - optind) != 1 || error)
display_help ();
img = argv[optind];
}
/*
* Dump image contents
*/
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%08x to 0x%08x\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%08x, 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 = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
if (crc != je32_to_cpu (node->u.hdr_crc)) {
printf ("Wrong hdr_crc at 0x%08x, 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%08x, 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 = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8);
if (crc != je32_to_cpu (node->i.node_crc)) {
printf ("Wrong node_crc at 0x%08x, 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 = 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%08x, 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%08x, 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 = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8);
if (crc != je32_to_cpu (node->d.node_crc)) {
printf ("Wrong node_crc at 0x%08x, 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 = 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%08x, 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_SUMMARY: {
int i;
struct jffs2_sum_marker * sm;
printf("%8s Inode Sum node at 0x%08x, 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 = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8);
if (crc != je32_to_cpu (node->s.node_crc)) {
printf ("Wrong node_crc at 0x%08x, 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 = 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%08x, 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;
}
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%08x, 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%08x, 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%08x, 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
*/
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 (fd, p, 4);
p += 4;
continue;
}
if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
printf ("Wrong bitmask at 0x%08x, 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 (fd, &newnode, 4);
p += 4;
continue;
}
crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4);
if (crc != je32_to_cpu (node->u.hdr_crc)) {
printf ("Wrong hdr_crc at 0x%08x, 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 (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 ( 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 (crc32 (0, &newnode, sizeof (struct jffs2_raw_inode) - 8));
write (fd, &newnode, sizeof (struct jffs2_raw_inode));
write (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 (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 (crc32 (0, &newnode, sizeof (struct jffs2_raw_dirent) - 8));
if (recalccrc)
newnode.d.name_crc = cpu_to_e32 ( crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize));
else
newnode.d.name_crc = cnv_e32 (node->d.name_crc);
write (fd, &newnode, sizeof (struct jffs2_raw_dirent));
write (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_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 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
write (fd, &newnode, sizeof (struct jffs2_unknown_node));
len = PAD(je32_to_cpu (node->u.totlen) - sizeof (struct jffs2_unknown_node));
if (len > 0)
write (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 (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 (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;
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 ( 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(fd, &newnode, sizeof (struct jffs2_raw_summary));
// write out new summary data
write(fd, &node->s.sum, sum_len + sizeof (struct jffs2_sum_marker));
break;
}
case 0xffff:
write (fd, p, 4);
p += 4;
break;
default:
printf ("Unknown node type: 0x%04x at 0x%08x, 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(1);
}
// get image length
imglen = lseek(fd, 0, SEEK_END);
lseek (fd, 0, SEEK_SET);
data = malloc (imglen);
if (!data) {
perror("out of memory");
close (fd);
exit(1);
}
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 (fd, &data[idx], datsize);
read (fd, oob, oobsize);
idx += datsize;
imglen -= oobsize;
len -= datsize + oobsize;
}
} else {
// read image data
read (fd, data, imglen);
}
// Close the input file
close(fd);
if (dumpcontent)
do_dumpcontent ();
if (convertendian)
do_endianconvert ();
// free memory
free (data);
// Return happy
exit (0);
}