/* * 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%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_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; } 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 */ 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%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 (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 (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 (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 (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 (mtd_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 (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; 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(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%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(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); }