| /* |
| * 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 |
| |
| void display_help (void) |
| { |
| printf("Usage: %s [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", |
| PROGRAM_NAME); |
| exit(0); |
| } |
| |
| void display_version (void) |
| { |
| printf("%1$s " VERSION "\n" |
| "\n" |
| "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 |
| |
| 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_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 |
| */ |
| 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_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 (fd, &newnode, sizeof (struct jffs2_raw_xattr)); |
| write (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 (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; |
| |
| 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(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); |
| } |