aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@shinybook.infradead.org>2006-04-11 19:09:16 -0400
committerDavid Woodhouse <dwmw2@shinybook.infradead.org>2006-04-11 19:09:16 -0400
commit72212c137c574b564723327af751c4054c7cfca6 (patch)
tree13be2da9d4be695002d6ea9c396660c986e5fa5c
Initial commit
-rwxr-xr-xMAKEDEV43
-rw-r--r--Makefile63
-rw-r--r--Makefile.am18
-rw-r--r--checkfs/Makefile14
-rw-r--r--checkfs/README173
-rw-r--r--checkfs/checkfs.c695
-rw-r--r--checkfs/comm.c67
-rw-r--r--checkfs/common.h7
-rw-r--r--checkfs/makefiles.c264
-rw-r--r--compr.c489
-rw-r--r--compr.h115
-rw-r--r--compr_rtime.c122
-rw-r--r--compr_zlib.c148
-rw-r--r--crc32.c97
-rw-r--r--crc32.h21
-rw-r--r--device_table.txt129
-rw-r--r--doc_loadbios.c149
-rw-r--r--docfdisk.c340
-rw-r--r--flash_erase.c188
-rw-r--r--flash_eraseall.c308
-rw-r--r--flash_info.c55
-rw-r--r--flash_lock.c84
-rw-r--r--flash_otp_dump.c54
-rw-r--r--flash_otp_info.c63
-rw-r--r--flash_otp_lock.c70
-rw-r--r--flash_otp_write.c96
-rw-r--r--flash_unlock.c64
-rw-r--r--flashcp.c392
-rw-r--r--ftl_check.c233
-rw-r--r--ftl_format.c343
-rw-r--r--jffs-dump.c361
-rw-r--r--jffs2dump.c725
-rw-r--r--jffs2reader.c941
-rw-r--r--jittertest/COPYING340
-rw-r--r--jittertest/JitterTest.c1044
-rw-r--r--jittertest/Makefile46
-rw-r--r--jittertest/README197
-rw-r--r--jittertest/filljffs2.sh16
-rw-r--r--jittertest/plotJittervsFill.c312
-rw-r--r--mkfs.ffs2.c239
-rw-r--r--mkfs.jffs.c739
-rw-r--r--mkfs.jffs2.1242
-rw-r--r--mkfs.jffs2.c1583
-rw-r--r--mtd-utils.spec40
-rw-r--r--mtd_debug.c445
-rw-r--r--nanddump.c312
-rw-r--r--nandwrite.c460
-rw-r--r--nftl_format.c442
-rw-r--r--nftldump.c299
-rw-r--r--rfddump.c338
-rw-r--r--rfdformat.c160
-rw-r--r--summary.h143
-rw-r--r--sumtool.c822
53 files changed, 15150 insertions, 0 deletions
diff --git a/MAKEDEV b/MAKEDEV
new file mode 100755
index 0000000..121a883
--- /dev/null
+++ b/MAKEDEV
@@ -0,0 +1,43 @@
+#!/bin/bash
+# $Id: MAKEDEV,v 1.9 2005/11/29 11:47:58 sean Exp $
+
+function mkftl () {
+ mknod /dev/ftl$1 b 44 $2
+ for a in `seq 1 15`; do
+ mknod /dev/ftl$1$a b 44 `expr $2 + $a`
+ done
+}
+function mknftl () {
+ mknod /dev/nftl$1 b 93 $2
+ for a in `seq 1 15`; do
+ mknod /dev/nftl$1$a b 93 `expr $2 + $a`
+ done
+}
+function mkrfd () {
+ mknod /dev/rfd$1 b 256 $2
+ for a in `seq 1 15`; do
+ mknod /dev/rfd$1$a b 256 `expr $2 + $a`
+ done
+}
+function mkinftl () {
+ mknod /dev/inftl$1 b 96 $2
+ for a in `seq 1 15`; do
+ mknod /dev/inftl$1$a b 96 `expr $2 + $a`
+ done
+}
+
+M=0
+for C in a b c d e f g h i j k l m n o p; do
+ mkftl $C $M
+ mknftl $C $M
+ mkrfd $C $M
+ mkinftl $C $M
+ let M=M+16
+done
+
+for a in `seq 0 16` ; do
+ mknod /dev/mtd$a c 90 `expr $a + $a`
+ mknod /dev/mtdr$a c 90 `expr $a + $a + 1`
+ mknod /dev/mtdblock$a b 31 $a
+done
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a77c6eb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,63 @@
+
+# -*- sh -*-
+
+# $Id: Makefile,v 1.60 2005/11/07 11:15:09 gleixner Exp $
+
+SBINDIR=/usr/sbin
+MANDIR=/usr/man
+INCLUDEDIR=/usr/include
+#CROSS=arm-linux-
+CC := $(CROSS)gcc
+CFLAGS := -I../include -O2 -Wall
+
+TARGETS = ftl_format flash_erase flash_eraseall nanddump doc_loadbios \
+ mkfs.jffs ftl_check mkfs.jffs2 flash_lock flash_unlock flash_info \
+ flash_otp_info flash_otp_dump mtd_debug flashcp nandwrite \
+ jffs2dump \
+ nftldump nftl_format docfdisk \
+ rfddump rfdformat \
+ sumtool #jffs2reader
+
+SYMLINKS =
+
+%: %.o
+ $(CC) $(LDFLAGS) -g -o $@ $^
+
+%.o: %.c
+ $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$<.dep
+
+.SUFFIXES:
+
+all: $(TARGETS)
+
+IGNORE=${wildcard .*.c.dep}
+-include ${IGNORE}
+
+clean:
+ rm -f *.o $(TARGETS) .*.c.dep $(SYMLINKS)
+
+$(SYMLINKS):
+ ln -sf ../fs/jffs2/$@ $@
+
+mkfs.jffs2: crc32.o compr_rtime.o mkfs.jffs2.o compr_zlib.o compr.o
+ $(CC) $(LDFLAGS) -o $@ $^ -lz
+
+flash_eraseall: crc32.o flash_eraseall.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+jffs2reader: jffs2reader.o
+ $(CC) $(LDFLAGS) -o $@ $^ -lz
+
+jffs2dump: jffs2dump.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+sumtool: sumtool.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+install: ${TARGETS}
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m0755 -oroot -groot ${TARGETS} ${DESTDIR}/${SBINDIR}/
+ mkdir -p ${DESTDIR}/${MANDIR}/man1
+ gzip -c mkfs.jffs2.1 > ${DESTDIR}/${MANDIR}/man1/mkfs.jffs2.1.gz
+ mkdir -p ${DESTDIR}/${INCLUDEDIR}/mtd
+ install -m0644 -oroot -groot ../include/mtd/*.h ${DESTDIR}/${INCLUDEDIR}/mtd/
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..d9818fb
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,18 @@
+## Process this file with automake to produce Makefile.in
+
+sbin_PROGRAMS = ftl_format erase eraseall nftldump nanddump doc_loadbios \
+ nftl_format mkfs.jffs ftl_check nandtest nandwrite mkfs.jffs2 \
+ lock unlock einfo mtd_debug fcp
+
+CFLAGS = -O2 -Wall
+INCLUDES = -I@CONFIG_KERNELDIR@/include -I@CONFIG_KERNELDIR@/fs/jffs2
+SYMLINKS = crc32.h crc32.c compr_rtime.c compr_rubin.c compr_zlib.c compr.c pushpull.c pushpull.h histo_mips.h compr_rubin.h
+CLEANFILES = $(SYMLINKS)
+
+mkfs_jffs2_SOURCES = mkfs.jffs2.c $(SYMLINKS)
+
+$(SYMLINKS):
+ ln -sf @CONFIG_KERNELDIR@/fs/jffs2/$@ $@
+
+compr.o: compr.c
+ $(COMPILE) $(CFLAGS) $(INCLUDES) -Dprintk=printf -DKERN_NOTICE= -DKERN_WARNING= -c -o $@ $<
diff --git a/checkfs/Makefile b/checkfs/Makefile
new file mode 100644
index 0000000..ac94dde
--- /dev/null
+++ b/checkfs/Makefile
@@ -0,0 +1,14 @@
+
+all: checkfs makefiles
+
+checkfs: checkfs.c Makefile common.h comm.o
+ gcc -g -Wall checkfs.c comm.o -o checkfs
+
+comm.o: comm.c Makefile
+ gcc -g -Wall -c comm.c -o comm.o
+
+makefiles: makefiles.c Makefile common.h
+ gcc -g -Wall makefiles.c -o makefiles
+
+clean:
+ rm -f makefiles checkfs *~ *.o
diff --git a/checkfs/README b/checkfs/README
new file mode 100644
index 0000000..d9966a5
--- /dev/null
+++ b/checkfs/README
@@ -0,0 +1,173 @@
+$Id: README,v 1.2 2001/06/21 23:07:06 dwmw2 Exp $
+$Log: README,v $
+Revision 1.2 2001/06/21 23:07:06 dwmw2
+Initial import to MTD CVS
+
+Revision 1.1 2001/06/11 19:34:40 vipin
+Added README file to dir.
+
+
+This is the README file for the "checkfs" power fail test program.
+By: Vipin Malik
+
+NOTE: This program requires an external "power cycling box"
+connected to one of the com ports of the system under test.
+This power cycling box should wait for a random amount of time
+after it receives a "ok to power me down" message over the
+serial port, and then yank power to the system under test.
+(The box that I rigged up tested with waits anywhere from
+0 to ~40 seconds).
+
+
+It should then restore power after a few seconds and wait for the
+message again.
+
+
+ABOUT:
+
+This program's primary purpose it to test the reliiability
+of various file systems under Linux.
+
+SETUP:
+
+You need to setup the file system you want to test and run the
+"makefiles" program ONCE. This creates a set of files that are
+required by the "checkfs" program.
+
+Also copy the "checkfs" executable program to the same dir.
+
+Then you need to make sure that the program "checkfs" is called
+automatically on startup. You can customise the operation of
+the "checkfs" program by passing it various cmd line arguments.
+run "checkfs -?" for more details.
+
+****NOTE*******
+Make sure that you call the checkfs program only after you have
+mounted the file system you want to test (this is obvious), but
+also after you have run any "scan" utilities to check for and
+fix any file systems errors. The e2fsck is one utility for the
+ext2 file system. For an automated setup you of course need to
+provide these scan programs to run in standalone mode (-f -y
+flags for e2fsck for example).
+
+File systems like JFFS and JFFS2 do not have any such external
+utilities and you may call "checkfs" right after you have mounted
+the respective file system under test.
+
+There are two ways you can mount the file system under test:
+
+1. Mount your root fs on a "standard" fs like ext2 and then
+mount the file system under test (which may be ext2 on another
+partition or device) and then run "checkfs" on this mounted
+partition OR
+
+2. Make your fs AND device that you have put this fs as your
+root fs and run "checkfs" on the root device (i.e. "/").
+You can of course still run checkfs under a separate dir
+under your "/" root dir.
+
+I have found the second method to be a particularly stringent
+arrangement (and thus preferred when you are trying to break
+something).
+
+Using this arrangement I was able to find that JFFS clobbered
+some "sister" files on the root fs even though "checkfs" would
+run fine through all its own check files.
+
+(I found this out when one of the clobbered sister file happened
+to be /bin/bash. The system refused to run rc.local thus
+preventing my "checkfs" program from being launched :)
+
+"checkfs":
+
+The "formatting" reliability of the fs as well as the file data integrity
+of files on the fs can be checked using this program.
+
+"formatiing" reliability can only be checked via an indirect method.
+If there is severe formatting reliability issues with the file system,
+it will most likely cause other system failures that will prevent this
+program from running successfully on a power up. This will prevent
+a "ok to power me down" message from going out to the power cycling
+black box and prevent power being turned off again.
+
+File data reliability is checked more directly. A fixed number of
+files are created in the current dir (using the program "makefiles").
+
+Each file has a random number of bytes in it (set by using the
+-s cmd line flag). The number of "ints" in the file is stored as the
+first "int" in it (note: 0 length files are not allowed). Each file
+is then filled with random data and a 16 bit CRC appended at the end.
+
+When "checkfs" is run, it runs through all files (with predetermined
+file names)- one at a time- and checks for the number of "int's"
+in it as well as the ending CRC.
+
+The program exits if the numbers of files that are corrupt are greater
+that a user specified parameter (set by using the -e cmd line flag).
+
+If the number of corrupt files is less than this parameter, the corrupt
+files are repaired and operation resumes as explained below.
+
+The idea behind allowing a user specified amount of corrupt files is as
+follows:
+
+If you are testing for "formatting" reliability of a fs, and for
+the data reliability of "other" files present of the fs, use -e 1.
+"other" files are defined as sister files on the fs, not being written to
+by the "checkfs" test program.
+
+As mentioned, in this case you would set -e 1, or allow at most 1 file
+to be corrupt each time after a power fail. This would be the file
+that was probably being written to when power failed (and CRC was not
+updated to reflect the new data being written). You would check file
+systems like ext2 etc. with such a configuration.
+(As you have no hope that these file systems provide for either your
+new data or old data to be present in the file if power failed during
+the write. This is called "roll back and recover".)
+
+With JFFS2 I tested for such "roll back and recover" file data reliability
+by setting -e 0 and making sure that all writes to the file being
+updated are done in a *single* write().
+
+This is how I found that JFFS2 (yet) does NOT support this functionality.
+(There was a great debate if this was a bug or a feature that was lacking
+or even an issue at all. See the mtd archives for more details).
+
+In other words, JFFS2 will partially update a file on FLASH even before
+the write() command has completed, thus leaving part old data part new
+data in your file if power failed in the middle of a write().
+
+This is bad functionality if you are updating a binary structure or a
+CRC protected file (as in our case).
+
+
+If All Files Check Out OK:
+
+On the startup scan, if there are less errors than specified by the "-e flag"
+a "ok to power me down message" is sent via the specified com port.
+
+The actual format of this message will depend on the format expected
+by the power cycling box that will receive this message. One may customise
+the actual message that goes out in the "do_pwr_dn)" routine in "comm.c".
+
+This file is called with an open file descriptor to the comm port that
+this message needs to go out over and the count of the current power
+cycle (in case your power cycling box can display/log this count).
+
+After this message has been sent out, the checkfs program goes into
+a while(1) loop of writing new data (with CRC), one at a time, into
+all the "check files" in the dir.
+
+Its life comes to a sudden end when power is asynchronously pulled from
+under its feet (by your external power cycling box).
+
+It comes back to life when power is restored and the system boots and
+checkfs is called from the rc.local script file.
+
+The cycle then repeats till a problem is detected, at which point
+the "ok to power me down" message is not sent and the cycle stops
+waiting for the user to examine the system.
+
+
+
+
diff --git a/checkfs/checkfs.c b/checkfs/checkfs.c
new file mode 100644
index 0000000..da2d0c4
--- /dev/null
+++ b/checkfs/checkfs.c
@@ -0,0 +1,695 @@
+/*
+
+ * Copyright Daniel Industries.
+ *
+ * Created by: Vipin Malik (vipin.malik@daniel.com)
+ *
+ * This code is released under the GPL version 2. See the file COPYING
+ * for more details.
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+
+ This program opens files in progression (file00001, file00002 etc),
+ upto MAX_NUM_FILES and checks their CRC. If a file is not found or the
+ CRC does not match it stops it's operation.
+
+ Everything is logged in a logfile called './logfile'.
+
+ If everything is ok this program sends a signal, via com1, to the remote
+ power control box to power cycle this computer.
+
+ This program then proceeds to create new files file0....file<MAX_NUM_FILES>
+ in a endless loop and checksum each before closing them.
+
+ STRUCTURE OF THE FILES:
+ The fist int is the size of the file in bytes.
+ The last 2 bytes are the CRC for the entire file.
+ There is random data in between.
+
+ The files are opened in the current dir.
+
+ $Id: checkfs.c,v 1.8 2005/11/07 11:15:17 gleixner Exp $
+ $Log: checkfs.c,v $
+ Revision 1.8 2005/11/07 11:15:17 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.7 2001/06/21 23:04:17 dwmw2
+ Initial import to MTD CVS
+
+ Revision 1.6 2001/06/08 22:26:05 vipin
+ Split the modbus comm part of the program (that sends the ok to pwr me down
+ message) into another file "comm.c"
+
+ Revision 1.5 2001/06/08 21:29:56 vipin
+ fixed small issue with write() checking for < 0 instead of < (bytes to be written).
+ Now it does the latter (as it should).
+
+ Revision 1.4 2001/05/11 22:29:40 vipin
+ Added a test to check and err out if the first int in file (which tells us
+ how many bytes there are in the file) is zero. This will prevent a corrupt
+ file with zero's in it from passing the crc test.
+
+ Revision 1.3 2001/05/11 21:33:54 vipin
+ Changed to use write() rather than fwrite() when creating new file. Additionally,
+ and more important, it now does a single write() for the entire data. This will
+ enable us to use this program to test for power fail *data* reliability when
+ writing over an existing file, specially on powr fail "safe" file systems as
+ jffs/jffs2. Also added a new cmdline parameter "-e" that specifies the max # of
+ errors that can be tolerated. This should be set to ZERO to test for the above,
+ as old data should be reliabily maintained if the newer write never "took" before
+ power failed. If the write did succeed, then the newer data will have its own
+ CRC in place when it gets checked => hence no error. In theory at least!
+
+
+ Revision 1.2 2001/05/11 19:27:33 vipin
+ Added cmd line args to change serial port, and specify max size of
+ random files created. Some cleanup. Added -Wall to Makefile.
+
+ Revision 1.1 2001/05/11 16:06:28 vipin
+ Importing checkfs (the power fail test program) into CVS. This was
+ originally done for NEWS. NEWS had a lot of version, this is
+ based off the last one done for NEWS. The "makefiles" program
+ is run once initially to create the files in the current dir.
+ "checkfs" is then run on every powerup to check consistancy
+ of the files. See checkfs.c for more details.
+
+
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include "common.h"
+
+
+
+extern int do_pwr_dn(int fd, int cycleCnt);
+
+#define CMDLINE_PORT "-p"
+#define CMDLINE_MAXFILEBYTES "-s"
+#define CMDLINE_MAXERROR "-e"
+#define CMDLINE_HELPSHORT "-?"
+#define CMDLINE_HELPLONG "--help"
+
+
+int CycleCount;
+
+char SerialDevice[255] = "/dev/ttyS0"; /* default, can be changed
+ through cmd line. */
+
+#define MAX_INTS_ALLOW 100000 /* max # of int's in the file written.
+ Statis limit to size struct. */
+float FileSizeMax = 1024.0; /*= (file size in bytes), MUST be float*/
+
+int MaxErrAllowed = 1; /* default, can ge changed thru cmd line*/
+
+
+/* Needed for CRC generation/checking */
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+
+/*
+ Set's up the Linux serial port. Must be passed string to device to
+ open. Parameters are fixed to 9600,e,1
+
+ [A possible enhancement to this program would be to pass these
+ parameters via the command line.]
+
+ Returns file descriptor to open port. Use this fd to write to port
+ and close it later, when done.
+*/
+int setupSerial (const char *dev) {
+ int i, fd;
+ struct termios tios;
+
+ fd = open(dev,O_RDWR | O_NDELAY );
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s\n", dev, sys_errlist[errno]);
+ exit(1);
+ }
+ if (tcgetattr(fd, &tios) < 0) {
+ fprintf(stderr,"Could not get terminal attributes: %s",sys_errlist[errno]);
+ exit(1);
+ }
+
+ tios.c_cflag =
+ CS7 |
+ CREAD | // Enable Receiver
+ HUPCL | // Hangup after close
+ CLOCAL | // Ignore modem control lines
+ PARENB; // Enable parity (even by default)
+
+
+
+ tios.c_iflag = IGNBRK; // Ignore break
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ for(i = 0; i < NCCS; i++) {
+ tios.c_cc[i] = '\0'; // no special characters
+ }
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ cfsetospeed (&tios, B9600);
+ cfsetispeed (&tios, B9600);
+
+ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+ fprintf(stderr,"Could not set attributes: ,%s",sys_errlist[errno]);
+ exit(1);
+ }
+ return fd;
+}
+
+
+
+
+
+//A portion of this code was taken from the AX.25 HDLC packet driver
+//in LINUX. Once I test and have a better understanding of what
+//it is doing, it will be better commented.
+
+//For now one can speculate that the CRC routine always expects the
+//CRC to calculate out to 0xf0b8 (the hardcoded value at the end)
+//and returns TRUE if it is and FALSE if it doesn't.
+//Why don't people document better!!!!
+int check_crc_ccitt(char *filename)
+{
+ FILE *fp;
+ FILE *logfp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+ char done;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename);
+ fclose(logfp);
+ return FALSE;
+ }
+
+
+ /*the first int contains an int that is the length of the file in long.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "verify checksum:Error reading from file: %s\n", filename);
+ fclose(fp);
+ fclose(logfp);
+ return FALSE;
+ }
+
+ /* printf("Checking %i bytes for CRC in \"%s\".\n", len, filename); */
+
+ /* Make sure that we did not read 0 as the number of bytes in file. This
+ check prevents a corrupt file with zero's in it from passing the
+ CRC test. A good file will always have some data in it. */
+ if(len == 0)
+ {
+
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "verify checksum: first int claims there are 0 data in file. Error!: %s\n", filename);
+ fclose(fp);
+ fclose(logfp);
+ return FALSE;
+ }
+
+
+ rewind(fp);
+ len+=2; /*the file has two extra bytes at the end, it's checksum. Those
+ two MUST also be included in the checksum calculation.
+ */
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ done = FALSE;
+ while(!done){
+ if(fread(&dataByte, sizeof(char), 1, fp) != 1){
+ retry--;
+ }else{
+ done = TRUE;
+ }
+ if(retry == 0){
+ done = TRUE;
+ }
+ }
+ if(!retry){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ fprintf(logfp, "Unexpected end of file: %s\n", filename);
+ fprintf(logfp, "...bytes left to be read %i.\n",len);
+ fclose(logfp);
+ fclose(fp);
+ return FALSE;
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ fclose(fp);
+ if( (crc & 0xffff) != 0xf0b8){
+ /*printf("The CRC of read file:%x\n", crc); */
+ return FALSE;
+ }
+ return TRUE;
+}/*end check_crc_ccitt() */
+
+
+
+/*
+ Sends "OK to power me down" message to the remote
+ power cycling box, via the serial port.
+ Also updates the num power cycle count in a local
+ file.
+ This file "./cycleCnt" must be present. This is
+ initially (and once) created by the separate "makefiles.c"
+ program.
+*/
+void send_pwrdn_ok(void){
+
+ int fd;
+ FILE *cyclefp;
+ int cycle_fd;
+
+ cyclefp = fopen("cycleCnt","rb");
+ if(!cyclefp){
+ printf("expecting file \"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ if(fread(&CycleCount, sizeof(CycleCount),1,cyclefp) != 1){
+ fprintf(stderr, "Error! Unexpected end of file cycleCnt.\n");
+ exit(1);
+ }
+ fclose(cyclefp);
+
+ CycleCount++;
+
+ /*now write this puppy back*/
+ cyclefp = fopen("cycleCnt","wb");
+ cycle_fd = fileno(cyclefp);
+ if(!cyclefp){
+ fprintf(stderr, "Error! cannot open file for write:\"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ if(fwrite(&CycleCount, sizeof(CycleCount), 1,cyclefp) !=1){
+ fprintf(stderr, "Error writing to file cycleCnt. Cannot continue.\n");
+ exit(1);
+ }
+ if(fdatasync(cycle_fd)){
+ fprintf(stderr, "Error! cannot sync file buffer with disk.\n");
+ exit(1);
+ }
+
+ fclose(cyclefp);
+ (void)sync();
+
+ printf("\n\n Sending Power down command to the remote box.\n");
+ fd = setupSerial(SerialDevice);
+
+ if(do_pwr_dn(fd, CycleCount) < 0)
+ {
+ fprintf(stderr, "Error sending power down command.\n");
+ exit(1);
+ }
+
+ close(fd);
+}//end send_pwrnd_ok()
+
+
+
+
+/*
+ Appends 16bit CRC at the end of numBytes long buffer.
+ Make sure buf, extends at least 2 bytes beyond.
+ */
+void appendChecksum(char *buf, int numBytes){
+
+ unsigned short crc = 0xffff;
+ int index = 0;
+
+ /* printf("Added CRC (2 bytes) to %i bytes.\n", numBytes); */
+
+ for (; numBytes > 0; numBytes--){
+
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ buf[index++]) & 0xff];
+ }
+ crc ^= 0xffff;
+ /*printf("The CRC: %x\n\n", crc);*/
+
+ buf[index++] = crc;
+ buf[index++] = crc >> 8;
+
+
+
+}/*end checksum()*/
+
+
+
+
+
+/*
+ This guy make a new "random data file" with the filename
+ passed to it. This file is checksummed with the checksum
+ stored at the end. The first "int" in the file is the
+ number of int's in it (this is needed to know how much
+ data to read and checksum later).
+*/
+void make_new_file(char *filename){
+
+
+ int dfd; /* data file descriptor */
+ int rand_data;
+ int data_size;
+ int temp_size;
+ int dataIndex = 0;
+ int err;
+
+
+ struct {
+ int sizeInBytes; /* must be int */
+ int dataInt[MAX_INTS_ALLOW+1]; /* how many int's can we write? */
+ }__attribute((packed)) dataBuf;
+
+
+ fprintf(stderr, "Creating File:%s. ", filename);
+
+ if((dfd = open(filename, O_RDWR | O_CREAT | O_SYNC)) <= 0)
+ {
+ printf("Error! Cannot open file: %s\n",filename);
+ perror("Error");
+ exit(1);
+ }
+
+ /*now write a bunch of random binary data to the file*/
+ /*first figure out how much data to write. That is random also.*/
+
+ /*file should not be less than 5 ints long. (so that we have decent length files,
+ that's all)*/
+ while(
+ ((data_size = (int)(1+(int)((FileSizeMax/sizeof(int))*rand()/(RAND_MAX+1.0)))) < 5)
+ );
+
+ /* printf("Writing %i ints to the file.\n", data_size); */
+
+ temp_size = data_size * sizeof(int);
+
+ /* Make sure that all data is written in one go! This is important to
+ check for reliability of file systems like JFFS/JFFS that purport to
+ have "reliable" writes during powre fail.
+ */
+
+ dataBuf.sizeInBytes = temp_size;
+
+ data_size--; /*one alrady written*/
+ dataIndex = 0;
+
+ while(data_size--){
+ rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0)));
+
+ dataBuf.dataInt[dataIndex++] = rand_data;
+
+ }
+
+ /*now calculate the file checksum and append it to the end*/
+ appendChecksum((char *)&dataBuf, dataBuf.sizeInBytes);
+
+ /* Don't forget to increase the size of data written by the 2 chars of CRC at end.
+ These 2 bytes are NOT included in the sizeInBytes field. */
+ if((err = write(dfd, (void *)&dataBuf, dataBuf.sizeInBytes + sizeof(short))) <
+ (dataBuf.sizeInBytes + sizeof(short))
+ )
+ {
+ printf("Error writing data buffer to file. Written %i bytes rather than %i bytes.",
+ err, dataBuf.sizeInBytes);
+ perror("Error");
+ exit(1);
+ }
+
+ /* Now that the data is (hopefully) safely written. I can truncate the file to the new
+ length so that I can reclaim any unused space, if the older file was larger.
+ */
+ if(ftruncate(dfd, dataBuf.sizeInBytes + sizeof(short)) < 0)
+ {
+ perror("Error: Unable to truncate file.");
+ exit(1);
+ }
+
+
+ close(dfd);
+
+
+}//end make_new_file()
+
+
+
+/*
+ Show's help on stdout
+ */
+void printHelp(char **argv)
+{
+ printf("Usage:%s <options, defined below>\n", argv[0]);
+ printf("%s </dev/ttyS0,1,2...>: Set com port to send ok to pwr dn msg on\n",
+ CMDLINE_PORT);
+ printf("%s <n>: Set Max size in bytes of each file to be created.\n",
+ CMDLINE_MAXFILEBYTES);
+ printf("%s <n>: Set Max errors allowed when checking all files for CRC on start.\n",
+ CMDLINE_MAXERROR);
+ printf("%s or %s: This Help screen.\n", CMDLINE_HELPSHORT,
+ CMDLINE_HELPLONG);
+
+}/* end printHelp()*/
+
+
+
+void processCmdLine(int argc, char **argv)
+{
+
+ int cnt;
+
+ /* skip past name of this program, process rest */
+ for(cnt = 1; cnt < argc; cnt++)
+ {
+ if(strcmp(argv[cnt], CMDLINE_PORT) == 0)
+ {
+ strncpy(SerialDevice, argv[++cnt], sizeof(SerialDevice));
+ continue;
+ }else
+ if(strcmp(argv[cnt], CMDLINE_MAXFILEBYTES) == 0)
+ {
+ FileSizeMax = (float)atoi(argv[++cnt]);
+ if(FileSizeMax > (MAX_INTS_ALLOW*sizeof(int)))
+ {
+ printf("Max file size allowd is %i.\n",
+ MAX_INTS_ALLOW*sizeof(int));
+ exit(0);
+ }
+
+ continue;
+ }else
+ if(strcmp(argv[cnt], CMDLINE_HELPSHORT) == 0)
+ {
+ printHelp(argv);
+ exit(0);
+
+ }else
+ if(strcmp(argv[cnt], CMDLINE_HELPLONG) == 0)
+ {
+ printHelp(argv);
+ exit(0);
+ }else
+
+ if(strcmp(argv[cnt], CMDLINE_MAXERROR) == 0)
+ {
+ MaxErrAllowed = atoi(argv[++cnt]);
+ }
+ else
+ {
+ printf("Unknown cmd line option:%s\n", argv[cnt]);
+ printHelp(argv);
+ exit(0);
+
+ }
+ }
+
+
+}/* end processCmdLine() */
+
+
+
+
+
+int main(int argc, char **argv){
+
+ FILE *logfp;
+ int log_fd;
+ char filename[30];
+ short filenameCounter = 0;
+ unsigned short counter;
+ unsigned short numberFiles;
+ char error = FALSE;
+ short errorCnt = 0;
+ time_t timep;
+ char * time_string;
+ unsigned int seed;
+
+
+ numberFiles = MAX_NUM_FILES;
+
+ if(argc >= 1)
+ {
+ processCmdLine(argc, argv);
+ }
+
+
+ /*
+ First open MAX_NUM_FILES and make sure that the checksum is ok.
+ Also make an intry into the logfile.
+ */
+ /* timestamp! */
+ time(&timep);
+ time_string = (char *)ctime((time_t *)&timep);
+
+ /*start a new check, make a log entry and continue*/
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"%s", time_string);
+ fprintf(logfp,"Starting new check.\n");
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+
+ fclose(logfp);
+ (void)sync();
+
+ /*
+ Now check all random data files in this dir.
+ */
+ for(counter=0;counter<MAX_NUM_FILES;counter++){
+
+ fprintf(stderr, "%i.", counter);
+
+ /*create the filename in sequence. The number of files
+ to check and the algorithm to create the filename is
+ fixed and known in advance.*/
+ sprintf(filename,"file%i",filenameCounter++);
+
+ if(!check_crc_ccitt(filename)){
+ /*oops, checksum does not match. Make an entry into the log file
+ and decide if we can continue or not.*/
+ fprintf(stderr, "crcError:%s ", filename);
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"CRC error in file: %s\n", filename);
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+ fclose(logfp);
+ (void)sync();
+
+ error = TRUE;
+ errorCnt++;
+
+ if(errorCnt > MaxErrAllowed){
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"\nMax Error count exceed. Stopping!\n");
+ if(fdatasync(log_fd) == -1){
+ fprintf(stderr,"Error! Cannot sync file data with disk.\n");
+ exit(1);
+ }
+ fclose(logfp);
+ (void)sync();
+
+ fprintf(stderr, "Too many errors. See \"logfile\".\n");
+ exit(1);
+ }/* if too many errors */
+
+ /*we have decided to continue, however first repair this file
+ so that we do not cumulate errors across power cycles.*/
+ make_new_file(filename);
+ }
+ }//for
+
+ /*all files checked, make a log entry and continue*/
+ logfp = fopen("logfile","a"); /*open for appending only.*/
+ log_fd = fileno(logfp);
+ fprintf(logfp,"All files checked. Total errors found: %i\n\n", errorCnt);
+ if(fdatasync(log_fd)){
+ fprintf(stderr, "Error! cannot sync file buffer with disk.\n");
+ exit(1);
+ }
+
+ fclose(logfp);
+ (void)sync();
+
+ /*now send a message to the remote power box and have it start a random
+ pwer down timer after which power will be killed to this unit.
+ */
+ send_pwrdn_ok();
+
+ /*now go into a forever loop of writing to files and CRC'ing them on
+ a continious basis.*/
+
+ /*start from a random file #*/
+ /*seed rand based on the current time*/
+ seed = (unsigned int)time(NULL);
+ srand(seed);
+
+ filenameCounter=(int)(1+(int)((float)(MAX_NUM_FILES-1)*rand()/(RAND_MAX+1.0)));
+
+ while(1){
+
+ for(;filenameCounter<MAX_NUM_FILES;filenameCounter++){
+
+ /*create the filename in sequence*/
+ sprintf(filename,"file%i",filenameCounter);
+ make_new_file(filename);
+ }
+ filenameCounter = 0;
+ }
+
+ exit(0); /* though we will never reach here, but keeps the compiler happy*/
+}/*end main()*/
diff --git a/checkfs/comm.c b/checkfs/comm.c
new file mode 100644
index 0000000..57fc7ed
--- /dev/null
+++ b/checkfs/comm.c
@@ -0,0 +1,67 @@
+/*
+ File: comm.c
+ Desc: This file implements the actual transmission portion
+ of the "ok to power me down" message to the remote
+ power cycling black box.
+
+ It's been sepatated into a separate file so that
+ it may be replaced by any other comm mechanism desired.
+
+ (including none or non serial mode at all)
+
+ $Id: comm.c,v 1.3 2005/11/07 11:15:17 gleixner Exp $
+ $Log: comm.c,v $
+ Revision 1.3 2005/11/07 11:15:17 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.2 2001/06/21 23:07:18 dwmw2
+ Initial import to MTD CVS
+
+ Revision 1.1 2001/06/08 22:26:05 vipin
+ Split the modbus comm part of the program (that sends the ok to pwr me down
+ message) into another file "comm.c"
+
+
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+
+/*
+ This is the routine that forms and
+ sends the "ok to pwr me down" message
+ to the remote power cycling "black box".
+
+ */
+int do_pwr_dn(int fd, int cycleCnt)
+{
+
+ char buf[200];
+
+ sprintf(buf, "ok to power me down!\nCount = %i\n", cycleCnt);
+
+ if(write(fd, buf, strlen(buf)) < strlen(buf))
+ {
+ perror("write error");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/checkfs/common.h b/checkfs/common.h
new file mode 100644
index 0000000..1d33f8b
--- /dev/null
+++ b/checkfs/common.h
@@ -0,0 +1,7 @@
+/* $Id: common.h,v 1.1 2001/06/21 23:07:56 dwmw2 Exp $ */
+//this .h file is common to both the file creation utility and
+//the file checking utility.
+#define TRUE 1
+#define FALSE 0
+
+#define MAX_NUM_FILES 100
diff --git a/checkfs/makefiles.c b/checkfs/makefiles.c
new file mode 100644
index 0000000..662fe86
--- /dev/null
+++ b/checkfs/makefiles.c
@@ -0,0 +1,264 @@
+/*
+
+ * Copyright Daniel Industries.
+
+ * Created by: Vipin Malik (vipin.malik@daniel.com)
+ *
+ * This is GPL code. See the file COPYING for more details
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+
+ * $Id: makefiles.c,v 1.2 2005/11/07 11:15:17 gleixner Exp $
+
+This program creates MAX_NUM_FILES files (file00001, file00002 etc) and
+fills them with random numbers till they are a random length. Then it checksums
+the files (with the checksum as the last two bytes) and closes the file.
+
+The fist int is the size of the file in bytes.
+
+It then opens another file and the process continues.
+
+The files are opened in the current dir.
+
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+
+#define FILESIZE_MAX 20000.0 /* for each file in sizeof(int). Must be a float #
+ Hence, 20000.0 = 20000*4 = 80KB max file size
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+//This code was taken from the AX.25 HDLC packet driver
+//in LINUX. Once I test and have a better understanding of what
+//it is doing, it will be better commented.
+
+//For now one can speculate that the CRC routine always expects the
+//CRC to calculate out to 0xf0b8 (the hardcoded value at the end)
+//and returns TRUE if it is and FALSE if it doesn't.
+//Why don't people document better!!!!
+void check_crc_ccitt(char *filename)
+{
+ FILE *fp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ printf("Verify checksum:Error! Cannot open filename passed for verify checksum: %s\n",filename);
+ exit(1);
+ }
+ /*the first int contains an int that is the length of the file in long.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ printf("verify checksum:Error reading from file: %s", filename);
+ fclose(fp);
+ exit(1);
+ }
+ rewind(fp);
+ len+=2; /*the file has two extra bytes at the end, it's checksum. Those
+ two MUST also be included in the checksum calculation.
+ */
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--);
+ if(!retry){
+ printf("Unexpected error reading from file: %s\n", filename);
+ printf("...bytes left to be read %i.\n\n",len);
+ fclose(fp);
+ exit(1);
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ fclose(fp);
+ if( (crc & 0xffff) != 0xf0b8){
+ printf("Verify checksum: Error in file %s.\n\n",filename);
+ exit(1);
+ }
+}//end check_crc_ccitt()
+
+
+
+/*this routine opens a file 'filename' and checksumn's the entire
+ contents, and then appends the checksum at the end of the file,
+ closes the file and returns.
+*/
+void checksum(char *filename){
+
+ FILE *fp;
+ unsigned short crc = 0xffff;
+ int len;
+ char dataByte;
+ int retry;
+
+ fp = fopen(filename,"rb");
+ if(!fp){
+ printf("Error! Cannot open filename passed for checksum: %s\n",filename);
+ exit(1);
+ }
+ /*the first int contains an int that is the length of the file in longs.*/
+ if(fread(&len, sizeof(int), 1, fp) != 1){
+ printf("Error reading from file: %s", filename);
+ fclose(fp);
+ exit(1);
+ }
+ printf("Calculating checksum on %i bytes.\n",len);
+ rewind(fp); /*the # of bytes int is also included in the checksum.*/
+
+ for (;len>0;len--){
+ retry=5; /*retry 5 times*/
+ while(!fread(&dataByte, sizeof(char), 1, fp) && retry--);
+ if(!retry){
+ printf("Unexpected error reading from file: %s\n", filename);
+ printf("...bytes left to be read %i.\n\n",len);
+ fclose(fp);
+ exit(1);
+ }
+ crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ dataByte) & 0xff];
+ }
+ crc ^= 0xffff;
+ printf("The CRC: %x\n\n", crc);
+
+ /*the CRC has been calculated. now close the file and open it in append mode*/
+ fclose(fp);
+
+ fp = fopen(filename,"ab"); /*open in append mode. CRC goes at the end.*/
+ if(!fp){
+ printf("Error! Cannot open filename to update checksum: %s\n",filename);
+ exit(1);
+ }
+ if(fwrite(&crc, sizeof(crc), 1, fp) != 1){
+ printf("error! unable to update the file for checksum.\n");
+ fclose(fp);
+ exit(1);
+ }
+ fflush(fp);
+ fclose(fp);
+
+
+}/*end checksum()*/
+
+
+
+int main(void){
+
+ FILE *fp, *cyclefp;
+ int cycleCount;
+ int rand_data;
+ int data_size;
+ int temp_size;
+ char filename[30];
+ short filenameCounter = 0;
+ unsigned short counter;
+ unsigned short numberFiles;
+
+ numberFiles = MAX_NUM_FILES;
+
+ for(counter=0;counter<numberFiles;counter++){
+ /*create the filename in sequence*/
+ sprintf(filename,"file%i",filenameCounter++);
+ fp = fopen(filename,"wb");
+ if(!fp){
+ printf("Error! Cannot open file: %s\n",filename);
+ exit(1);
+ }
+ /*now write a bunch of random binary data to the file*/
+ /*first figure out how much data to write. That is random also.*/
+
+ while(
+ ((data_size = (int)(1 + (int)(FILESIZE_MAX*rand()/(RAND_MAX+1.0)))) < 100)
+ )/*file should not be less than 100 ints long. (so that we have decent length files, that's all)*/
+
+ printf("Writing %i ints to the file.\n", data_size);
+
+ temp_size = data_size * sizeof(int);
+
+ if(!fwrite(&temp_size, sizeof(int), 1, fp)){
+ printf("File write error!!.\n");
+ fclose(fp);
+ exit(1);
+ }
+ data_size--; /*one alrady written*/
+
+ while(data_size--){
+ rand_data = (int)(1 + (int)(10000.0*rand()/(RAND_MAX+1.0)));
+ if(!fwrite(&rand_data, sizeof(int), 1, fp)){
+ printf("File write error!!.\n");
+ fclose(fp);
+ exit(1);
+ }
+ }
+ fflush(fp);
+ fclose(fp);
+ /*now calculate the file checksum and append it to the end*/
+ checksum(filename);
+ /*this is just a test. Check the CRC to amek sure that it is OK.*/
+ check_crc_ccitt(filename);
+ }
+
+ /*now make a file called "cycleCnt" and put a binary (int)0 in it.
+ This file keeps count as to how many cycles have taken place!*/
+ cyclefp = fopen("cycleCnt","wb");
+ if(!cyclefp){
+ printf("cannot open file \"cycleCnt\". Cannot continue.\n");
+ exit(1);
+ }
+ cycleCount = 0;
+ if(fwrite(&cycleCount, sizeof(cycleCount), 1,cyclefp) !=1){
+ printf("Error writing to file cycleCnt. Cannot continue.\n");
+ exit(1);
+ }
+ fclose(cyclefp);
+ exit(0);
+
+}/*end main()*/
+
+
+
+
+
+
+
diff --git a/compr.c b/compr.c
new file mode 100644
index 0000000..4f7087a
--- /dev/null
+++ b/compr.c
@@ -0,0 +1,489 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory
+ * in the jffs2 directory.
+ *
+ * $Id: compr.c,v 1.9 2005/11/07 11:15:09 gleixner Exp $
+ *
+ */
+
+#include "compr.h"
+#include <string.h>
+#include <stdlib.h>
+#include <linux/jffs2.h>
+
+extern int page_size;
+
+/* LIST IMPLEMENTATION (from linux/list.h) */
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = (void *) 0;
+ entry->prev = (void *) 0;
+}
+
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+
+void jffs2_set_compression_mode(int mode)
+{
+ jffs2_compression_mode = mode;
+}
+
+int jffs2_get_compression_mode(void)
+{
+ return jffs2_compression_mode;
+}
+
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+
+/* Compression test stuffs */
+
+static int jffs2_compression_check = 0;
+
+static unsigned char *jffs2_compression_check_buf = NULL;
+
+void jffs2_compression_check_set(int yesno)
+{
+ jffs2_compression_check = yesno;
+}
+
+int jffs2_compression_check_get(void)
+{
+ return jffs2_compression_check;
+}
+
+static int jffs2_error_cnt = 0;
+
+int jffs2_compression_check_errorcnt_get(void)
+{
+ return jffs2_error_cnt;
+}
+
+#define JFFS2_BUFFER_FILL 0x55
+
+/* Called before compression (if compression_check is setted) to prepare
+ the buffer for buffer overflow test */
+static void jffs2_decompression_test_prepare(unsigned char *buf, int size)
+{
+ memset(buf,JFFS2_BUFFER_FILL,size+1);
+}
+
+/* Called after compression (if compression_check is setted) to test the result */
+static void jffs2_decompression_test(struct jffs2_compressor *compr,
+ unsigned char *data_in, unsigned char *output_buf,
+ uint32_t cdatalen, uint32_t datalen, uint32_t buf_size)
+{
+ uint32_t i;
+
+ /* buffer overflow test */
+ for (i=buf_size;i>cdatalen;i--) {
+ if (output_buf[i]!=JFFS2_BUFFER_FILL) {
+ fprintf(stderr,"COMPR_ERROR: buffer overflow at %s. "
+ "(bs=%d csize=%d b[%d]=%d)\n", compr->name,
+ buf_size, cdatalen, i, (int)(output_buf[i]));
+ jffs2_error_cnt++;
+ return;
+ }
+ }
+ /* allocing temporary buffer for decompression */
+ if (!jffs2_compression_check_buf) {
+ jffs2_compression_check_buf = malloc(page_size);
+ if (!jffs2_compression_check_buf) {
+ fprintf(stderr,"No memory for buffer allocation. Compression check disabled.\n");
+ jffs2_compression_check = 0;
+ return;
+ }
+ }
+ /* decompressing */
+ if (!compr->decompress) {
+ fprintf(stderr,"JFFS2 compression check: there is no decompress function at %s.\n", compr->name);
+ jffs2_error_cnt++;
+ return;
+ }
+ if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen,NULL)) {
+ fprintf(stderr,"JFFS2 compression check: decompression failed at %s.\n", compr->name);
+ jffs2_error_cnt++;
+ }
+ /* validate decompression */
+ else {
+ for (i=0;i<datalen;i++) {
+ if (data_in[i]!=jffs2_compression_check_buf[i]) {
+ fprintf(stderr,"JFFS2 compression check: data mismatch at %s (pos %d).\n", compr->name, i);
+ jffs2_error_cnt++;
+ break;
+ }
+ }
+ }
+}
+
+/* jffs2_compress:
+ * @data: Pointer to uncompressed data
+ * @cdata: Pointer to returned pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ * On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+ * data. On exit, expected to hold the actual size of the compressed
+ * data.
+ *
+ * Returns: Lower byte to be stored with data indicating compression type used.
+ * Zero is used to show that the data could not be compressed - the
+ * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
+ *
+ * If the cdata buffer isn't large enough to hold all the uncompressed data,
+ * jffs2_compress should compress as much as will fit, and should set
+ * *datalen accordingly to show the amount of data which were compressed.
+ */
+uint16_t jffs2_compress( unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen)
+{
+ int ret = JFFS2_COMPR_NONE;
+ int compr_ret;
+ struct jffs2_compressor *this, *best=NULL;
+ unsigned char *output_buf = NULL, *tmp_buf;
+ uint32_t orig_slen, orig_dlen;
+ uint32_t best_slen=0, best_dlen=0;
+
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ break;
+ case JFFS2_COMPR_MODE_PRIORITY:
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ output_buf = malloc(orig_dlen+jffs2_compression_check);
+ if (!output_buf) {
+ fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. Compression failed.\n");
+ goto out;
+ }
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ if ((!this->compress)||(this->disabled))
+ continue;
+
+ this->usecount++;
+
+ if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+ jffs2_decompression_test_prepare(output_buf, orig_dlen);
+
+ *datalen = orig_slen;
+ *cdatalen = orig_dlen;
+ compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+ this->usecount--;
+ if (!compr_ret) {
+ ret = this->compr;
+ this->stat_compr_blocks++;
+ this->stat_compr_orig_size += *datalen;
+ this->stat_compr_new_size += *cdatalen;
+ if (jffs2_compression_check)
+ jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen, orig_dlen);
+ break;
+ }
+ }
+ if (ret == JFFS2_COMPR_NONE) free(output_buf);
+ break;
+ case JFFS2_COMPR_MODE_SIZE:
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only backwards-compatibility and disabled modules */
+ if ((!this->compress)||(this->disabled))
+ continue;
+ /* Allocating memory for output buffer if necessary */
+ if ((this->compr_buf_size<orig_dlen+jffs2_compression_check)&&(this->compr_buf)) {
+ free(this->compr_buf);
+ this->compr_buf_size=0;
+ this->compr_buf=NULL;
+ }
+ if (!this->compr_buf) {
+ tmp_buf = malloc(orig_dlen+jffs2_compression_check);
+ if (!tmp_buf) {
+ fprintf(stderr,"mkfs.jffs2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
+ continue;
+ }
+ else {
+ this->compr_buf = tmp_buf;
+ this->compr_buf_size = orig_dlen;
+ }
+ }
+ this->usecount++;
+ if (jffs2_compression_check) /*preparing output buffer for testing buffer overflow */
+ jffs2_decompression_test_prepare(this->compr_buf,this->compr_buf_size);
+ *datalen = orig_slen;
+ *cdatalen = orig_dlen;
+ compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+ this->usecount--;
+ if (!compr_ret) {
+ if (jffs2_compression_check)
+ jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen, this->compr_buf_size);
+ if ((!best_dlen)||(best_dlen>*cdatalen)) {
+ best_dlen = *cdatalen;
+ best_slen = *datalen;
+ best = this;
+ }
+ }
+ }
+ if (best_dlen) {
+ *cdatalen = best_dlen;
+ *datalen = best_slen;
+ output_buf = best->compr_buf;
+ best->compr_buf = NULL;
+ best->compr_buf_size = 0;
+ best->stat_compr_blocks++;
+ best->stat_compr_orig_size += best_slen;
+ best->stat_compr_new_size += best_dlen;
+ ret = best->compr;
+ }
+ break;
+ default:
+ fprintf(stderr,"mkfs.jffs2: unknow compression mode.\n");
+ }
+ out:
+ if (ret == JFFS2_COMPR_NONE) {
+ *cpage_out = data_in;
+ *datalen = *cdatalen;
+ none_stat_compr_blocks++;
+ none_stat_compr_size += *datalen;
+ }
+ else {
+ *cpage_out = output_buf;
+ }
+ return ret;
+}
+
+
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+ struct jffs2_compressor *this;
+
+ if (!comp->name) {
+ fprintf(stderr,"NULL compressor name at registering JFFS2 compressor. Failed.\n");
+ return -1;
+ }
+ comp->compr_buf_size=0;
+ comp->compr_buf=NULL;
+ comp->usecount=0;
+ comp->stat_compr_orig_size=0;
+ comp->stat_compr_new_size=0;
+ comp->stat_compr_blocks=0;
+ comp->stat_decompr_blocks=0;
+
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ goto out;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+ return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+
+ if (comp->usecount) {
+ fprintf(stderr,"mkfs.jffs2: Compressor modul is in use. Unregister failed.\n");
+ return -1;
+ }
+ list_del(&comp->list);
+
+ return 0;
+}
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"disabled");
+ else
+ act_buf += sprintf(act_buf,"enabled");
+ act_buf += sprintf(act_buf,"\n");
+ }
+ return buf;
+}
+
+char *jffs2_stats(void)
+{
+ struct jffs2_compressor *this;
+ char *buf, *act_buf;
+
+ act_buf = buf = malloc(JFFS2_STAT_BUF_SIZE);
+
+ act_buf += sprintf(act_buf,"Compression mode: ");
+ switch (jffs2_compression_mode) {
+ case JFFS2_COMPR_MODE_NONE:
+ act_buf += sprintf(act_buf,"none");
+ break;
+ case JFFS2_COMPR_MODE_PRIORITY:
+ act_buf += sprintf(act_buf,"priority");
+ break;
+ case JFFS2_COMPR_MODE_SIZE:
+ act_buf += sprintf(act_buf,"size");
+ break;
+ default:
+ act_buf += sprintf(act_buf,"unkown");
+ break;
+ }
+ act_buf += sprintf(act_buf,"\nCompressors:\n");
+ act_buf += sprintf(act_buf,"%10s ","none");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d) decompr: %d blocks\n", none_stat_compr_blocks,
+ none_stat_compr_size, none_stat_decompr_blocks);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority);
+ if ((this->disabled)||(!this->compress))
+ act_buf += sprintf(act_buf,"- ");
+ else
+ act_buf += sprintf(act_buf,"+ ");
+ act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d) decompr: %d blocks ", this->stat_compr_blocks,
+ this->stat_compr_new_size, this->stat_compr_orig_size,
+ this->stat_decompr_blocks);
+ act_buf += sprintf(act_buf,"\n");
+ }
+ return buf;
+}
+
+int jffs2_set_compression_mode_name(const char *name)
+{
+ if (!strcmp("none",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+ return 0;
+ }
+ if (!strcmp("priority",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+ return 0;
+ }
+ if (!strcmp("size",name)) {
+ jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+ return 0;
+ }
+ return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+ struct jffs2_compressor *this;
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (!strcmp(this->name, name)) {
+ this->disabled = disabled;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+ return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+ return jffs2_compressor_Xable(name, 1);
+}
+
+int jffs2_set_compressor_priority(const char *name, int priority)
+{
+ struct jffs2_compressor *this,*comp;
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (!strcmp(this->name, name)) {
+ this->priority = priority;
+ comp = this;
+ goto reinsert;
+ }
+ }
+ fprintf(stderr,"mkfs.jffs2: compressor %s not found.\n",name);
+ return 1;
+reinsert:
+ /* list is sorted in the order of priority, so if
+ we change it we have to reinsert it into the
+ good place */
+ list_del(&comp->list);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ if (this->priority < comp->priority) {
+ list_add(&comp->list, this->list.prev);
+ return 0;
+ }
+ }
+ list_add_tail(&comp->list, &jffs2_compressor_list);
+ return 0;
+}
+
+
+int jffs2_compressors_init(void)
+{
+#ifdef CONFIG_JFFS2_ZLIB
+ jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+ jffs2_rtime_init();
+#endif
+ return 0;
+}
+
+int jffs2_compressors_exit(void)
+{
+#ifdef CONFIG_JFFS2_RTIME
+ jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+ jffs2_zlib_exit();
+#endif
+ return 0;
+}
diff --git a/compr.h b/compr.h
new file mode 100644
index 0000000..a30d953
--- /dev/null
+++ b/compr.h
@@ -0,0 +1,115 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: compr.h,v 1.7 2005/11/07 11:15:09 gleixner Exp $
+ *
+ */
+
+#ifndef __JFFS2_COMPR_H__
+#define __JFFS2_COMPR_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "linux/jffs2.h"
+
+#define CONFIG_JFFS2_ZLIB
+#define CONFIG_JFFS2_RTIME
+
+#define JFFS2_RUBINMIPS_PRIORITY 10
+#define JFFS2_DYNRUBIN_PRIORITY 20
+#define JFFS2_RTIME_PRIORITY 50
+#define JFFS2_ZLIB_PRIORITY 60
+
+#define JFFS2_COMPR_MODE_NONE 0
+#define JFFS2_COMPR_MODE_PRIORITY 1
+#define JFFS2_COMPR_MODE_SIZE 2
+
+#define kmalloc(a,b) malloc(a)
+#define kfree(a) free(a)
+#ifndef GFP_KERNEL
+#define GFP_KERNEL 0
+#endif
+
+#define vmalloc(a) malloc(a)
+#define vfree(a) free(a)
+
+#define printk(...) fprintf(stderr,__VA_ARGS__)
+
+#define KERN_EMERG
+#define KERN_ALERT
+#define KERN_CRIT
+#define KERN_ERR
+#define KERN_WARNING
+#define KERN_NOTICE
+#define KERN_INFO
+#define KERN_DEBUG
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+void jffs2_set_compression_mode(int mode);
+int jffs2_get_compression_mode(void);
+int jffs2_set_compression_mode_name(const char *mode_name);
+
+int jffs2_enable_compressor_name(const char *name);
+int jffs2_disable_compressor_name(const char *name);
+
+int jffs2_set_compressor_priority(const char *name, int priority);
+
+struct jffs2_compressor {
+ struct list_head list;
+ int priority; /* used by prirority comr. mode */
+ char *name;
+ char compr; /* JFFS2_COMPR_XXX */
+ int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *srclen, uint32_t *destlen, void *model);
+ int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+ uint32_t cdatalen, uint32_t datalen, void *model);
+ int usecount;
+ int disabled; /* if seted the compressor won't compress */
+ unsigned char *compr_buf; /* used by size compr. mode */
+ uint32_t compr_buf_size; /* used by size compr. mode */
+ uint32_t stat_compr_orig_size;
+ uint32_t stat_compr_new_size;
+ uint32_t stat_compr_blocks;
+ uint32_t stat_decompr_blocks;
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
+ uint32_t *datalen, uint32_t *cdatalen);
+
+/* If it is setted, a decompress will be called after every compress */
+void jffs2_compression_check_set(int yesno);
+int jffs2_compression_check_get(void);
+int jffs2_compression_check_errorcnt_get(void);
+
+char *jffs2_list_compressors(void);
+char *jffs2_stats(void);
+
+/* Compressor modules */
+
+/* These functions will be called by jffs2_compressors_init/exit */
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
diff --git a/compr_rtime.c b/compr_rtime.c
new file mode 100644
index 0000000..ab8cce3
--- /dev/null
+++ b/compr_rtime.c
@@ -0,0 +1,122 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: compr_rtime.c,v 1.4 2005/11/07 11:15:09 gleixner Exp $
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+ * occurrences" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+ * The algorithm is intended to only send "whole bytes", no bit-messing.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "compr.h"
+
+/* _compress returns the compressed size, -1 if bigger */
+static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
+{
+ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
+ int backpos, runlen=0;
+ unsigned char value;
+
+ value = data_in[pos];
+
+ cpage_out[outpos++] = data_in[pos++];
+
+ backpos = positions[value];
+ positions[value]=pos;
+
+ while ((backpos < pos) && (pos < (*sourcelen)) &&
+ (data_in[pos]==data_in[backpos++]) && (runlen<255)) {
+ pos++;
+ runlen++;
+ }
+ cpage_out[outpos++] = runlen;
+ }
+
+ if (outpos >= pos) {
+ /* We failed */
+ return -1;
+ }
+
+ /* Tell the caller how much we managed to compress, and how much space it took */
+ *sourcelen = pos;
+ *dstlen = outpos;
+ return 0;
+}
+
+
+static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
+{
+ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (outpos<destlen) {
+ unsigned char value;
+ int backoffs;
+ int repeat;
+
+ value = data_in[pos++];
+ cpage_out[outpos++] = value; /* first the verbatim copied byte */
+ repeat = data_in[pos++];
+ backoffs = positions[value];
+
+ positions[value]=outpos;
+ if (repeat) {
+ if (backoffs + repeat >= outpos) {
+ while(repeat) {
+ cpage_out[outpos++] = cpage_out[backoffs++];
+ repeat--;
+ }
+ } else {
+ memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
+ outpos+=repeat;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static struct jffs2_compressor jffs2_rtime_comp = {
+ .priority = JFFS2_RTIME_PRIORITY,
+ .name = "rtime",
+ .disabled = 0,
+ .compr = JFFS2_COMPR_RTIME,
+ .compress = &jffs2_rtime_compress,
+ .decompress = &jffs2_rtime_decompress,
+};
+
+int jffs2_rtime_init(void)
+{
+ return jffs2_register_compressor(&jffs2_rtime_comp);
+}
+
+void jffs2_rtime_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
diff --git a/compr_zlib.c b/compr_zlib.c
new file mode 100644
index 0000000..167946e
--- /dev/null
+++ b/compr_zlib.c
@@ -0,0 +1,148 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_zlib.c,v 1.4 2005/11/07 11:15:09 gleixner Exp $
+ *
+ */
+
+#include <stdint.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <asm/types.h>
+#include <linux/jffs2.h>
+#include "compr.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+
+ /* Plan: call deflate() with avail_in == *sourcelen,
+ avail_out = *dstlen - 12 and flush == Z_FINISH.
+ If it doesn't manage to finish, call it again with
+ avail_in == 0 and avail_out set to the remaining 12
+ bytes for it to clean up.
+ Q: Is 12 bytes sufficient?
+ */
+#define STREAM_END_SPACE 12
+
+int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *sourcelen, uint32_t *dstlen, void *model)
+{
+ z_stream strm;
+ int ret;
+
+ if (*dstlen <= STREAM_END_SPACE)
+ return -1;
+
+ strm.zalloc = (void *)0;
+ strm.zfree = (void *)0;
+
+ if (Z_OK != deflateInit(&strm, 3)) {
+ return -1;
+ }
+ strm.next_in = data_in;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.total_out = 0;
+
+ while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+ strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+ strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+ ret = deflate(&strm, Z_PARTIAL_FLUSH);
+ if (ret != Z_OK) {
+ deflateEnd(&strm);
+ return -1;
+ }
+ }
+ strm.avail_out += STREAM_END_SPACE;
+ strm.avail_in = 0;
+ ret = deflate(&strm, Z_FINISH);
+ if (ret != Z_STREAM_END) {
+ deflateEnd(&strm);
+ return -1;
+ }
+ deflateEnd(&strm);
+
+ if (strm.total_out >= strm.total_in)
+ return -1;
+
+
+ *dstlen = strm.total_out;
+ *sourcelen = strm.total_in;
+ return 0;
+}
+
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen, void *model)
+{
+ z_stream strm;
+ int ret;
+
+ strm.zalloc = (void *)0;
+ strm.zfree = (void *)0;
+
+ if (Z_OK != inflateInit(&strm)) {
+ return 1;
+ }
+ strm.next_in = data_in;
+ strm.avail_in = srclen;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.avail_out = destlen;
+ strm.total_out = 0;
+
+ while((ret = inflate(&strm, Z_FINISH)) == Z_OK)
+ ;
+
+ inflateEnd(&strm);
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+ .priority = JFFS2_ZLIB_PRIORITY,
+ .name = "zlib",
+ .disabled = 0,
+ .compr = JFFS2_COMPR_ZLIB,
+ .compress = &jffs2_zlib_compress,
+ .decompress = &jffs2_zlib_decompress,
+};
+
+int jffs2_zlib_init(void)
+{
+ return jffs2_register_compressor(&jffs2_zlib_comp);
+}
+
+void jffs2_zlib_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_zlib_comp);
+}
diff --git a/crc32.c b/crc32.c
new file mode 100644
index 0000000..7d1253d
--- /dev/null
+++ b/crc32.c
@@ -0,0 +1,97 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera
+ * tions for all combinations of data and CRC register values
+ *
+ * The values must be right-shifted by eight bits by the "updcrc
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions
+ * polynomial $edb88320
+ */
+
+/* $Id: crc32.c,v 1.5 2004/02/24 17:40:51 dwmw2 Exp $ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
diff --git a/crc32.h b/crc32.h
new file mode 100644
index 0000000..f3f8caf
--- /dev/null
+++ b/crc32.h
@@ -0,0 +1,21 @@
+#ifndef CRC32_H
+#define CRC32_H
+
+/* $Id: crc32.h,v 1.6 2005/11/07 11:15:09 gleixner Exp $ */
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+static inline uint32_t
+crc32(uint32_t val, const void *ss, int len)
+{
+ const unsigned char *s = ss;
+ while (--len >= 0)
+ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+ return val;
+}
+
+#endif
diff --git a/device_table.txt b/device_table.txt
new file mode 100644
index 0000000..74fdc56
--- /dev/null
+++ b/device_table.txt
@@ -0,0 +1,129 @@
+# This is a sample device table file for use with mkfs.jffs2. You can
+# do all sorts of interesting things with a device table file. For
+# example, if you want to adjust the permissions on a particular file
+# you can just add an entry like:
+# /sbin/foobar f 2755 0 0 - - - - -
+# and (assuming the file /sbin/foobar exists) it will be made setuid
+# root (regardless of what its permissions are on the host filesystem.
+#
+# Device table entries take the form of:
+# <name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+# where name is the file name, type can be one of:
+# f A regular file
+# d Directory
+# c Character special device file
+# b Block special device file
+# p Fifo (named pipe)
+# uid is the user id for the target file, gid is the group id for the
+# target file. The rest of the entried apply only to device special
+# file.
+
+# When building a target filesystem, it is desirable to not have to
+# become root and then run 'mknod' a thousand times. Using a device
+# table you can create device nodes and directories "on the fly".
+# Furthermore, you can use a single table entry to create a many device
+# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15]
+# I could just use the following two table entries:
+# /dev/hda b 640 0 0 3 0 0 0 -
+# /dev/hda b 640 0 0 3 1 1 1 15
+#
+# Have fun
+# -Erik Andersen <andersen@codepoet.org>
+#
+
+#<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+/dev d 755 0 0 - - - - -
+/dev/mem c 640 0 0 1 1 0 0 -
+/dev/kmem c 640 0 0 1 2 0 0 -
+/dev/null c 640 0 0 1 3 0 0 -
+/dev/zero c 640 0 0 1 5 0 0 -
+/dev/random c 640 0 0 1 8 0 0 -
+/dev/urandom c 640 0 0 1 9 0 0 -
+/dev/tty c 666 0 0 5 0 0 0 -
+/dev/tty c 666 0 0 4 0 0 1 6
+/dev/console c 640 0 0 5 1 0 0 -
+/dev/ram b 640 0 0 1 1 0 0 -
+/dev/ram b 640 0 0 1 0 0 1 4
+/dev/loop b 640 0 0 7 0 0 1 2
+/dev/ptmx c 666 0 0 5 2 0 0 -
+#/dev/ttyS c 640 0 0 4 64 0 1 4
+#/dev/psaux c 640 0 0 10 1 0 0 -
+#/dev/rtc c 640 0 0 10 135 0 0 -
+
+# Adjust permissions on some normal files
+#/etc/shadow f 600 0 0 - - - - -
+#/bin/tinylogin f 4755 0 0 - - - - -
+
+# User-mode Linux stuff
+/dev/ubda b 640 0 0 98 0 0 0 -
+/dev/ubda b 640 0 0 98 1 1 1 15
+
+# IDE Devices
+/dev/hda b 640 0 0 3 0 0 0 -
+/dev/hda b 640 0 0 3 1 1 1 15
+/dev/hdb b 640 0 0 3 64 0 0 -
+/dev/hdb b 640 0 0 3 65 1 1 15
+#/dev/hdc b 640 0 0 22 0 0 0 -
+#/dev/hdc b 640 0 0 22 1 1 1 15
+#/dev/hdd b 640 0 0 22 64 0 0 -
+#/dev/hdd b 640 0 0 22 65 1 1 15
+#/dev/hde b 640 0 0 33 0 0 0 -
+#/dev/hde b 640 0 0 33 1 1 1 15
+#/dev/hdf b 640 0 0 33 64 0 0 -
+#/dev/hdf b 640 0 0 33 65 1 1 15
+#/dev/hdg b 640 0 0 34 0 0 0 -
+#/dev/hdg b 640 0 0 34 1 1 1 15
+#/dev/hdh b 640 0 0 34 64 0 0 -
+#/dev/hdh b 640 0 0 34 65 1 1 15
+
+# SCSI Devices
+#/dev/sda b 640 0 0 8 0 0 0 -
+#/dev/sda b 640 0 0 8 1 1 1 15
+#/dev/sdb b 640 0 0 8 16 0 0 -
+#/dev/sdb b 640 0 0 8 17 1 1 15
+#/dev/sdc b 640 0 0 8 32 0 0 -
+#/dev/sdc b 640 0 0 8 33 1 1 15
+#/dev/sdd b 640 0 0 8 48 0 0 -
+#/dev/sdd b 640 0 0 8 49 1 1 15
+#/dev/sde b 640 0 0 8 64 0 0 -
+#/dev/sde b 640 0 0 8 65 1 1 15
+#/dev/sdf b 640 0 0 8 80 0 0 -
+#/dev/sdf b 640 0 0 8 81 1 1 15
+#/dev/sdg b 640 0 0 8 96 0 0 -
+#/dev/sdg b 640 0 0 8 97 1 1 15
+#/dev/sdh b 640 0 0 8 112 0 0 -
+#/dev/sdh b 640 0 0 8 113 1 1 15
+#/dev/sg c 640 0 0 21 0 0 1 15
+#/dev/scd b 640 0 0 11 0 0 1 15
+#/dev/st c 640 0 0 9 0 0 1 8
+#/dev/nst c 640 0 0 9 128 0 1 8
+#/dev/st c 640 0 0 9 32 1 1 4
+#/dev/st c 640 0 0 9 64 1 1 4
+#/dev/st c 640 0 0 9 96 1 1 4
+
+# Floppy disk devices
+#/dev/fd b 640 0 0 2 0 0 1 2
+#/dev/fd0d360 b 640 0 0 2 4 0 0 -
+#/dev/fd1d360 b 640 0 0 2 5 0 0 -
+#/dev/fd0h1200 b 640 0 0 2 8 0 0 -
+#/dev/fd1h1200 b 640 0 0 2 9 0 0 -
+#/dev/fd0u1440 b 640 0 0 2 28 0 0 -
+#/dev/fd1u1440 b 640 0 0 2 29 0 0 -
+#/dev/fd0u2880 b 640 0 0 2 32 0 0 -
+#/dev/fd1u2880 b 640 0 0 2 33 0 0 -
+
+# All the proprietary cdrom devices in the world
+#/dev/aztcd b 640 0 0 29 0 0 0 -
+#/dev/bpcd b 640 0 0 41 0 0 0 -
+#/dev/capi20 c 640 0 0 68 0 0 1 2
+#/dev/cdu31a b 640 0 0 15 0 0 0 -
+#/dev/cdu535 b 640 0 0 24 0 0 0 -
+#/dev/cm206cd b 640 0 0 32 0 0 0 -
+#/dev/sjcd b 640 0 0 18 0 0 0 -
+#/dev/sonycd b 640 0 0 15 0 0 0 -
+#/dev/gscd b 640 0 0 16 0 0 0 -
+#/dev/sbpcd b 640 0 0 25 0 0 0 -
+#/dev/sbpcd b 640 0 0 25 0 0 1 4
+#/dev/mcd b 640 0 0 23 0 0 0 -
+#/dev/optcd b 640 0 0 17 0 0 0 -
+
diff --git a/doc_loadbios.c b/doc_loadbios.c
new file mode 100644
index 0000000..f7ba8fa
--- /dev/null
+++ b/doc_loadbios.c
@@ -0,0 +1,149 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+/* $Id: doc_loadbios.c,v 1.9 2005/11/07 11:15:10 gleixner Exp $ */
+#include <mtd/mtd-user.h>
+
+unsigned char databuf[512];
+
+int main(int argc,char **argv)
+{
+ mtd_info_t meminfo;
+ int ifd,ofd;
+ struct stat statbuf;
+ erase_info_t erase;
+ unsigned long retlen, ofs, iplsize, ipltailsize;
+ unsigned char *iplbuf;
+ iplbuf = NULL;
+
+ if (argc < 3) {
+ fprintf(stderr,"You must specify a device,"
+ " the source firmware file and the offset\n");
+ return 1;
+ }
+
+ // Open and size the device
+ if ((ofd = open(argv[1],O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if ((ifd = open(argv[2], O_RDONLY)) < 0) {
+ perror("Open firmware file\n");
+ close(ofd);
+ return 1;
+ }
+
+ if (fstat(ifd, &statbuf) != 0) {
+ perror("Stat firmware file");
+ goto error;
+ }
+
+#if 0
+ if (statbuf.st_size > 65536) {
+ printf("Firmware too large (%ld bytes)\n",statbuf.st_size);
+ goto error;
+ }
+#endif
+
+ if (ioctl(ofd,MEMGETINFO,&meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ goto error;
+ }
+
+ iplsize = (ipltailsize = 0);
+ if (argc >= 4) {
+ /* DoC Millennium has IPL in the first 1K of flash memory */
+ /* You may want to specify the offset 1024 to store
+ the firmware next to IPL. */
+ iplsize = strtoul(argv[3], NULL, 0);
+ ipltailsize = iplsize % meminfo.erasesize;
+ }
+
+ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) {
+ perror("lseek");
+ goto error;
+ }
+
+ if (ipltailsize) {
+ iplbuf = malloc(ipltailsize);
+ if (iplbuf == NULL) {
+ fprintf(stderr, "Not enough memory for IPL tail buffer of"
+ " %lu bytes\n", (unsigned long) ipltailsize);
+ goto error;
+ }
+ printf("Reading IPL%s area of length %lu at offset %lu\n",
+ (iplsize - ipltailsize) ? " tail" : "",
+ (long unsigned) ipltailsize,
+ (long unsigned) (iplsize - ipltailsize));
+ if (read(ofd, iplbuf, ipltailsize) != ipltailsize) {
+ perror("read");
+ goto error;
+ }
+ }
+
+ erase.length = meminfo.erasesize;
+
+ for (ofs = iplsize - ipltailsize ;
+ ofs < iplsize + statbuf.st_size ;
+ ofs += meminfo.erasesize) {
+ erase.start = ofs;
+ printf("Performing Flash Erase of length %lu at offset %lu\n",
+ (long unsigned) erase.length, (long unsigned) erase.start);
+
+ if (ioctl(ofd,MEMERASE,&erase) != 0) {
+ perror("ioctl(MEMERASE)");
+ goto error;
+ }
+ }
+
+ if (lseek(ofd, iplsize - ipltailsize, SEEK_SET) < 0) {
+ perror("lseek");
+ goto error;
+ }
+
+ if (ipltailsize) {
+ printf("Writing IPL%s area of length %lu at offset %lu\n",
+ (iplsize - ipltailsize) ? " tail" : "",
+ (long unsigned) ipltailsize,
+ (long unsigned) (iplsize - ipltailsize));
+ if (write(ofd, iplbuf, ipltailsize) != ipltailsize) {
+ perror("write");
+ goto error;
+ }
+ }
+
+ printf("Writing the firmware of length %lu at %lu... ",
+ (unsigned long) statbuf.st_size,
+ (unsigned long) iplsize);
+ do {
+ retlen = read(ifd, databuf, 512);
+ if (retlen < 512)
+ memset(databuf+retlen, 0xff, 512-retlen);
+ if (write(ofd, databuf, 512) != 512) {
+ perror("write");
+ goto error;
+ }
+ } while (retlen == 512);
+ printf("Done.\n");
+
+ if (iplbuf != NULL)
+ free(iplbuf);
+ close(ifd);
+ close(ofd);
+ return 0;
+
+error:
+ if (iplbuf != NULL)
+ free(iplbuf);
+ close(ifd);
+ close(ofd);
+ return 1;
+}
diff --git a/docfdisk.c b/docfdisk.c
new file mode 100644
index 0000000..4d82bde
--- /dev/null
+++ b/docfdisk.c
@@ -0,0 +1,340 @@
+/*
+ * docfdisk.c: Modify INFTL partition tables
+ *
+ *
+ * $Id: docfdisk.c,v 1.9 2005/11/07 11:15:10 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _XOPEN_SOURCE 500 /* for pread/pwrite */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <string.h>
+
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+#include <mtd/inftl-user.h>
+
+#define swab16(x) \
+ ((__u16)( \
+ (((__u16)(x) & (__u16)0x00ffU) << 8) | \
+ (((__u16)(x) & (__u16)0xff00U) >> 8) ))
+#define swab32(x) \
+ ((__u32)( \
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(x) ({ __u16 _x = x; swab16(_x); })
+#define cpu_to_le32(x) ({ __u32 _x = x; swab32(_x); })
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#endif
+#define le32_to_cpu(x) cpu_to_le32(x)
+#define le16_to_cpu(x) cpu_to_le16(x)
+
+unsigned char *buf;
+
+mtd_info_t meminfo;
+erase_info_t erase;
+int fd;
+struct INFTLMediaHeader *mh;
+
+#define MAXSCAN 10
+
+void show_header(int mhoffs) {
+ int i, unitsize, numunits, bmbits, numpart;
+ int start, end, num, nextunit;
+ unsigned int flags;
+ struct INFTLPartition *ip;
+
+ bmbits = le32_to_cpu(mh->BlockMultiplierBits);
+ printf(" bootRecordID = %s\n"
+ " NoOfBootImageBlocks = %d\n"
+ " NoOfBinaryPartitions = %d\n"
+ " NoOfBDTLPartitions = %d\n"
+ " BlockMultiplierBits = %d\n"
+ " FormatFlags = %d\n"
+ " OsakVersion = %d.%d.%d.%d\n"
+ " PercentUsed = %d\n",
+ mh->bootRecordID, le32_to_cpu(mh->NoOfBootImageBlocks),
+ le32_to_cpu(mh->NoOfBinaryPartitions),
+ le32_to_cpu(mh->NoOfBDTLPartitions),
+ bmbits,
+ le32_to_cpu(mh->FormatFlags),
+ ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+ le32_to_cpu(mh->PercentUsed));
+
+ numpart = le32_to_cpu(mh->NoOfBinaryPartitions) +
+ le32_to_cpu(mh->NoOfBDTLPartitions);
+ unitsize = meminfo.erasesize >> bmbits;
+ numunits = meminfo.size / unitsize;
+ nextunit = mhoffs / unitsize;
+ nextunit++;
+ printf("Unitsize is %d bytes. Device has %d units.\n",
+ unitsize, numunits);
+ if (numunits > 32768) {
+ printf("WARNING: More than 32768 units! Unexpectedly small BlockMultiplierBits.\n");
+ }
+ if (bmbits && (numunits <= 16384)) {
+ printf("NOTICE: Unexpectedly large BlockMultiplierBits.\n");
+ }
+ for (i = 0; i < 4; i++) {
+ ip = &(mh->Partitions[i]);
+ flags = le32_to_cpu(ip->flags);
+ start = le32_to_cpu(ip->firstUnit);
+ end = le32_to_cpu(ip->lastUnit);
+ num = le32_to_cpu(ip->virtualUnits);
+ if (start < nextunit) {
+ printf("ERROR: Overlapping or misordered partitions!\n");
+ }
+ if (start > nextunit) {
+ printf(" Unpartitioned space: %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n",
+ (start - nextunit) * unitsize, start - nextunit,
+ nextunit, start - 1);
+ }
+ if (flags & INFTL_BINARY)
+ printf(" Partition %d (BDK):", i+1);
+ else
+ printf(" Partition %d (BDTL):", i+1);
+ printf(" %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n"
+ " flags = 0x%x\n"
+ " spareUnits = %d\n",
+ num * unitsize, num, start, end,
+ le32_to_cpu(ip->flags), le32_to_cpu(ip->spareUnits));
+ if (num > (1 + end - start)) {
+ printf("ERROR: virtualUnits not consistent with first/lastUnit!\n");
+ }
+ end++;
+ if (end > nextunit)
+ nextunit = end;
+ if (flags & INFTL_LAST)
+ break;
+ }
+ if (i >= 4) {
+ printf("Odd. Last partition was not marked with INFTL_LAST.\n");
+ i--;
+ }
+ if ((i+1) != numpart) {
+ printf("ERROR: Number of partitions != (NoOfBinaryPartitions + NoOfBDTLPartitions)\n");
+ }
+ if (nextunit > numunits) {
+ printf("ERROR: Partitions appear to extend beyond end of device!\n");
+ }
+ if (nextunit < numunits) {
+ printf(" Unpartitioned space: %d bytes\n"
+ " virtualUnits = %d\n"
+ " firstUnit = %d\n"
+ " lastUnit = %d\n",
+ (numunits - nextunit) * unitsize, numunits - nextunit,
+ nextunit, numunits - 1);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret, i, mhblock, unitsize, block;
+ unsigned int nblocks[4], npart;
+ unsigned int totblocks;
+ struct INFTLPartition *ip;
+ unsigned char *oobbuf;
+ struct mtd_oob_buf oob;
+ char line[20];
+ int mhoffs;
+ struct INFTLMediaHeader *mh2;
+
+ if (argc < 2) {
+ printf(
+"Usage: %s <mtddevice> [<size1> [<size2> [<size3> [<size4]]]]\n"
+" Sizes are in device units (run with no sizes to show unitsize and current\n"
+" partitions). Last size = 0 means go to end of device.\n",
+ argv[0]);
+ return 1;
+ }
+
+ npart = argc - 2;
+ if (npart > 4) {
+ printf("Max 4 partitions allowed.\n");
+ return 1;
+ }
+
+ for (i = 0; i < npart; i++) {
+ nblocks[i] = strtoul(argv[2+i], NULL, 0);
+ if (i && !nblocks[i-1]) {
+ printf("No sizes allowed after 0\n");
+ return 1;
+ }
+ }
+
+ // Open and size the device
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ return 1;
+ }
+
+ printf("Device size is %d bytes. Erasesize is %d bytes.\n",
+ meminfo.size, meminfo.erasesize);
+
+ buf = malloc(meminfo.erasesize);
+ oobbuf = malloc((meminfo.erasesize / meminfo.oobblock) * meminfo.oobsize);
+ if (!buf || !oobbuf) {
+ printf("Can't malloc block buffer\n");
+ return 1;
+ }
+ oob.length = meminfo.oobsize;
+
+ mh = (struct INFTLMediaHeader *) buf;
+
+ for (mhblock = 0; mhblock < MAXSCAN; mhblock++) {
+ if ((ret = pread(fd, buf, meminfo.erasesize, mhblock * meminfo.erasesize)) < 0) {
+ if (errno == EBADMSG) {
+ printf("ECC error at eraseblock %d\n", mhblock);
+ continue;
+ }
+ perror("Read eraseblock");
+ return 1;
+ }
+ if (ret != meminfo.erasesize) {
+ printf("Short read!\n");
+ return 1;
+ }
+ if (!strcmp("BNAND", mh->bootRecordID)) break;
+ }
+ if (mhblock >= MAXSCAN) {
+ printf("Unable to find INFTL Media Header\n");
+ return 1;
+ }
+ printf("Found INFTL Media Header at block %d:\n", mhblock);
+ mhoffs = mhblock * meminfo.erasesize;
+
+ oob.ptr = oobbuf;
+ oob.start = mhoffs;
+ for (i = 0; i < meminfo.erasesize; i += meminfo.oobblock) {
+ if (ioctl(fd, MEMREADOOB, &oob)) {
+ perror("ioctl(MEMREADOOB)");
+ return 1;
+ }
+ oob.start += meminfo.oobblock;
+ oob.ptr += meminfo.oobsize;
+ }
+
+ show_header(mhoffs);
+
+ if (!npart)
+ return 0;
+
+ printf("\n"
+"-------------------------------------------------------------------------\n");
+
+ unitsize = meminfo.erasesize >> le32_to_cpu(mh->BlockMultiplierBits);
+ totblocks = meminfo.size / unitsize;
+ block = mhoffs / unitsize;
+ block++;
+
+ mh->NoOfBDTLPartitions = 0;
+ mh->NoOfBinaryPartitions = npart;
+
+ for (i = 0; i < npart; i++) {
+ ip = &(mh->Partitions[i]);
+ ip->firstUnit = cpu_to_le32(block);
+ if (!nblocks[i])
+ nblocks[i] = totblocks - block;
+ ip->virtualUnits = cpu_to_le32(nblocks[i]);
+ block += nblocks[i];
+ ip->lastUnit = cpu_to_le32(block-1);
+ ip->spareUnits = 0;
+ ip->flags = cpu_to_le32(INFTL_BINARY);
+ }
+ if (block > totblocks) {
+ printf("Requested partitions extend beyond end of device.\n");
+ return 1;
+ }
+ ip->flags = cpu_to_le32(INFTL_BINARY | INFTL_LAST);
+
+ /* update the spare as well */
+ mh2 = (struct INFTLMediaHeader *) (buf + 4096);
+ memcpy((void *) mh2, (void *) mh, sizeof(struct INFTLMediaHeader));
+
+ printf("\nProposed new Media Header:\n");
+ show_header(mhoffs);
+
+ printf("\nReady to update device. Type 'yes' to proceed, anything else to abort: ");
+ fgets(line, sizeof(line), stdin);
+ if (strcmp("yes\n", line))
+ return 0;
+ printf("Updating MediaHeader...\n");
+
+ erase.start = mhoffs;
+ erase.length = meminfo.erasesize;
+ if (ioctl(fd, MEMERASE, &erase)) {
+ perror("ioctl(MEMERASE)");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+
+ oob.ptr = oobbuf;
+ oob.start = mhoffs;
+ for (i = 0; i < meminfo.erasesize; i += meminfo.oobblock) {
+ memset(oob.ptr, 0xff, 6); // clear ECC.
+ if (ioctl(fd, MEMWRITEOOB, &oob)) {
+ perror("ioctl(MEMWRITEOOB)");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+ if ((ret = pwrite(fd, buf, meminfo.oobblock, oob.start)) < 0) {
+ perror("Write page");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+ if (ret != meminfo.oobblock) {
+ printf("Short write!\n");
+ printf("Your MediaHeader may be hosed. UHOH!\n");
+ return 1;
+ }
+
+ oob.start += meminfo.oobblock;
+ oob.ptr += meminfo.oobsize;
+ buf += meminfo.oobblock;
+ }
+
+ printf("Success. REBOOT or unload the diskonchip module to update partitions!\n");
+ return 0;
+}
diff --git a/flash_erase.c b/flash_erase.c
new file mode 100644
index 0000000..870cf40
--- /dev/null
+++ b/flash_erase.c
@@ -0,0 +1,188 @@
+/*
+ * flash_erase.c -- erase parts of a MTD device
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <mtd/mtd-user.h>
+
+int region_erase(int Fd, int start, int count, int unlock, int regcount)
+{
+ int i, j;
+ region_info_t * reginfo;
+
+ reginfo = calloc(regcount, sizeof(region_info_t));
+
+ for(i = 0; i < regcount; i++)
+ {
+ reginfo[i].regionindex = i;
+ if(ioctl(Fd,MEMGETREGIONINFO,&(reginfo[i])) != 0)
+ return 8;
+ else
+ printf("Region %d is at %d of %d sector and with sector "
+ "size %x\n", i, reginfo[i].offset, reginfo[i].numblocks,
+ reginfo[i].erasesize);
+ }
+
+ // We have all the information about the chip we need.
+
+ for(i = 0; i < regcount; i++)
+ { //Loop through the regions
+ region_info_t * r = &(reginfo[i]);
+
+ if((start >= reginfo[i].offset) &&
+ (start < (r->offset + r->numblocks*r->erasesize)))
+ break;
+ }
+
+ if(i >= regcount)
+ {
+ printf("Starting offset %x not within chip.\n", start);
+ return 8;
+ }
+
+ //We are now positioned within region i of the chip, so start erasing
+ //count sectors from there.
+
+ for(j = 0; (j < count)&&(i < regcount); j++)
+ {
+ erase_info_t erase;
+ region_info_t * r = &(reginfo[i]);
+
+ erase.start = start;
+ erase.length = r->erasesize;
+
+ if(unlock != 0)
+ { //Unlock the sector first.
+ if(ioctl(Fd, MEMUNLOCK, &erase) != 0)
+ {
+ perror("\nMTD Unlock failure");
+ close(Fd);
+ return 8;
+ }
+ }
+ printf("\rPerforming Flash Erase of length %u at offset 0x%x",
+ erase.length, erase.start);
+ fflush(stdout);
+ if(ioctl(Fd, MEMERASE, &erase) != 0)
+ {
+ perror("\nMTD Erase failure");
+ close(Fd);
+ return 8;
+ }
+
+
+ start += erase.length;
+ if(start >= (r->offset + r->numblocks*r->erasesize))
+ { //We finished region i so move to region i+1
+ printf("\nMoving to region %d\n", i+1);
+ i++;
+ }
+ }
+
+ printf(" done\n");
+
+ return 0;
+}
+
+int non_region_erase(int Fd, int start, int count, int unlock)
+{
+ mtd_info_t meminfo;
+
+ if (ioctl(Fd,MEMGETINFO,&meminfo) == 0)
+ {
+ erase_info_t erase;
+
+ erase.start = start;
+
+ erase.length = meminfo.erasesize;
+
+ for (; count > 0; count--) {
+ printf("\rPerforming Flash Erase of length %u at offset 0x%x",
+ erase.length, erase.start);
+ fflush(stdout);
+
+ if(unlock != 0)
+ {
+ //Unlock the sector first.
+ printf("\rPerforming Flash unlock at offset 0x%x",erase.start);
+ if(ioctl(Fd, MEMUNLOCK, &erase) != 0)
+ {
+ perror("\nMTD Unlock failure");
+ close(Fd);
+ return 8;
+ }
+ }
+
+ if (ioctl(Fd,MEMERASE,&erase) != 0)
+ {
+ perror("\nMTD Erase failure");
+ close(Fd);
+ return 8;
+ }
+ erase.start += meminfo.erasesize;
+ }
+ printf(" done\n");
+ }
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int regcount;
+ int Fd;
+ int start;
+ int count;
+ int unlock;
+ int res = 0;
+
+ if (1 >= argc)
+ {
+ fprintf(stderr,"You must specify a device\n");
+ return 16;
+ }
+
+ if (argc > 2)
+ start = strtol(argv[2], NULL, 0);
+ else
+ start = 0;
+
+ if (argc > 3)
+ count = strtol(argv[3], NULL, 0);
+ else
+ count = 1;
+
+ if(argc > 4)
+ unlock = strtol(argv[4], NULL, 0);
+ else
+ unlock = 0;
+
+
+ // Open and size the device
+ if ((Fd = open(argv[1],O_RDWR)) < 0)
+ {
+ fprintf(stderr,"File open error\n");
+ return 8;
+ }
+
+ printf("Erase Total %d Units\n", count);
+
+ if (ioctl(Fd,MEMGETREGIONCOUNT,&regcount) == 0)
+ {
+ if(regcount == 0)
+ {
+ res = non_region_erase(Fd, start, count, unlock);
+ }
+ else
+ {
+ res = region_erase(Fd, start, count, unlock, regcount);
+ }
+ }
+
+ return res;
+}
diff --git a/flash_eraseall.c b/flash_eraseall.c
new file mode 100644
index 0000000..d88246b
--- /dev/null
+++ b/flash_eraseall.c
@@ -0,0 +1,308 @@
+/* eraseall.c -- erase the whole of a MTD device
+
+ Copyright (C) 2000 Arcom Control System Ltd
+
+ Renamed to flash_eraseall.c
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+ $Id: flash_eraseall.c,v 1.24 2005/11/07 11:15:10 gleixner Exp $
+*/
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include "crc32.h"
+
+#include <mtd/mtd-user.h>
+#include <mtd/jffs2-user.h>
+
+#define PROGRAM "flash_eraseall"
+#define VERSION "$Revision: 1.24 $"
+
+static const char *exe_name;
+static const char *mtd_device;
+static int quiet; /* true -- don't output progress */
+static int jffs2; // format for jffs2 usage
+
+static void process_options (int argc, char *argv[]);
+static void display_help (void);
+static void display_version (void);
+static struct jffs2_raw_ebh ebh;
+int target_endian = __BYTE_ORDER;
+
+int main (int argc, char *argv[])
+{
+ mtd_info_t meminfo;
+ int fd, ebhpos = 0, ebhlen = 0;
+ erase_info_t erase;
+ int isNAND, bbtest = 1;
+ uint32_t pages_per_eraseblock, available_oob_space;
+
+ process_options(argc, argv);
+
+
+ if ((fd = open(mtd_device, O_RDWR)) < 0) {
+ fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno));
+ exit(1);
+ }
+
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ fprintf(stderr, "%s: %s: unable to get MTD device info\n", exe_name, mtd_device);
+ exit(1);
+ }
+
+ erase.length = meminfo.erasesize;
+ isNAND = meminfo.type == MTD_NANDFLASH ? 1 : 0;
+
+ if (jffs2) {
+ ebh.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+ ebh.nodetype = cpu_to_je16 (JFFS2_NODETYPE_ERASEBLOCK_HEADER);
+ ebh.totlen = cpu_to_je32(sizeof(struct jffs2_raw_ebh));
+ ebh.hdr_crc = cpu_to_je32 (crc32 (0, &ebh, sizeof (struct jffs2_unknown_node) - 4));
+ ebh.reserved = 0;
+ ebh.compat_fset = JFFS2_EBH_COMPAT_FSET;
+ ebh.incompat_fset = JFFS2_EBH_INCOMPAT_FSET;
+ ebh.rocompat_fset = JFFS2_EBH_ROCOMPAT_FSET;
+ ebh.erase_count = cpu_to_je32(0);
+ ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4,
+ sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4));
+
+ if (isNAND) {
+ struct nand_oobinfo oobinfo;
+
+ if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) {
+ fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device);
+ exit(1);
+ }
+
+ /* Check for autoplacement */
+ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+ /* Get the position of the free bytes */
+ if (!oobinfo.oobfree[0][1]) {
+ fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n");
+ exit(1);
+ }
+ ebhpos = oobinfo.oobfree[0][0];
+ ebhlen = oobinfo.oobfree[0][1];
+ } else {
+ /* Legacy mode */
+ switch (meminfo.oobsize) {
+ case 8:
+ ebhpos = 6;
+ ebhlen = 2;
+ break;
+ case 16:
+ ebhpos = 8;
+ ebhlen = 8;
+ break;
+ case 64:
+ ebhpos = 16;
+ ebhlen = 8;
+ break;
+ }
+ }
+ pages_per_eraseblock = meminfo.erasesize/meminfo.oobblock;
+ available_oob_space = ebhlen * pages_per_eraseblock;
+ if (available_oob_space < sizeof(struct jffs2_raw_ebh)) {
+ fprintf(stderr, "The OOB area(%d) is not big enough to hold eraseblock_header(%d)", available_oob_space, sizeof(struct jffs2_raw_ebh));
+ exit(1);
+ }
+ }
+ }
+
+ for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) {
+ if (bbtest) {
+ loff_t offset = erase.start;
+ int ret = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (ret > 0) {
+ if (!quiet)
+ printf ("\nSkipping bad block at 0x%08x\n", erase.start);
+ continue;
+ } else if (ret < 0) {
+ if (errno == EOPNOTSUPP) {
+ bbtest = 0;
+ if (isNAND) {
+ fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ if (!quiet) {
+ printf
+ ("\rErasing %d Kibyte @ %x -- %2llu %% complete.",
+ meminfo.erasesize / 1024, erase.start,
+ (unsigned long long)
+ erase.start * 100 / meminfo.size);
+ }
+ fflush(stdout);
+
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+
+ /* format for JFFS2 ? */
+ if (!jffs2)
+ continue;
+
+ /* write cleanmarker */
+ if (isNAND) {
+ struct mtd_oob_buf oob;
+ uint32_t i = 0, written = 0;
+
+ while (written < sizeof(struct jffs2_raw_ebh)) {
+ oob.ptr = (unsigned char *) &ebh + written;
+ oob.start = erase.start + meminfo.oobblock*i + ebhpos;
+ oob.length = (sizeof(struct jffs2_raw_ebh) - written) < ebhlen ? (sizeof(struct jffs2_raw_ebh) - written) : ebhlen;
+ if (ioctl (fd, MEMWRITEOOB, &oob) != 0) {
+ fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno));
+ break;
+ }
+ i++;
+ written += oob.length;
+ }
+ if (written < sizeof(struct jffs2_raw_ebh)) {
+ continue;
+ }
+ } else {
+ if (lseek (fd, erase.start, SEEK_SET) < 0) {
+ fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+ if (write (fd , &ebh, sizeof (ebh)) != sizeof (ebh)) {
+ fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno));
+ continue;
+ }
+ }
+ if (!quiet)
+ printf (" Cleanmarker written at %x.", erase.start);
+ }
+ if (!quiet)
+ printf("\n");
+
+ return 0;
+}
+
+
+void process_options (int argc, char *argv[])
+{
+ int error = 0;
+
+ exe_name = argv[0];
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "jq";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"jffs2", no_argument, 0, 'j'},
+ {"quiet", no_argument, 0, 'q'},
+ {"silent", no_argument, 0, 'q'},
+
+ {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 'q':
+ quiet = 1;
+ break;
+ case 'j':
+ jffs2 = 1;
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+ if (optind == argc) {
+ fprintf(stderr, "%s: no MTD device specified\n", exe_name);
+ error = 1;
+ }
+ if (error) {
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ exe_name);
+ exit(1);
+ }
+
+ mtd_device = argv[optind];
+}
+
+
+void display_help (void)
+{
+ printf("Usage: %s [OPTION] MTD_DEVICE\n"
+ "Erases all of the specified MTD device.\n"
+ "\n"
+ " -j, --jffs2 format the device for jffs2\n"
+ " -q, --quiet don't display progress messages\n"
+ " --silent same as --quiet\n"
+ " --help display this help and exit\n"
+ " --version output version information and exit\n",
+ exe_name);
+ exit(0);
+}
+
+
+void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "Copyright (C) 2000 Arcom Control Systems Ltd\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);
+}
diff --git a/flash_info.c b/flash_info.c
new file mode 100644
index 0000000..7157b9e
--- /dev/null
+++ b/flash_info.c
@@ -0,0 +1,55 @@
+/*
+ * flash_info.c -- print info about a MTD device
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int regcount;
+ int Fd;
+
+ if (1 >= argc)
+ {
+ fprintf(stderr,"Usage: flash_info device\n");
+ return 16;
+ }
+
+ // Open and size the device
+ if ((Fd = open(argv[1],O_RDONLY)) < 0)
+ {
+ fprintf(stderr,"File open error\n");
+ return 8;
+ }
+
+ if (ioctl(Fd,MEMGETREGIONCOUNT,&regcount) == 0)
+ {
+ int i;
+ region_info_t reginfo;
+ printf("Device %s has %d erase regions\n", argv[1], regcount);
+ for (i = 0; i < regcount; i++)
+ {
+ reginfo.regionindex = i;
+ if(ioctl(Fd, MEMGETREGIONINFO, &reginfo) == 0)
+ {
+ printf("Region %d is at 0x%x with size 0x%x and "
+ "has 0x%x blocks\n", i, reginfo.offset,
+ reginfo.erasesize, reginfo.numblocks);
+ }
+ else
+ {
+ printf("Strange can not read region %d from a %d region device\n",
+ i, regcount);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/flash_lock.c b/flash_lock.c
new file mode 100644
index 0000000..1238a3d
--- /dev/null
+++ b/flash_lock.c
@@ -0,0 +1,84 @@
+/*
+ * FILE flash_lock.c
+ *
+ * This utility locks one or more sectors of flash device.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct mtd_info_user mtdInfo;
+ struct erase_info_user mtdLockInfo;
+ int num_sectors;
+ int ofs;
+
+ /*
+ * Parse command line options
+ */
+ if(argc != 4)
+ {
+ fprintf(stderr, "USAGE: %s <mtd device> <ofs in hex> <num of sectors in decimal or -1 for all sectors>\n", argv[0]);
+ exit(1);
+ }
+ else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
+ {
+ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0)
+ {
+ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
+ exit(1);
+ }
+
+ if(ioctl(fd, MEMGETINFO, &mtdInfo))
+ {
+ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+ sscanf(argv[2], "%x",&ofs);
+ sscanf(argv[3], "%d",&num_sectors);
+ if(ofs > mtdInfo.size - mtdInfo.erasesize)
+ {
+ fprintf(stderr, "%x is beyond device size %x\n",ofs,(unsigned int)(mtdInfo.size - mtdInfo.erasesize));
+ exit(1);
+ }
+
+ if (num_sectors == -1) {
+ num_sectors = mtdInfo.size/mtdInfo.erasesize;
+ }
+ else {
+ if(num_sectors > mtdInfo.size/mtdInfo.erasesize)
+ {
+ fprintf(stderr, "%d are too many sectors, device only has %d\n",num_sectors,(int)(mtdInfo.size/mtdInfo.erasesize));
+ exit(1);
+ }
+ }
+
+ mtdLockInfo.start = ofs;
+ mtdLockInfo.length = num_sectors * mtdInfo.erasesize;
+ if(ioctl(fd, MEMLOCK, &mtdLockInfo))
+ {
+ fprintf(stderr, "Could not lock MTD device: %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ return 0;
+}
+
diff --git a/flash_otp_dump.c b/flash_otp_dump.c
new file mode 100644
index 0000000..b930ca4
--- /dev/null
+++ b/flash_otp_dump.c
@@ -0,0 +1,54 @@
+/*
+ * flash_otp_dump.c -- display One-Time-Programm data
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, offset, ret;
+ unsigned char buf[16];
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", argv[0]);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ printf("OTP %s data for %s\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2]);
+ offset = 0;
+ while ((ret = read(fd, buf, sizeof(buf)))) {
+ if (ret < 0) {
+ perror("read()");
+ return errno;
+ }
+ printf("0x%04x:", offset);
+ for (i = 0; i < ret; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+ offset += ret;
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/flash_otp_info.c b/flash_otp_info.c
new file mode 100644
index 0000000..3292da7
--- /dev/null
+++ b/flash_otp_info.c
@@ -0,0 +1,63 @@
+/*
+ * flash_otp_info.c -- print info about One-Time-Programm data
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, i, ret;
+
+ if (argc != 3 || (strcmp(argv[1], "-f") && strcmp(argv[1], "-u"))) {
+ fprintf(stderr,"Usage: %s [ -f | -u ] <device>\n", argv[0]);
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = argv[1][1] == 'f' ? MTD_OTP_FACTORY : MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ ret = ioctl(fd, OTPGETREGIONCOUNT, &val);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ printf("Number of OTP %s blocks on %s: %d\n",
+ argv[1][1] == 'f' ? "factory" : "user", argv[2], val);
+
+ if (val > 0) {
+ struct otp_info info[val];
+
+ ret = ioctl(fd, OTPGETREGIONINFO, &info);
+ if (ret < 0) {
+ perror("OTPGETREGIONCOUNT");
+ return errno;
+ }
+
+ for (i = 0; i < val; i++)
+ printf("block %2d: offset = 0x%04x "
+ "size = %2d bytes %s\n",
+ i, info[i].start, info[i].length,
+ info[i].locked ? "[locked]" : "[unlocked]");
+ }
+
+ close(fd);
+ return 0;
+}
diff --git a/flash_otp_lock.c b/flash_otp_lock.c
new file mode 100644
index 0000000..bd56df3
--- /dev/null
+++ b/flash_otp_lock.c
@@ -0,0 +1,70 @@
+/*
+ * flash_otp_lock.c -- lock area of One-Time-Program data
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, offset, size;
+ char *p, buf[8];
+
+ if (argc != 5 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset> <size>\n", argv[0]);
+ fprintf(stderr, "offset and size must match on OTP region boundaries\n");
+ fprintf(stderr, "CAUTION! ONCE LOCKED, OTP REGIONS CAN'T BE UNLOCKED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ offset = strtoul(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", argv[0]);
+ return ERANGE;
+ }
+
+ size = strtoul(argv[4], &p, 0);
+ if (argv[4][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad size value\n", argv[0]);
+ return ERANGE;
+ }
+
+ printf("About to lock OTP user data on %s from 0x%x to 0x%x\n",
+ argv[2], offset, offset + size);
+ printf("Are you sure (yes|no)? ");
+ if (fgets(buf, sizeof(buf), stdin) && strcmp(buf, "yes\n") == 0) {
+ struct otp_info info;
+ info.start = offset;
+ info.length = size;
+ ret = ioctl(fd, OTPLOCK, &info);
+ if (ret < 0) {
+ perror("OTPLOCK");
+ return errno;
+ }
+ printf("Done.\n");
+ } else {
+ printf("Aborted\n");
+ }
+
+ return 0;
+}
diff --git a/flash_otp_write.c b/flash_otp_write.c
new file mode 100644
index 0000000..71d3168
--- /dev/null
+++ b/flash_otp_write.c
@@ -0,0 +1,96 @@
+/*
+ * flash_otp_write.c -- write One-Time-Program data
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc,char *argv[])
+{
+ int fd, val, ret, size, wrote, len;
+ mtd_info_t mtdInfo;
+ off_t offset;
+ char *p, buf[2048];
+
+ if (argc != 4 || strcmp(argv[1], "-u")) {
+ fprintf(stderr, "Usage: %s -u <device> <offset>\n", argv[0]);
+ fprintf(stderr, "the raw data to write should be provided on stdin\n");
+ fprintf(stderr, "CAUTION! ONCE SET TO 0, OTP DATA BITS CAN'T BE ERASED!\n");
+ return EINVAL;
+ }
+
+ fd = open(argv[2], O_WRONLY);
+ if (fd < 0) {
+ perror(argv[2]);
+ return errno;
+ }
+
+ val = MTD_OTP_USER;
+ ret = ioctl(fd, OTPSELECT, &val);
+ if (ret < 0) {
+ perror("OTPSELECT");
+ return errno;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &mtdInfo)) {
+ perror("MEMGETINFO");
+ return errno;
+ }
+
+ offset = strtoul(argv[3], &p, 0);
+ if (argv[3][0] == 0 || *p != 0) {
+ fprintf(stderr, "%s: bad offset value\n", argv[0]);
+ return ERANGE;
+ }
+
+ if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+ perror("lseek()");
+ return errno;
+ }
+
+ printf("Writing OTP user data on %s at offset 0x%lx\n", argv[2], offset);
+
+ if (mtdInfo.type == MTD_NANDFLASH)
+ len = mtdInfo.oobblock;
+ else
+ len = 256;
+
+ wrote = 0;
+ while ((size = read(0, buf, len))) {
+ if (size < 0) {
+ perror("read()");
+ return errno;
+ }
+ p = buf;
+ while (size > 0) {
+ if (mtdInfo.type == MTD_NANDFLASH) {
+ /* Fill remain buffers with 0xff */
+ memset(buf + size, 0xff, mtdInfo.oobblock - size);
+ size = mtdInfo.oobblock;
+ }
+ ret = write(fd, p, size);
+ if (ret < 0) {
+ perror("write()");
+ return errno;
+ }
+ if (ret == 0) {
+ printf("write() returned 0 after writing %d bytes\n", wrote);
+ return 0;
+ }
+ p += ret;
+ wrote += ret;
+ size -= ret;
+ }
+ }
+
+ printf("Wrote %d bytes of OTP user data\n", wrote);
+ return 0;
+}
diff --git a/flash_unlock.c b/flash_unlock.c
new file mode 100644
index 0000000..f4d7e20
--- /dev/null
+++ b/flash_unlock.c
@@ -0,0 +1,64 @@
+/*
+ * FILE flash_unlock.c
+ *
+ * This utility unlock all sectors of flash device.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+
+#include <mtd/mtd-user.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct mtd_info_user mtdInfo;
+ struct erase_info_user mtdLockInfo;
+
+ /*
+ * Parse command line options
+ */
+ if(argc != 2)
+ {
+ fprintf(stderr, "USAGE: %s <mtd device>\n", argv[0]);
+ exit(1);
+ }
+ else if(strncmp(argv[1], "/dev/mtd", 8) != 0)
+ {
+ fprintf(stderr, "'%s' is not a MTD device. Must specify mtd device: /dev/mtd?\n", argv[1]);
+ exit(1);
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if(fd < 0)
+ {
+ fprintf(stderr, "Could not open mtd device: %s\n", argv[1]);
+ exit(1);
+ }
+
+ if(ioctl(fd, MEMGETINFO, &mtdInfo))
+ {
+ fprintf(stderr, "Could not get MTD device info from %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ mtdLockInfo.start = 0;
+ mtdLockInfo.length = mtdInfo.size;
+ if(ioctl(fd, MEMUNLOCK, &mtdLockInfo))
+ {
+ fprintf(stderr, "Could not unlock MTD device: %s\n", argv[1]);
+ close(fd);
+ exit(1);
+ }
+
+ return 0;
+}
+
diff --git a/flashcp.c b/flashcp.c
new file mode 100644
index 0000000..8033f53
--- /dev/null
+++ b/flashcp.c
@@ -0,0 +1,392 @@
+
+/*
+ * Copyright (c) 2d3D, Inc.
+ * Written by Abraham vd Merwe <abraham@2d3d.co.za>
+ * All rights reserved.
+ *
+ * $Id: flashcp.c,v 1.6 2005/11/07 11:15:11 gleixner Exp $
+ *
+ * Renamed to flashcp.c to avoid conflicts with fcp from fsh package
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of other contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <mtd/mtd-user.h>
+#include <getopt.h>
+
+typedef int bool;
+#define true 1
+#define false 0
+
+#define EXIT_FAILURE 1
+#define EXIT_SUCCESS 0
+
+/* for debugging purposes only */
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
+#else
+#undef DEBUG
+#define DEBUG(fmt,args...)
+#endif
+
+#define KB(x) ((x) / 1024)
+#define PERCENTAGE(x,total) (((x) * 100) / (total))
+
+/* size of read/write buffer */
+#define BUFSIZE (10 * 1024)
+
+/* cmd-line flags */
+#define FLAG_NONE 0x00
+#define FLAG_VERBOSE 0x01
+#define FLAG_HELP 0x02
+#define FLAG_FILENAME 0x04
+#define FLAG_DEVICE 0x08
+
+/* error levels */
+#define LOG_NORMAL 1
+#define LOG_ERROR 2
+
+static void log_printf (int level,const char *fmt, ...)
+{
+ FILE *fp = level == LOG_NORMAL ? stdout : stderr;
+ va_list ap;
+ va_start (ap,fmt);
+ vfprintf (fp,fmt,ap);
+ va_end (ap);
+ fflush (fp);
+}
+
+static void showusage (const char *progname,bool error)
+{
+ int level = error ? LOG_ERROR : LOG_NORMAL;
+
+ log_printf (level,
+ "\n"
+ "Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
+ "\n"
+ "usage: %s [ -v | --verbose ] <filename> <device>\n"
+ " %s -h | --help\n"
+ "\n"
+ " -h | --help Show this help message\n"
+ " -v | --verbose Show progress reports\n"
+ " <filename> File which you want to copy to flash\n"
+ " <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
+ "\n",
+ progname,progname);
+
+ exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static int safe_open (const char *pathname,int flags)
+{
+ int fd;
+
+ fd = open (pathname,flags);
+ if (fd < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to open %s",pathname);
+ if (flags & O_RDWR)
+ log_printf (LOG_ERROR," for read/write access");
+ else if (flags & O_RDONLY)
+ log_printf (LOG_ERROR," for read access");
+ else if (flags & O_WRONLY)
+ log_printf (LOG_ERROR," for write access");
+ log_printf (LOG_ERROR,": %m\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return (fd);
+}
+
+static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
+{
+ ssize_t result;
+
+ result = read (fd,buf,count);
+ if (count != result)
+ {
+ if (verbose) log_printf (LOG_NORMAL,"\n");
+ if (result < 0)
+ {
+ log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+ log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void safe_rewind (int fd,const char *filename)
+{
+ if (lseek (fd,0L,SEEK_SET) < 0)
+ {
+ log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+}
+
+/******************************************************************************/
+
+static int dev_fd = -1,fil_fd = -1;
+
+static void cleanup (void)
+{
+ if (dev_fd > 0) close (dev_fd);
+ if (fil_fd > 0) close (fil_fd);
+}
+
+int main (int argc,char *argv[])
+{
+ const char *progname,*filename = NULL,*device = NULL;
+ int i,flags = FLAG_NONE;
+ ssize_t result;
+ size_t size,written;
+ struct mtd_info_user mtd;
+ struct erase_info_user erase;
+ struct stat filestat;
+ unsigned char src[BUFSIZE],dest[BUFSIZE];
+
+ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
+
+ /*********************
+ * parse cmd-line
+ *****************/
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hv";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"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':
+ flags |= FLAG_HELP;
+ DEBUG("Got FLAG_HELP\n");
+ break;
+ case 'v':
+ flags |= FLAG_VERBOSE;
+ DEBUG("Got FLAG_VERBOSE\n");
+ break;
+ default:
+ DEBUG("Unknown parameter: %s\n",argv[option_index]);
+ showusage (progname,true);
+ }
+ }
+ if (optind+2 == argc) {
+ flags |= FLAG_FILENAME;
+ filename = argv[optind];
+ DEBUG("Got filename: %s\n",filename);
+
+ flags |= FLAG_DEVICE;
+ device = argv[optind+1];
+ DEBUG("Got device: %s\n",device);
+ }
+
+ if (flags & FLAG_HELP || progname == NULL || device == NULL)
+ showusage (progname,flags != FLAG_HELP);
+
+ atexit (cleanup);
+
+ /* get some info about the flash device */
+ dev_fd = safe_open (device,O_SYNC | O_RDWR);
+ if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
+ {
+ DEBUG("ioctl(): %m\n");
+ log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* get some info about the file we want to copy */
+ fil_fd = safe_open (filename,O_RDONLY);
+ if (fstat (fil_fd,&filestat) < 0)
+ {
+ log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
+ exit (EXIT_FAILURE);
+ }
+
+ /* does it fit into the device/partition? */
+ if (filestat.st_size > mtd.size)
+ {
+ log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
+ exit (EXIT_FAILURE);
+ }
+
+ /*****************************************************
+ * erase enough blocks so that we can write the file *
+ *****************************************************/
+
+#warning "Check for smaller erase regions"
+
+ erase.start = 0;
+ erase.length = filestat.st_size & ~(mtd.erasesize - 1);
+ if (filestat.st_size % mtd.erasesize) erase.length += mtd.erasesize;
+ if (flags & FLAG_VERBOSE)
+ {
+ /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
+ int blocks = erase.length / mtd.erasesize;
+ erase.length = mtd.erasesize;
+ log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
+ for (i = 1; i <= blocks; i++)
+ {
+ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
+ if (ioctl (dev_fd,MEMERASE,&erase) < 0)
+ {
+ log_printf (LOG_NORMAL,"\n");
+ log_printf (LOG_ERROR,
+ "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
+ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
+ exit (EXIT_FAILURE);
+ }
+ erase.start += mtd.erasesize;
+ }
+ log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
+ }
+ else
+ {
+ /* if not, erase the whole chunk in one shot */
+ if (ioctl (dev_fd,MEMERASE,&erase) < 0)
+ {
+ log_printf (LOG_ERROR,
+ "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
+ (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
+ exit (EXIT_FAILURE);
+ }
+ }
+ DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
+
+ /**********************************
+ * write the entire file to flash *
+ **********************************/
+
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size));
+ size = filestat.st_size;
+ i = BUFSIZE;
+ written = 0;
+ while (size)
+ {
+ if (size < BUFSIZE) i = size;
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)",
+ KB (written + i),
+ KB (filestat.st_size),
+ PERCENTAGE (written + i,filestat.st_size));
+
+ /* read from filename */
+ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
+
+ /* write to device */
+ result = write (dev_fd,src,i);
+ if (i != result)
+ {
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
+ if (result < 0)
+ {
+ log_printf (LOG_ERROR,
+ "While writing data to 0x%.8x-0x%.8x on %s: %m\n",
+ written,written + i,device);
+ exit (EXIT_FAILURE);
+ }
+ log_printf (LOG_ERROR,
+ "Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n",
+ written,written + i,device,written + result,filestat.st_size);
+ exit (EXIT_FAILURE);
+ }
+
+ written += i;
+ size -= i;
+ }
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rWriting data: %luk/%luk (100%%)\n",
+ KB (filestat.st_size),
+ KB (filestat.st_size));
+ DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size);
+
+ /**********************************
+ * verify that flash == file data *
+ **********************************/
+
+ safe_rewind (fil_fd,filename);
+ safe_rewind (dev_fd,device);
+ size = filestat.st_size;
+ i = BUFSIZE;
+ written = 0;
+ if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size));
+ while (size)
+ {
+ if (size < BUFSIZE) i = size;
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rVerifying data: %dk/%luk (%lu%%)",
+ KB (written + i),
+ KB (filestat.st_size),
+ PERCENTAGE (written + i,filestat.st_size));
+
+ /* read from filename */
+ safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
+
+ /* read from device */
+ safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
+
+ /* compare buffers */
+ if (memcmp (src,dest,i))
+ {
+ log_printf (LOG_ERROR,
+ "File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
+ written,written + i);
+ exit (EXIT_FAILURE);
+ }
+
+ written += i;
+ size -= i;
+ }
+ if (flags & FLAG_VERBOSE)
+ log_printf (LOG_NORMAL,
+ "\rVerifying data: %luk/%luk (100%%)\n",
+ KB (filestat.st_size),
+ KB (filestat.st_size));
+ DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size);
+
+ exit (EXIT_SUCCESS);
+}
+
diff --git a/ftl_check.c b/ftl_check.c
new file mode 100644
index 0000000..1a64f6b
--- /dev/null
+++ b/ftl_check.c
@@ -0,0 +1,233 @@
+/* Ported to MTD system.
+ * $Id: ftl_check.c,v 1.6 2005/11/07 11:15:11 gleixner Exp $
+ * Based on:
+ */
+/*======================================================================
+
+ Utility to create an FTL partition in a memory region
+
+ ftl_check.c 1.10 1999/10/25 20:01:35
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/mtd/ftl.h>
+
+#include <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TO_LE32(x) (x)
+# define TO_LE16(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define TO_LE32(x) (bswap_32(x))
+# define TO_LE16(x) (bswap_16(x))
+#else
+# error cannot detect endianess
+#endif
+
+#define FROM_LE32(x) TO_LE32(x)
+#define FROM_LE16(x) TO_LE16(x)
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static void check_partition(int fd, int verbose)
+{
+ mtd_info_t mtd;
+ erase_unit_header_t hdr, hdr2;
+ u_int i, j, nbam, *bam;
+ int control, data, free, deleted;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return;
+ }
+
+ printf("Memory region info:\n");
+ printf(" Region size = ");
+ print_size(mtd.size);
+ printf(" Erase block size = ");
+ print_size(mtd.erasesize);
+ printf("\n\n");
+
+ for (i = 0; i < mtd.size/mtd.erasesize; i++) {
+ if (lseek(fd, (i * mtd.erasesize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ read(fd, &hdr, sizeof(hdr));
+ if ((FROM_LE32(hdr.FormattedSize) > 0) &&
+ (FROM_LE32(hdr.FormattedSize) <= mtd.size) &&
+ (FROM_LE16(hdr.NumEraseUnits) > 0) &&
+ (FROM_LE16(hdr.NumEraseUnits) <= mtd.size/mtd.erasesize))
+ break;
+ }
+ if (i == mtd.size/mtd.erasesize) {
+ fprintf(stderr, "No valid erase unit headers!\n");
+ return;
+ }
+
+ printf("Partition header:\n");
+ printf(" Formatted size = ");
+ print_size(FROM_LE32(hdr.FormattedSize));
+ printf(", erase units = %d, transfer units = %d\n",
+ FROM_LE16(hdr.NumEraseUnits), hdr.NumTransferUnits);
+ printf(" Erase unit size = ");
+ print_size(1 << hdr.EraseUnitSize);
+ printf(", virtual block size = ");
+ print_size(1 << hdr.BlockSize);
+ printf("\n");
+
+ /* Create basic block allocation table for control blocks */
+ nbam = (mtd.erasesize >> hdr.BlockSize);
+ bam = malloc(nbam * sizeof(u_int));
+
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ if (lseek(fd, (i << hdr.EraseUnitSize), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, &hdr2, sizeof(hdr2)) == -1) {
+ perror("read failed");
+ break;
+ }
+ printf("\nErase unit %d:\n", i);
+ if ((hdr2.FormattedSize != hdr.FormattedSize) ||
+ (hdr2.NumEraseUnits != hdr.NumEraseUnits) ||
+ (hdr2.SerialNumber != hdr.SerialNumber))
+ printf(" Erase unit header is corrupt.\n");
+ else if (FROM_LE16(hdr2.LogicalEUN) == 0xffff)
+ printf(" Transfer unit, erase count = %d\n", FROM_LE32(hdr2.EraseCount));
+ else {
+ printf(" Logical unit %d, erase count = %d\n",
+ FROM_LE16(hdr2.LogicalEUN), FROM_LE32(hdr2.EraseCount));
+ if (lseek(fd, (i << hdr.EraseUnitSize)+FROM_LE32(hdr.BAMOffset),
+ SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (read(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("read failed");
+ break;
+ }
+ free = deleted = control = data = 0;
+ for (j = 0; j < nbam; j++) {
+ if (BLOCK_FREE(FROM_LE32(bam[j])))
+ free++;
+ else if (BLOCK_DELETED(FROM_LE32(bam[j])))
+ deleted++;
+ else switch (BLOCK_TYPE(FROM_LE32(bam[j]))) {
+ case BLOCK_CONTROL: control++; break;
+ case BLOCK_DATA: data++; break;
+ default: break;
+ }
+ }
+ printf(" Block allocation: %d control, %d data, %d free,"
+ " %d deleted\n", control, data, free, deleted);
+ }
+ }
+} /* format_partition */
+
+/* Show usage information */
+void showusage(char *pname)
+{
+ fprintf(stderr, "usage: %s [-v] device\n", pname);
+ fprintf(stderr, "-v verbose messages\n");
+}
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int verbose;
+ int optch, errflg, fd;
+ struct stat buf;
+
+ errflg = 0;
+ verbose = 0;
+ while ((optch = getopt(argc, argv, "vh")) != -1) {
+ switch (optch) {
+ case 'h':
+ errflg = 1; break;
+ case 'v':
+ verbose = 1; break;
+ default:
+ errflg = -1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ showusage(argv[0]);
+ exit(errflg > 0 ? 0 : EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ check_partition(fd, verbose);
+ close(fd);
+
+ exit(EXIT_SUCCESS);
+ return 0;
+}
diff --git a/ftl_format.c b/ftl_format.c
new file mode 100644
index 0000000..231fb3f
--- /dev/null
+++ b/ftl_format.c
@@ -0,0 +1,343 @@
+/* Ported to MTD system.
+ * $Id: ftl_format.c,v 1.9 2005/11/07 11:15:12 gleixner Exp $
+ * Based on:
+ */
+/*======================================================================
+
+ Utility to create an FTL partition in a memory region
+
+ ftl_format.c 1.13 1999/10/25 20:01:35
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/mtd/ftl.h>
+
+#include <byteswap.h>
+#include <endian.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define TO_LE32(x) (x)
+# define TO_LE16(x) (x)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define TO_LE32(x) (bswap_32(x))
+# define TO_LE16(x) (bswap_16(x))
+#else
+# error cannot detect endianess
+#endif
+
+#define FROM_LE32(x) TO_LE32(x)
+#define FROM_LE16(x) TO_LE16(x)
+
+/*====================================================================*/
+
+static void print_size(u_int s)
+{
+ if ((s > 0x100000) && ((s % 0x100000) == 0))
+ printf("%d mb", s / 0x100000);
+ else if ((s > 0x400) && ((s % 0x400) == 0))
+ printf("%d kb", s / 0x400);
+ else
+ printf("%d bytes", s);
+}
+
+/*====================================================================*/
+
+static const char LinkTarget[] = {
+ 0x13, 0x03, 'C', 'I', 'S'
+};
+static const char DataOrg[] = {
+ 0x46, 0x39, 0x00, 'F', 'T', 'L', '1', '0', '0', 0x00
+};
+
+static void build_header(erase_unit_header_t *hdr, u_int RegionSize,
+ u_int BlockSize, u_int Spare, int Reserve,
+ u_int BootSize)
+{
+ u_int i, BootUnits, nbam, __FormattedSize;
+
+ /* Default everything to the erased state */
+ memset(hdr, 0xff, sizeof(*hdr));
+ memcpy(hdr->LinkTargetTuple, LinkTarget, 5);
+ memcpy(hdr->DataOrgTuple, DataOrg, 10);
+ hdr->EndTuple[0] = hdr->EndTuple[1] = 0xff;
+ BootSize = (BootSize + (BlockSize-1)) & ~(BlockSize-1);
+ BootUnits = BootSize / BlockSize;
+
+ /* We only support 512-byte blocks */
+ hdr->BlockSize = 9;
+ hdr->EraseUnitSize = 0;
+ for (i = BlockSize; i > 1; i >>= 1)
+ hdr->EraseUnitSize++;
+ hdr->EraseCount = TO_LE32(0);
+ hdr->FirstPhysicalEUN = TO_LE16(BootUnits);
+ hdr->NumEraseUnits = TO_LE16((RegionSize - BootSize) >> hdr->EraseUnitSize);
+ hdr->NumTransferUnits = Spare;
+ __FormattedSize = RegionSize - ((Spare + BootUnits) << hdr->EraseUnitSize);
+ /* Leave a little bit of space between the CIS and BAM */
+ hdr->BAMOffset = TO_LE32(0x80);
+ /* Adjust size to account for BAM space */
+ nbam = ((1 << (hdr->EraseUnitSize - hdr->BlockSize)) * sizeof(u_int)
+ + FROM_LE32(hdr->BAMOffset) + (1 << hdr->BlockSize) - 1) >> hdr->BlockSize;
+
+ __FormattedSize -=
+ (FROM_LE16(hdr->NumEraseUnits) - Spare) * (nbam << hdr->BlockSize);
+ __FormattedSize -= ((__FormattedSize * Reserve / 100) & ~0xfff);
+
+ hdr->FormattedSize = TO_LE32(__FormattedSize);
+
+ /* hdr->FirstVMAddress defaults to erased state */
+ hdr->NumVMPages = TO_LE16(0);
+ hdr->Flags = 0;
+ /* hdr->Code defaults to erased state */
+ hdr->SerialNumber = TO_LE32(time(NULL));
+ /* hdr->AltEUHOffset defaults to erased state */
+
+} /* build_header */
+
+/*====================================================================*/
+
+static int format_partition(int fd, int quiet, int interrogate,
+ u_int spare, int reserve, u_int bootsize)
+{
+ mtd_info_t mtd;
+ erase_info_t erase;
+ erase_unit_header_t hdr;
+ u_int step, lun, i, nbam, *bam;
+
+ /* Get partition size, block size */
+ if (ioctl(fd, MEMGETINFO, &mtd) != 0) {
+ perror("get info failed");
+ return -1;
+ }
+
+#if 0
+ /* Intel Series 100 Flash: skip first block */
+ if ((region.JedecMfr == 0x89) && (region.JedecInfo == 0xaa) &&
+ (bootsize == 0)) {
+ if (!quiet)
+ printf("Skipping first block to protect CIS info...\n");
+ bootsize = 1;
+ }
+#endif
+
+ /* Create header */
+ build_header(&hdr, mtd.size, mtd.erasesize,
+ spare, reserve, bootsize);
+
+ if (!quiet) {
+ printf("Partition size = ");
+ print_size(mtd.size);
+ printf(", erase unit size = ");
+ print_size(mtd.erasesize);
+ printf(", %d transfer units\n", spare);
+ if (bootsize != 0) {
+ print_size(FROM_LE16(hdr.FirstPhysicalEUN) << hdr.EraseUnitSize);
+ printf(" allocated for boot image\n");
+ }
+ printf("Reserved %d%%, formatted size = ", reserve);
+ print_size(FROM_LE32(hdr.FormattedSize));
+ printf("\n");
+ fflush(stdout);
+ }
+
+ if (interrogate) {
+ char str[3];
+ printf("This will destroy all data on the target device. "
+ "Confirm (y/n): ");
+ if (fgets(str, 3, stdin) == NULL)
+ return -1;
+ if ((strcmp(str, "y\n") != 0) && (strcmp(str, "Y\n") != 0))
+ return -1;
+ }
+
+ /* Create basic block allocation table for control blocks */
+ nbam = ((mtd.erasesize >> hdr.BlockSize) * sizeof(u_int)
+ + FROM_LE32(hdr.BAMOffset) + (1 << hdr.BlockSize) - 1) >> hdr.BlockSize;
+ bam = malloc(nbam * sizeof(u_int));
+ for (i = 0; i < nbam; i++)
+ bam[i] = TO_LE32(BLOCK_CONTROL);
+
+ /* Erase partition */
+ if (!quiet) {
+ printf("Erasing all blocks...\n");
+ fflush(stdout);
+ }
+ erase.length = mtd.erasesize;
+ erase.start = mtd.erasesize * FROM_LE16(hdr.FirstPhysicalEUN);
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ if (ioctl(fd, MEMERASE, &erase) < 0) {
+ if (!quiet) {
+ putchar('\n');
+ fflush(stdout);
+ }
+ perror("block erase failed");
+ return -1;
+ }
+ erase.start += erase.length;
+ if (!quiet) {
+ if (mtd.size <= 0x800000) {
+ if (erase.start % 0x100000) {
+ if (!(erase.start % 0x20000)) putchar('-');
+ }
+ else putchar('+');
+ }
+ else {
+ if (erase.start % 0x800000) {
+ if (!(erase.start % 0x100000)) putchar('+');
+ }
+ else putchar('*');
+ }
+ fflush(stdout);
+ }
+ }
+ if (!quiet) putchar('\n');
+
+ /* Prepare erase units */
+ if (!quiet) {
+ printf("Writing erase unit headers...\n");
+ fflush(stdout);
+ }
+ lun = 0;
+ /* Distribute transfer units over the entire region */
+ step = (spare) ? (FROM_LE16(hdr.NumEraseUnits)/spare) : (FROM_LE16(hdr.NumEraseUnits)+1);
+ for (i = 0; i < FROM_LE16(hdr.NumEraseUnits); i++) {
+ u_int ofs = (i + FROM_LE16(hdr.FirstPhysicalEUN)) << hdr.EraseUnitSize;
+ if (lseek(fd, ofs, SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ /* Is this a transfer unit? */
+ if (((i+1) % step) == 0)
+ hdr.LogicalEUN = TO_LE16(0xffff);
+ else {
+ hdr.LogicalEUN = TO_LE16(lun);
+ lun++;
+ }
+ if (write(fd, &hdr, sizeof(hdr)) == -1) {
+ perror("write failed");
+ break;
+ }
+ if (lseek(fd, ofs + FROM_LE32(hdr.BAMOffset), SEEK_SET) == -1) {
+ perror("seek failed");
+ break;
+ }
+ if (write(fd, bam, nbam * sizeof(u_int)) == -1) {
+ perror("write failed");
+ break;
+ }
+ }
+ if (i < FROM_LE16(hdr.NumEraseUnits))
+ return -1;
+ else
+ return 0;
+} /* format_partition */
+
+/*====================================================================*/
+
+int main(int argc, char *argv[])
+{
+ int quiet, interrogate, reserve;
+ int optch, errflg, fd, ret;
+ u_int spare, bootsize;
+ char *s;
+ extern char *optarg;
+ struct stat buf;
+
+ quiet = 0;
+ interrogate = 0;
+ spare = 1;
+ reserve = 5;
+ errflg = 0;
+ bootsize = 0;
+
+ while ((optch = getopt(argc, argv, "qir:s:b:")) != -1) {
+ switch (optch) {
+ case 'q':
+ quiet = 1; break;
+ case 'i':
+ interrogate = 1; break;
+ case 's':
+ spare = strtoul(optarg, NULL, 0); break;
+ case 'r':
+ reserve = strtoul(optarg, NULL, 0); break;
+ case 'b':
+ bootsize = strtoul(optarg, &s, 0);
+ if ((*s == 'k') || (*s == 'K'))
+ bootsize *= 1024;
+ break;
+ default:
+ errflg = 1; break;
+ }
+ }
+ if (errflg || (optind != argc-1)) {
+ fprintf(stderr, "usage: %s [-q] [-i] [-s spare-blocks]"
+ " [-r reserve-percent] [-b bootsize] device\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (stat(argv[optind], &buf) != 0) {
+ perror("status check failed");
+ exit(EXIT_FAILURE);
+ }
+ if (!(buf.st_mode & S_IFCHR)) {
+ fprintf(stderr, "%s is not a character special device\n",
+ argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+ fd = open(argv[optind], O_RDWR);
+ if (fd == -1) {
+ perror("open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = format_partition(fd, quiet, interrogate, spare, reserve,
+ bootsize);
+ if (!quiet) {
+ if (ret)
+ printf("format failed.\n");
+ else
+ printf("format successful.\n");
+ }
+ close(fd);
+
+ exit((ret) ? EXIT_FAILURE : EXIT_SUCCESS);
+ return 0;
+}
diff --git a/jffs-dump.c b/jffs-dump.c
new file mode 100644
index 0000000..0a8b7e5
--- /dev/null
+++ b/jffs-dump.c
@@ -0,0 +1,361 @@
+/*
+ * Dump JFFS filesystem.
+ * Useful when it buggers up.
+ */
+
+/* $Id: jffs-dump.c,v 1.3 2005/11/07 11:15:12 gleixner Exp $ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define BLOCK_SIZE 1024
+#define JFFS_MAGIC 0x34383931 /* "1984" */
+#define JFFS_MAX_NAME_LEN 256
+#define JFFS_MIN_INO 1
+#define JFFS_TRACE_INDENT 4
+#define JFFS_ALIGN_SIZE 4
+#define MAX_CHUNK_SIZE 32768
+
+/* How many padding bytes should be inserted between two chunks of data
+ on the flash? */
+#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \
+ - ((__u32)(size) % JFFS_ALIGN_SIZE)) \
+ % JFFS_ALIGN_SIZE)
+
+#define JFFS_EMPTY_BITMASK 0xffffffff
+#define JFFS_MAGIC_BITMASK 0x34383931
+#define JFFS_DIRTY_BITMASK 0x00000000
+
+#define min(x,y) (x) > (y) ? (y) : (x)
+
+struct jffs_raw_inode
+{
+ __u32 magic; /* A constant magic number. */
+ __u32 ino; /* Inode number. */
+ __u32 pino; /* Parent's inode number. */
+ __u32 version; /* Version number. */
+ __u32 mode; /* file_type, mode */
+ __u16 uid;
+ __u16 gid;
+ __u32 atime;
+ __u32 mtime;
+ __u32 ctime;
+ __u32 offset; /* Where to begin to write. */
+ __u32 dsize; /* Size of the file data. */
+ __u32 rsize; /* How much are going to be replaced? */
+ __u8 nsize; /* Name length. */
+ __u8 nlink; /* Number of links. */
+ __u8 spare : 6; /* For future use. */
+ __u8 rename : 1; /* Is this a special rename? */
+ __u8 deleted : 1; /* Has this file been deleted? */
+ __u8 accurate; /* The inode is obsolete if accurate == 0. */
+ __u32 dchksum; /* Checksum for the data. */
+ __u16 nchksum; /* Checksum for the name. */
+ __u16 chksum; /* Checksum for the raw_inode. */
+};
+
+
+struct jffs_file
+{
+ struct jffs_raw_inode inode;
+ char *name;
+ unsigned char *data;
+};
+
+
+char *root_directory_name = NULL;
+int fs_pos = 0;
+int verbose = 0;
+
+#define ENDIAN_HOST 0
+#define ENDIAN_BIG 1
+#define ENDIAN_LITTLE 2
+int endian = ENDIAN_HOST;
+
+static __u32 jffs_checksum(void *data, int size);
+void jffs_print_trace(const char *path, int depth);
+int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path,
+ int depth);
+void write_file(struct jffs_file *f, FILE *fs, struct stat st);
+void read_data(struct jffs_file *f, const char *path, int offset);
+int mkfs(FILE *fs, const char *path, int ino, int parent, int depth);
+
+
+static __u32
+jffs_checksum(void *data, int size)
+{
+ __u32 sum = 0;
+ __u8 *ptr = (__u8 *)data;
+
+ while (size-- > 0)
+ {
+ sum += *ptr++;
+ }
+
+ return sum;
+}
+
+
+void
+jffs_print_trace(const char *path, int depth)
+{
+ int path_len = strlen(path);
+ int out_pos = depth * JFFS_TRACE_INDENT;
+ int pos = path_len - 1;
+ char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1);
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path);
+ }
+
+ if (!out) {
+ fprintf(stderr, "jffs_print_trace(): Allocation failed.\n");
+ fprintf(stderr, " path: \"%s\"\n", path);
+ fprintf(stderr, "depth: %d\n", depth);
+ exit(1);
+ }
+
+ memset(out, ' ', depth * JFFS_TRACE_INDENT);
+
+ if (path[pos] == '/')
+ {
+ pos--;
+ }
+ while (path[pos] && (path[pos] != '/'))
+ {
+ pos--;
+ }
+ for (pos++; path[pos] && (path[pos] != '/'); pos++)
+ {
+ out[out_pos++] = path[pos];
+ }
+ out[out_pos] = '\0';
+ fprintf(stderr, "%s\n", out);
+}
+
+
+/* Print the contents of a raw inode. */
+void
+jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
+{
+ fprintf(stdout, "jffs_raw_inode: inode number: %u, version %u\n", raw_inode->ino, raw_inode->version);
+ fprintf(stdout, "{\n");
+ fprintf(stdout, " 0x%08x, /* magic */\n", raw_inode->magic);
+ fprintf(stdout, " 0x%08x, /* ino */\n", raw_inode->ino);
+ fprintf(stdout, " 0x%08x, /* pino */\n", raw_inode->pino);
+ fprintf(stdout, " 0x%08x, /* version */\n", raw_inode->version);
+ fprintf(stdout, " 0x%08x, /* mode */\n", raw_inode->mode);
+ fprintf(stdout, " 0x%04x, /* uid */\n", raw_inode->uid);
+ fprintf(stdout, " 0x%04x, /* gid */\n", raw_inode->gid);
+ fprintf(stdout, " 0x%08x, /* atime */\n", raw_inode->atime);
+ fprintf(stdout, " 0x%08x, /* mtime */\n", raw_inode->mtime);
+ fprintf(stdout, " 0x%08x, /* ctime */\n", raw_inode->ctime);
+ fprintf(stdout, " 0x%08x, /* offset */\n", raw_inode->offset);
+ fprintf(stdout, " 0x%08x, /* dsize */\n", raw_inode->dsize);
+ fprintf(stdout, " 0x%08x, /* rsize */\n", raw_inode->rsize);
+ fprintf(stdout, " 0x%02x, /* nsize */\n", raw_inode->nsize);
+ fprintf(stdout, " 0x%02x, /* nlink */\n", raw_inode->nlink);
+ fprintf(stdout, " 0x%02x, /* spare */\n",
+ raw_inode->spare);
+ fprintf(stdout, " %u, /* rename */\n",
+ raw_inode->rename);
+ fprintf(stdout, " %u, /* deleted */\n",
+ raw_inode->deleted);
+ fprintf(stdout, " 0x%02x, /* accurate */\n",
+ raw_inode->accurate);
+ fprintf(stdout, " 0x%08x, /* dchksum */\n", raw_inode->dchksum);
+ fprintf(stdout, " 0x%04x, /* nchksum */\n", raw_inode->nchksum);
+ fprintf(stdout, " 0x%04x, /* chksum */\n", raw_inode->chksum);
+ fprintf(stdout, "}\n");
+}
+
+static void write_val32(__u32 *adr, __u32 val)
+{
+ switch(endian) {
+ case ENDIAN_HOST:
+ *adr = val;
+ break;
+ case ENDIAN_LITTLE:
+ *adr = __cpu_to_le32(val);
+ break;
+ case ENDIAN_BIG:
+ *adr = __cpu_to_be32(val);
+ break;
+ }
+}
+
+static void write_val16(__u16 *adr, __u16 val)
+{
+ switch(endian) {
+ case ENDIAN_HOST:
+ *adr = val;
+ break;
+ case ENDIAN_LITTLE:
+ *adr = __cpu_to_le16(val);
+ break;
+ case ENDIAN_BIG:
+ *adr = __cpu_to_be16(val);
+ break;
+ }
+}
+
+static __u32 read_val32(__u32 *adr)
+{
+ __u32 val;
+
+ switch(endian) {
+ case ENDIAN_HOST:
+ val = *adr;
+ break;
+ case ENDIAN_LITTLE:
+ val = __le32_to_cpu(*adr);
+ break;
+ case ENDIAN_BIG:
+ val = __be32_to_cpu(*adr);
+ break;
+ }
+ return val;
+}
+
+static __u16 read_val16(__u16 *adr)
+{
+ __u16 val;
+
+ switch(endian) {
+ case ENDIAN_HOST:
+ val = *adr;
+ break;
+ case ENDIAN_LITTLE:
+ val = __le16_to_cpu(*adr);
+ break;
+ case ENDIAN_BIG:
+ val = __be16_to_cpu(*adr);
+ break;
+ }
+ return val;
+}
+
+int
+main(int argc, char **argv)
+{
+ int fs;
+ struct stat sb;
+ __u32 wordbuf;
+ off_t pos = 0;
+ off_t end;
+ struct jffs_raw_inode ino;
+ unsigned char namebuf[4096];
+ int myino = -1;
+
+ if (argc < 2) {
+ printf("no filesystem given\n");
+ exit(1);
+ }
+
+ fs = open(argv[1], O_RDONLY);
+ if (fs < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ if (argc > 2) {
+ myino = atol(argv[2]);
+ printf("Printing ino #%d\n" , myino);
+ }
+
+ if (fstat(fs, &sb) < 0) {
+ perror("stat");
+ close(fs);
+ exit(1);
+ }
+ end = sb.st_size;
+
+ while (pos < end) {
+ if (pread(fs, &wordbuf, 4, pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+
+ switch(wordbuf) {
+ case JFFS_EMPTY_BITMASK:
+ // printf("0xff started at 0x%lx\n", pos);
+ for (; pos < end && wordbuf == JFFS_EMPTY_BITMASK; pos += 4) {
+ if (pread(fs, &wordbuf, 4, pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+ }
+ if (pos < end)
+ pos -= 4;
+ // printf("0xff ended at 0x%lx\n", pos);
+ continue;
+
+ case JFFS_DIRTY_BITMASK:
+ // printf("0x00 started at 0x%lx\n", pos);
+ for (; pos < end && wordbuf == JFFS_DIRTY_BITMASK; pos += 4) {
+ if (pread(fs, &wordbuf, 4, pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+ }
+ if (pos < end)
+ pos -=4;
+ // printf("0x00 ended at 0x%lx\n", pos);
+ continue;
+
+ default:
+ printf("Argh. Dirty memory at 0x%lx\n", pos);
+ // file_hexdump(fs, pos, 128);
+ for (pos += 4; pos < end; pos += 4) {
+ if (pread(fs, &wordbuf, 4, pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+ if (wordbuf == JFFS_MAGIC_BITMASK)
+ break;
+ }
+
+ case JFFS_MAGIC_BITMASK:
+ if (pread(fs, &ino, sizeof(ino), pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+ if (myino == -1 || ino.ino == myino) {
+ printf("Magic found at 0x%lx\n", pos);
+ jffs_print_raw_inode(&ino);
+ }
+ pos += sizeof(ino);
+
+ if (myino == -1 || ino.ino == myino) {
+ if (ino.nsize) {
+ if (pread(fs, namebuf, min(ino.nsize, 4095), pos) < 0) {
+ perror("pread");
+ exit(1);
+ }
+ if (ino.nsize < 4095)
+ namebuf[ino.nsize] = 0;
+ else
+ namebuf[4095] = 0;
+ printf("Name: \"%s\"\n", namebuf);
+ } else {
+ printf("No Name\n");
+ }
+ }
+ pos += (ino.nsize + 3) & ~3;
+
+ pos += (ino.dsize + 3) & ~3;
+ }
+
+
+
+ }
+}
diff --git a/jffs2dump.c b/jffs2dump.c
new file mode 100644
index 0000000..c5179a6
--- /dev/null
+++ b/jffs2dump.c
@@ -0,0 +1,725 @@
+/*
+ * dumpjffs2.c
+ *
+ * Copyright (C) 2003 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: jffs2dump.c,v 1.12 2005/11/07 11:15:12 gleixner Exp $
+ *
+ * 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.12 $"
+
+#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: dumpjffs2 [OPTION] INPUTFILE\n"
+ "Dumps the contents of a binary JFFS2 image.\n"
+ "\n"
+ " --help display this help and exit\n"
+ " --version output 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 fname --endianconvert=fname convert image endianness, output to file fname\n"
+ "-r --recalccrc recalc name and data crc on endian conversion\n"
+ "-d len --datsize=len size of data chunks, when oob data in binary image (NAND only)\n"
+ "-o len --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_ERASEBLOCK_HEADER:
+ printf ("%8s EBH node at 0x%08x, totlen 0x%08x, compat_fset 0x%02x, incompat_fset 0x%02x, rocompat_fset 0x%02x, erase_count 0x%08x\n",
+ obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->eh.totlen), node->eh.compat_fset,
+ node->eh.incompat_fset, node->eh.rocompat_fset, je32_to_cpu (node->eh.erase_count));
+
+ crc = crc32(0, p + sizeof(struct jffs2_unknown_node) + 4, je32_to_cpu(node->eh.totlen) - sizeof(struct jffs2_unknown_node) - 4);
+ if (crc != je32_to_cpu(node->eh.node_crc)) {
+ printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->eh.node_crc), crc);
+ p += PAD(je32_to_cpu (node->eh.totlen));
+ dirty += PAD(je32_to_cpu (node->eh.totlen));;
+ continue;
+ }
+
+ p += PAD(je32_to_cpu (node->eh.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 JFFS2_NODETYPE_ERASEBLOCK_HEADER:
+ newnode.eh.magic = cnv_e16 (node->eh.magic);
+ newnode.eh.nodetype = cnv_e16 (node->eh.nodetype);
+ newnode.eh.totlen = cnv_e32 (node->eh.totlen);
+ newnode.eh.hdr_crc = cpu_to_e32 (crc32 (0, &newnode, sizeof (struct jffs2_unknown_node) - 4));
+ newnode.eh.reserved = node->eh.reserved;
+ newnode.eh.compat_fset = node->eh.compat_fset;
+ newnode.eh.incompat_fset = node->eh.incompat_fset;
+ newnode.eh.rocompat_fset = node->eh.rocompat_fset;
+ newnode.eh.erase_count = cnv_e32 (node->eh.erase_count);
+ newnode.eh.node_crc = cpu_to_e32 (crc32 (0, (unsigned char *)&newnode + sizeof(struct jffs2_unknown_node) + 4,
+ je32_to_cpu(node->eh.totlen) - sizeof(struct jffs2_unknown_node) + 4));
+ write(fd, &newnode, sizeof(struct jffs2_raw_ebh));
+ write(fd, p + sizeof(struct jffs2_raw_ebh), PAD(je32_to_cpu(node->eh.totlen) - sizeof(struct jffs2_raw_ebh)));
+ p += PAD(je32_to_cpu (node->eh.totlen));
+ 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);
+}
diff --git a/jffs2reader.c b/jffs2reader.c
new file mode 100644
index 0000000..fdbf581
--- /dev/null
+++ b/jffs2reader.c
@@ -0,0 +1,941 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * jffs2reader v0.0.18 A jffs2 image reader
+ *
+ * Copyright (c) 2001 Jari Kirma <Jari.Kirma@hut.fi>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the author be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose, including commercial applications, and to alter it and
+ * redistribute it freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ *
+ *********
+ * This code was altered September 2001
+ * Changes are Copyright (c) Erik Andersen <andersen@codepoet.org>
+ *
+ * In compliance with (2) above, this is hereby marked as an altered
+ * version of this software. It has been altered as follows:
+ * *) Listing a directory now mimics the behavior of 'ls -l'
+ * *) Support for recursive listing has been added
+ * *) Without options, does a recursive 'ls' on the whole filesystem
+ * *) option parsing now uses getopt()
+ * *) Now uses printf, and error messages go to stderr.
+ * *) The copyright notice has been cleaned up and reformatted
+ * *) The code has been reformatted
+ * *) Several twisty code paths have been fixed so I can understand them.
+ * -Erik, 1 September 2001
+ *
+ * *) Made it show major/minor numbers for device nodes
+ * *) Made it show symlink targets
+ * -Erik, 13 September 2001
+ *
+ * $Id: jffs2reader.c,v 1.6 2005/11/07 11:15:12 gleixner Exp $
+*/
+
+
+/*
+ TODO:
+
+ - Add CRC checking code to places marked with XXX.
+ - Add support for other node compression types.
+
+ - Test with real life images.
+ - Maybe port into bootloader.
+*/
+
+/*
+ BUGS:
+
+ - Doesn't check CRC checksums.
+*/
+
+
+#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 <dirent.h>
+#include <zlib.h>
+#include <linux/jffs2.h>
+
+#define SCRATCH_SIZE (5*1024*1024)
+
+#ifndef MAJOR
+/* FIXME: I am using illicit insider knowledge of
+ * kernel major/minor representation... */
+#define MAJOR(dev) (((dev)>>8)&0xff)
+#define MINOR(dev) ((dev)&0xff)
+#endif
+
+
+#define DIRENT_INO(dirent) ((dirent)!=NULL?(dirent)->ino:0)
+#define DIRENT_PINO(dirent) ((dirent)!=NULL?(dirent)->pino:0)
+
+struct dir {
+ struct dir *next;
+ uint8_t type;
+ uint8_t nsize;
+ uint32_t ino;
+ char name[256];
+};
+
+void putblock(char *, size_t, size_t *, struct jffs2_raw_inode *);
+struct dir *putdir(struct dir *, struct jffs2_raw_dirent *);
+void printdir(char *o, size_t size, struct dir *d, char *path,
+ int recurse);
+void freedir(struct dir *);
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino);
+struct jffs2_raw_dirent *resolvedirent(char *, size_t, uint32_t, uint32_t,
+ char *, uint8_t);
+struct jffs2_raw_dirent *resolvename(char *, size_t, uint32_t, char *, uint8_t);
+struct jffs2_raw_dirent *resolveinode(char *, size_t, uint32_t);
+
+struct jffs2_raw_dirent *resolvepath0(char *, size_t, uint32_t, char *,
+ uint32_t *, int);
+struct jffs2_raw_dirent *resolvepath(char *, size_t, uint32_t, char *,
+ uint32_t *);
+
+void lsdir(char *, size_t, char *, int);
+void catfile(char *, size_t, char *, char *, size_t, size_t *);
+
+int main(int, char **);
+
+/* writes file node into buffer, to the proper position. */
+/* reading all valid nodes in version order reconstructs the file. */
+
+/*
+ b - buffer
+ bsize - buffer size
+ rsize - result size
+ n - node
+*/
+
+void putblock(char *b, size_t bsize, size_t * rsize,
+ struct jffs2_raw_inode *n)
+{
+ uLongf dlen = n->dsize;
+
+ if (n->isize > bsize || (n->offset + dlen) > bsize) {
+ fprintf(stderr, "File does not fit into buffer!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (*rsize < n->isize)
+ bzero(b + *rsize, n->isize - *rsize);
+
+ switch (n->compr) {
+ case JFFS2_COMPR_ZLIB:
+ uncompress((Bytef *) b + n->offset, &dlen,
+ (Bytef *) ((char *) n) + sizeof(struct jffs2_raw_inode),
+ (uLongf) n->csize);
+ break;
+
+ case JFFS2_COMPR_NONE:
+ memcpy(b + n->offset,
+ ((char *) n) + sizeof(struct jffs2_raw_inode), dlen);
+ break;
+
+ case JFFS2_COMPR_ZERO:
+ bzero(b + n->offset, dlen);
+ break;
+
+ /* [DYN]RUBIN support required! */
+
+ default:
+ fprintf(stderr, "Unsupported compression method!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ *rsize = n->isize;
+}
+
+/* adds/removes directory node into dir struct. */
+/* reading all valid nodes in version order reconstructs the directory. */
+
+/*
+ dd - directory struct being processed
+ n - node
+
+ return value: directory struct value replacing dd
+*/
+
+struct dir *putdir(struct dir *dd, struct jffs2_raw_dirent *n)
+{
+ struct dir *o, *d, *p;
+
+ o = dd;
+
+ if (n->ino) {
+ if (dd == NULL) {
+ d = malloc(sizeof(struct dir));
+ d->type = n->type;
+ memcpy(d->name, n->name, n->nsize);
+ d->nsize = n->nsize;
+ d->ino = n->ino;
+ d->next = NULL;
+
+ return d;
+ }
+
+ while (1) {
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ dd->type = n->type;
+ dd->ino = n->ino;
+
+ return o;
+ }
+
+ if (dd->next == NULL) {
+ dd->next = malloc(sizeof(struct dir));
+ dd->next->type = n->type;
+ memcpy(dd->next->name, n->name, n->nsize);
+ dd->next->nsize = n->nsize;
+ dd->next->ino = n->ino;
+ dd->next->next = NULL;
+
+ return o;
+ }
+
+ dd = dd->next;
+ }
+ } else {
+ if (dd == NULL)
+ return NULL;
+
+ if (n->nsize == dd->nsize && !memcmp(n->name, dd->name, n->nsize)) {
+ d = dd->next;
+ free(dd);
+ return d;
+ }
+
+ while (1) {
+ p = dd;
+ dd = dd->next;
+
+ if (dd == NULL)
+ return o;
+
+ if (n->nsize == dd->nsize &&
+ !memcmp(n->name, dd->name, n->nsize)) {
+ p->next = dd->next;
+ free(dd);
+
+ return o;
+ }
+ }
+ }
+}
+
+
+#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
+#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
+
+/* The special bits. If set, display SMODE0/1 instead of MODE0/1 */
+static const mode_t SBIT[] = {
+ 0, 0, S_ISUID,
+ 0, 0, S_ISGID,
+ 0, 0, S_ISVTX
+};
+
+/* The 9 mode bits to test */
+static const mode_t MBIT[] = {
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+};
+
+static const char MODE1[] = "rwxrwxrwx";
+static const char MODE0[] = "---------";
+static const char SMODE1[] = "..s..s..t";
+static const char SMODE0[] = "..S..S..T";
+
+/*
+ * Return the standard ls-like mode string from a file mode.
+ * This is static and so is overwritten on each call.
+ */
+const char *mode_string(int mode)
+{
+ static char buf[12];
+
+ int i;
+
+ buf[0] = TYPECHAR(mode);
+ for (i = 0; i < 9; i++) {
+ if (mode & SBIT[i])
+ buf[i + 1] = (mode & MBIT[i]) ? SMODE1[i] : SMODE0[i];
+ else
+ buf[i + 1] = (mode & MBIT[i]) ? MODE1[i] : MODE0[i];
+ }
+ return buf;
+}
+
+/* prints contents of directory structure */
+
+/*
+ d - dir struct
+*/
+
+void printdir(char *o, size_t size, struct dir *d, char *path, int recurse)
+{
+ char m;
+ char *filetime;
+ time_t age;
+ struct jffs2_raw_inode *ri;
+
+ if (!path)
+ return;
+ if (strlen(path) == 1 && *path == '/')
+ path++;
+
+ while (d != NULL) {
+ switch (d->type) {
+ case DT_REG:
+ m = ' ';
+ break;
+
+ case DT_FIFO:
+ m = '|';
+ break;
+
+ case DT_CHR:
+ m = ' ';
+ break;
+
+ case DT_BLK:
+ m = ' ';
+ break;
+
+ case DT_DIR:
+ m = '/';
+ break;
+
+ case DT_LNK:
+ m = ' ';
+ break;
+
+ case DT_SOCK:
+ m = '=';
+ break;
+
+ default:
+ m = '?';
+ }
+ ri = find_raw_inode(o, size, d->ino);
+ if (!ri) {
+ fprintf(stderr, "bug: raw_inode missing!\n");
+ d = d->next;
+ continue;
+ }
+
+ filetime = ctime((const time_t *) &(ri->ctime));
+ age = time(NULL) - ri->ctime;
+ printf("%s %-4d %-8d %-8d ", mode_string(ri->mode),
+ 1, ri->uid, ri->gid);
+ if ( d->type==DT_BLK || d->type==DT_CHR ) {
+ dev_t rdev;
+ size_t devsize;
+ putblock((char*)&rdev, sizeof(rdev), &devsize, ri);
+ printf("%4d, %3d ", (int)MAJOR(rdev), (int)MINOR(rdev));
+ } else {
+ printf("%9ld ", (long)ri->dsize);
+ }
+ d->name[d->nsize]='\0';
+ if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
+ /* hh:mm if less than 6 months old */
+ printf("%6.6s %5.5s %s/%s%c", filetime + 4, filetime + 11, path, d->name, m);
+ } else {
+ printf("%6.6s %4.4s %s/%s%c", filetime + 4, filetime + 20, path, d->name, m);
+ }
+ if (d->type == DT_LNK) {
+ char symbuf[1024];
+ size_t symsize;
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+ printf(" -> %s", symbuf);
+ }
+ printf("\n");
+
+ if (d->type == DT_DIR && recurse) {
+ char *tmp;
+ tmp = malloc(BUFSIZ);
+ if (!tmp) {
+ fprintf(stderr, "memory exhausted\n");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(tmp, "%s/%s", path, d->name);
+ lsdir(o, size, tmp, recurse); /* Go recursive */
+ free(tmp);
+ }
+
+ d = d->next;
+ }
+}
+
+/* frees memory used by directory structure */
+
+/*
+ d - dir struct
+*/
+
+void freedir(struct dir *d)
+{
+ struct dir *t;
+
+ while (d != NULL) {
+ t = d->next;
+ free(d);
+ d = t;
+ }
+}
+
+/* collects directory/file nodes in version order. */
+
+/*
+ f - file flag.
+ if zero, collect file, compare ino to inode
+ otherwise, collect directory, compare ino to parent inode
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - inode to compare against. see f.
+
+ return value: a jffs2_raw_inode that corresponds the the specified
+ inode, or NULL
+*/
+
+struct jffs2_raw_inode *find_raw_inode(char *o, size_t size, uint32_t ino)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_INODE &&
+ n->i.ino == ino && (v = n->i.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1))
+ return (&(n->i));
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin)
+ return (&(mp->i));
+ }
+ } while (vcur < vmax);
+
+ return NULL;
+}
+
+/* collects dir struct for selected inode */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - inode of the specified directory
+ d - input directory structure
+
+ return value: result directory structure, replaces d.
+*/
+
+struct dir *collectdir(char *o, size_t size, uint32_t ino, struct dir *d)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+ union jffs2_node_union *lr; /* last block position */
+ union jffs2_node_union *mp = NULL; /* minimum position */
+
+ uint32_t vmin, vmint, vmaxt, vmax, vcur, v;
+
+ vmin = 0; /* next to read */
+ vmax = ~((uint32_t) 0); /* last to read */
+ vmint = ~((uint32_t) 0);
+ vmaxt = 0; /* found maximum */
+ vcur = 0; /* XXX what is smallest version number used? */
+ /* too low version number can easily result excess log rereading */
+
+ n = (union jffs2_node_union *) o;
+ lr = n;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ n->d.pino == ino && (v = n->d.version) > vcur) {
+ /* XXX crc check */
+
+ if (vmaxt < v)
+ vmaxt = v;
+ if (vmint > v) {
+ vmint = v;
+ mp = n;
+ }
+
+ if (v == (vcur + 1)) {
+ d = putdir(d, &(n->d));
+
+ lr = n;
+ vcur++;
+ vmint = ~((uint32_t) 0);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ n = (union jffs2_node_union *) o; /* we're at the end, rewind to the beginning */
+
+ if (lr == n) { /* whole loop since last read */
+ vmax = vmaxt;
+ vmin = vmint;
+ vmint = ~((uint32_t) 0);
+
+ if (vcur < vmax && vcur < vmin) {
+ d = putdir(d, &(mp->d));
+
+ lr = n =
+ (union jffs2_node_union *) (((char *) mp) +
+ ((mp->u.totlen + 3) & ~3));
+
+ vcur = vmin;
+ }
+ }
+ } while (vcur < vmax);
+
+ return d;
+}
+
+
+
+/* resolve dirent based on criteria */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - if zero, ignore,
+ otherwise compare against dirent inode
+ pino - if zero, ingore,
+ otherwise compare against parent inode
+ and use name and nsize as extra criteria
+ name - name of wanted dirent, used if pino!=0
+ nsize - length of name of wanted dirent, used if pino!=0
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolvedirent(char *o, size_t size,
+ uint32_t ino, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ /* aligned! */
+ union jffs2_node_union *n;
+ union jffs2_node_union *e = (union jffs2_node_union *) (o + size);
+
+ struct jffs2_raw_dirent *dd = NULL;
+
+ uint32_t vmax, v;
+
+ if (!pino && ino <= 1)
+ return dd;
+
+ vmax = 0;
+
+ n = (union jffs2_node_union *) o;
+
+ do {
+ while (n < e && n->u.magic != JFFS2_MAGIC_BITMASK)
+ ((char *) n) += 4;
+
+ if (n < e && n->u.magic == JFFS2_MAGIC_BITMASK) {
+ if (n->u.nodetype == JFFS2_NODETYPE_DIRENT &&
+ (!ino || n->d.ino == ino) &&
+ (v = n->d.version) > vmax &&
+ (!pino || (n->d.pino == pino &&
+ nsize == n->d.nsize &&
+ !memcmp(name, n->d.name, nsize)))) {
+ /* XXX crc check */
+
+ if (vmax < v) {
+ vmax = v;
+ dd = &(n->d);
+ }
+ }
+
+ ((char *) n) += ((n->u.totlen + 3) & ~3);
+ } else
+ return dd;
+ } while (1);
+}
+
+/* resolve name under certain parent inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ pino - requested parent inode
+ name - name of wanted dirent
+ nsize - length of name of wanted dirent
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolvename(char *o, size_t size, uint32_t pino,
+ char *name, uint8_t nsize)
+{
+ return resolvedirent(o, size, 0, pino, name, nsize);
+}
+
+/* resolve inode to dirent */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - compare against dirent inode
+
+ return value: pointer to relevant dirent structure in
+ filesystem image or NULL
+*/
+
+struct jffs2_raw_dirent *resolveinode(char *o, size_t size, uint32_t ino)
+{
+ return resolvedirent(o, size, ino, 0, NULL, 0);
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+*/
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+ recc - recursion count, to detect symlink loops
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+*/
+
+struct jffs2_raw_dirent *resolvepath0(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos, int recc)
+{
+ struct jffs2_raw_dirent *dir = NULL;
+
+ int d = 1;
+ uint32_t tino;
+
+ char *next;
+
+ char *path, *pp;
+
+ char symbuf[1024];
+ size_t symsize;
+
+ if (recc > 16) {
+ /* probably symlink loop */
+ *inos = 0;
+ return NULL;
+ }
+
+ pp = path = strdup(p);
+
+ if (*path == '/') {
+ path++;
+ ino = 1;
+ }
+
+ if (ino > 1) {
+ dir = resolveinode(o, size, ino);
+
+ ino = DIRENT_INO(dir);
+ }
+
+ next = path - 1;
+
+ while (ino && next != NULL && next[1] != 0 && d) {
+ path = next + 1;
+ next = strchr(path, '/');
+
+ if (next != NULL)
+ *next = 0;
+
+ if (*path == '.' && path[1] == 0)
+ continue;
+ if (*path == '.' && path[1] == '.' && path[2] == 0) {
+ if (DIRENT_PINO(dir) == 1) {
+ ino = 1;
+ dir = NULL;
+ } else {
+ dir = resolveinode(o, size, DIRENT_PINO(dir));
+ ino = DIRENT_INO(dir);
+ }
+
+ continue;
+ }
+
+ dir = resolvename(o, size, ino, path, (uint8_t) strlen(path));
+
+ if (DIRENT_INO(dir) == 0 ||
+ (next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK))) {
+ free(pp);
+
+ *inos = 0;
+
+ return NULL;
+ }
+
+ if (dir->type == DT_LNK) {
+ struct jffs2_raw_inode *ri;
+ ri = find_raw_inode(o, size, DIRENT_INO(dir));
+ putblock(symbuf, sizeof(symbuf), &symsize, ri);
+ symbuf[symsize] = 0;
+
+ tino = ino;
+ ino = 0;
+
+ dir = resolvepath0(o, size, tino, symbuf, &ino, ++recc);
+
+ if (dir != NULL && next != NULL &&
+ !(dir->type == DT_DIR || dir->type == DT_LNK)) {
+ free(pp);
+
+ *inos = 0;
+ return NULL;
+ }
+ }
+ if (dir != NULL)
+ ino = DIRENT_INO(dir);
+ }
+
+ free(pp);
+
+ *inos = ino;
+
+ return dir;
+}
+
+/* resolve slash-style path into dirent and inode.
+ slash as first byte marks absolute path (root=inode 1).
+ . and .. are resolved properly, and symlinks are followed.
+*/
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ ino - root inode, used if path is relative
+ p - path to be resolved
+ inos - result inode, zero if failure
+
+ return value: pointer to dirent struct in file system image.
+ note that root directory doesn't have dirent struct
+ (return value is NULL), but it has inode (*inos=1)
+*/
+
+struct jffs2_raw_dirent *resolvepath(char *o, size_t size, uint32_t ino,
+ char *p, uint32_t * inos)
+{
+ return resolvepath0(o, size, ino, p, inos, 0);
+}
+
+/* lists files on directory specified by path */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+*/
+
+void lsdir(char *o, size_t size, char *path, int recurse)
+{
+ struct jffs2_raw_dirent *dd;
+ struct dir *d = NULL;
+
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0 ||
+ (dd == NULL && ino == 0) || (dd != NULL && dd->type != DT_DIR)) {
+ fprintf(stderr, "jffs2reader: %s: No such file or directory\n",
+ path);
+ exit(EXIT_FAILURE);
+ }
+
+ d = collectdir(o, size, ino, d);
+ printdir(o, size, d, path, recurse);
+ freedir(d);
+}
+
+/* writes file specified by path to the buffer */
+
+/*
+ o - filesystem image pointer
+ size - size of filesystem image
+ p - path to be resolved
+ b - file buffer
+ bsize - file buffer size
+ rsize - file result size
+*/
+
+void catfile(char *o, size_t size, char *path, char *b, size_t bsize,
+ size_t * rsize)
+{
+ struct jffs2_raw_dirent *dd;
+ struct jffs2_raw_inode *ri;
+ uint32_t ino;
+
+ dd = resolvepath(o, size, 1, path, &ino);
+
+ if (ino == 0) {
+ fprintf(stderr, "%s: No such file or directory\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (dd == NULL || dd->type != DT_REG) {
+ fprintf(stderr, "%s: Not a regular file\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ ri = find_raw_inode(o, size, ino);
+ putblock(b, bsize, rsize, ri);
+
+ write(1, b, *rsize);
+}
+
+/* usage example */
+
+int main(int argc, char **argv)
+{
+ int fd, opt, recurse = 0;
+ struct stat st;
+
+ char *scratch, *dir = NULL, *file = NULL;
+ size_t ssize = 0;
+
+ char *buf;
+
+ while ((opt = getopt(argc, argv, "rd:f:")) > 0) {
+ switch (opt) {
+ case 'd':
+ dir = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'r':
+ recurse++;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: jffs2reader <image> [-d|-f] < path > \n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fd = open(argv[optind], O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(2);
+ }
+
+ if (fstat(fd, &st)) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(3);
+ }
+
+ buf = malloc((size_t) st.st_size);
+ if (buf == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(4);
+ }
+
+ if (read(fd, buf, st.st_size) != (ssize_t) st.st_size) {
+ fprintf(stderr, "%s: %s\n", argv[optind], strerror(errno));
+ exit(5);
+ }
+
+ if (dir)
+ lsdir(buf, st.st_size, dir, recurse);
+
+ if (file) {
+ scratch = malloc(SCRATCH_SIZE);
+ if (scratch == NULL) {
+ fprintf(stderr, "%s: memory exhausted\n", argv[optind]);
+ exit(6);
+ }
+
+ catfile(buf, st.st_size, file, scratch, SCRATCH_SIZE, &ssize);
+ free(scratch);
+ }
+
+ if (!dir && !file)
+ lsdir(buf, st.st_size, "/", 1);
+
+
+ free(buf);
+ exit(EXIT_SUCCESS);
+}
diff --git a/jittertest/COPYING b/jittertest/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/jittertest/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/jittertest/JitterTest.c b/jittertest/JitterTest.c
new file mode 100644
index 0000000..13c64d9
--- /dev/null
+++ b/jittertest/JitterTest.c
@@ -0,0 +1,1044 @@
+/***********************************************************************
+ *
+ * Copyright: Daniel Measurement and Control, Inc.
+ * 9753 Pine Lake Drive
+ * Houston, TX 77055
+ *
+ * Created by: Vipin Malik and Gail Murray
+ * Released under GPL by permission of Daniel Industries.
+ *
+ * This software is licensed under the GPL version 2. Plese see the file
+ * COPYING for details on the license.
+ *
+ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose
+ * are made in this software. Please use at your own risk.
+ *
+ * Filename: JitterTest.c
+ *
+ * Description: Program to be used to measure wake jitter.
+ * See README file for more info.
+ *
+ *
+ * Revision History:
+ * $Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $
+ * $Log: JitterTest.c,v $
+ * Revision 1.13 2005/11/07 11:15:20 gleixner
+ * [MTD / JFFS2] Clean up trailing white spaces
+ *
+ * Revision 1.12 2001/08/10 19:23:11 vipin
+ * Ready to be released under GPL! Added proper headers etc.
+ *
+ * Revision 1.11 2001/07/09 15:35:50 vipin
+ * Couple of new features:1. The program runs by default as a "regular"
+ * (SCHED_OTHER) task by default, unless the -p n cmd line parameter is
+ * specified. It then runs as SCHED_RR at that priority.
+ * 2. Added ability to send SIGSTOP/SIGCONT to a specified PID. This
+ * would presumably be the PID of the JFFS2 GC task. SIGSTOP is sent
+ * before writing to the fs, and a SIGCONT after the write is done.
+ * 3. The "pad" data now written to the file on the "fs under test" is
+ * random, not sequential as previously.
+ *
+ * Revision 1.10 2001/06/27 19:14:24 vipin
+ * Now console file to log at can be specified from cmd line. This can enable
+ * you to run two instances of the program- one logging to the /dev/console
+ * and another to a regular file (if you want the data) or /dev/null if you don't.
+ *
+ * Revision 1.9 2001/06/25 20:21:31 vipin
+ * This is the latest version, NOT the one last checked in!
+ *
+ * Revision 1.7 2001/06/18 22:36:19 vipin
+ * Fix minor typo that excluded '\n' from msg on console.
+ *
+ * Revision 1.6 2001/06/18 21:17:50 vipin
+ * Added option to specify the amount of data written to outfile each period.
+ * The regular info is written, but is then padded to the requested size.
+ * This will enable testing of different sized writes to JFFS fs.
+ *
+ * Revision 1.5 2001/06/08 19:36:23 vipin
+ * All write() are now checked for return err code.
+ *
+ * Revision 1.4 2001/06/06 23:10:31 vipin
+ * Added README file.
+ * In JitterTest.c: Changed operation of periodic timer to one shot. The timer is now
+ * reset when the task wakes. This way every "jitter" is from the last time and
+ * jitters from previous times are not accumulated and screw aroud with our display.
+ *
+ * All jitter is now +ve. (as it should be). Also added a "read file" functionality
+ * to test for jitter in task if we have to read from JFFS fs.
+ * The program now also prints data to console- where it can be logged, interspersed with
+ * other "interesting" printk's from the kernel and drivers (flash sector erases etc.)
+ *
+ * Revision 1.3 2001/03/01 19:20:39 gmurray
+ * Add priority scheduling. Shortened name of default output file.
+ * Changed default interrupt period. Output delta time and jitter
+ * instead of time of day.
+ *
+ * Revision 1.2 2001/02/28 16:20:19 vipin
+ * Added version control Id and log fields.
+ *
+ ***********************************************************************/
+/*************************** Included Files ***************************/
+#include <stdio.h> /* fopen, printf, fprintf, fclose */
+#include <string.h> /* strcpy, strcmp */
+#include <stdlib.h> /* exit, atol, atoi */
+#include <sys/time.h> /* setitimer, settimeofday, gettimeofday */
+#include <signal.h> /* signal */
+#include <sched.h> /* sched_setscheduler, sched_get_priority_min,*/
+/* sched_get_priority_max */
+#include <unistd.h> /* gettimeofday, sleep */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+
+
+/**************************** Enumerations ****************************/
+enum timerActions
+ {
+ ONESHOT,
+ AUTORESETTING
+ };
+
+
+
+/****************************** Constants *****************************/
+/* Exit error codes */
+#define EXIT_FILE_OPEN_ERR (1) /* error opening output file*/
+#define EXIT_REG_SIGALRM_ERR (2) /* error registering SIGALRM*/
+#define EXIT_REG_SIGINT_ERR (3) /* error registering SIGINT */
+#define EXIT_INV_INT_PERIOD (4) /* error invalid int. period*/
+#define EXIT_MIN_PRIORITY_ERR (5) /* error, minimum priority */
+#define EXIT_MAX_PRIORITY_ERR (6) /* error, maximum priority */
+#define EXIT_INV_SCHED_PRIORITY (7) /* error, invalid priority */
+#define EXIT_SET_SCHEDULER_ERR (8) /* error, set scheduler */
+#define EXIT_PREV_TIME_OF_DAY_ERR (9) /* error, init. prev. */
+/* time of day */
+
+#define MAX_FILE_NAME_LEN (32) /* maximum file name length */
+
+#define STRINGS_EQUAL ((int) 0) /* strcmp value if equal */
+
+#define MIN_INT_PERIOD_MILLISEC ( 5L) /* minimum interrupt period */
+#define MAX_INT_PERIOD_MILLISEC (5000L) /* maximum interrupt period */
+#define DEF_INT_PERIOD_MILLISEC ( 10L) /* default interrupt period */
+
+#define READ_FILE_MESSAGE "This is a junk file. Must contain at least 1 byte though!\n"
+
+/* The user can specify that the program pad out the write file to
+ a given number of bytes. But a minimum number needs to be written. This
+ will contain the jitter info.
+*/
+#define MIN_WRITE_BYTES 30
+#define DEFAULT_WRITE_BYTES 30
+#define MAX_WRITE_BYTES 4096
+
+/* used for gen a printable ascii random # between spc and '~' */
+#define MIN_ASCII 32 /* <SPACE> can be char*/
+#define MAX_ASCII 126.0 /* needs to be float. See man rand() */
+
+/*----------------------------------------------------------------------
+ * It appears that the preprocessor can't handle multi-line #if
+ * statements. Thus, the check on the default is divided into two
+ * #if statements.
+ *---------------------------------------------------------------------*/
+#if (DEF_INT_PERIOD_MILLISEC < MIN_INT_PERIOD_MILLISEC)
+#error *** Invalid default interrupt period. ***
+#endif
+
+#if (DEF_INT_PERIOD_MILLISEC > MAX_INT_PERIOD_MILLISEC)
+#error *** Invalid default interrupt period. ***
+#endif
+
+
+#define TRUE 1 /* Boolean true value */
+#define FALSE 0
+
+/* Time conversion constants. */
+#define MILLISEC_PER_SEC (1000L) /* milliseconds per second */
+#define MICROSEC_PER_MILLISEC (1000L) /* microsecs per millisec */
+#define MICROSEC_PER_SEC (1000000L) /* microsecs per second */
+
+#define PRIORITY_POLICY ((int) SCHED_RR) /* If specified iwth "-p" */
+
+
+
+/************************** Module Variables **************************/
+/* version identifier (value supplied by CVS)*/
+static const char Version[] = "$Id: JitterTest.c,v 1.13 2005/11/07 11:15:20 gleixner Exp $";
+
+static char OutFileName[MAX_FILE_NAME_LEN+1]; /* output file name */
+static char LogFile[MAX_FILE_NAME_LEN+1] = "/dev/console"; /* default */
+static char ReadFile[MAX_FILE_NAME_LEN+1]; /* This file is read. Should
+ contain at least 1 byte */
+
+static int WriteBytes = DEFAULT_WRITE_BYTES; /* pad out file to these many bytes. */
+static int Fd1; /* fd where the above buffer if o/p */
+static int Cfd; /* fd to console (or file specified) */
+static int Fd2; /* fd for the ReadFile */
+static int DoRead = FALSE; /* should we attempt to ReadFile?*/
+static long InterruptPeriodMilliSec; /* interrupt period, millisec */
+static int MinPriority; /* minimum scheduler priority */
+static int MaxPriority; /* maximum scheduler priority */
+static int RequestedPriority; /* requested priority */
+static struct itimerval ITimer; /* interrupt timer structure */
+static struct timeval PrevTimeVal; /* previous time structure */
+static struct timeval CurrTimeVal; /* current time structure */
+static long LastMaxDiff = 0; /* keeps track of worst jitter encountered */
+
+static int GrabKProfile = FALSE; /* To help determine system bottle necks
+ this parameter can be set. This causes
+ the /proc/profile file to be read and
+ stored in unique filenames in current
+ dir, and indication to be o/p on the
+ /dev/console also.
+ */
+static long ProfileTriggerMSecs = 15000l; /* Jitter time in seconds that triggers
+ a snapshot of the profile to be taken
+
+ */
+static int SignalGCTask = FALSE; /* should be signal SIGSTOP/SIGCONT to gc task?*/
+static int GCTaskPID;
+
+static int RunAsRTTask = FALSE; /* default action unless priority is
+ specified on cmd line */
+
+
+/********************* Local Function Prototypes **********************/
+void HandleCmdLineArgs(int argc, char *argv[]);
+void SetFileName(char * pFileName);
+void SetInterruptPeriod(char * pASCIIInterruptPeriodMilliSec);
+void SetSchedulerPriority(char * pASCIISchedulerPriority);
+
+void PrintVersionInfo(void);
+void PrintHelpInfo(void);
+
+int Write(int fd, void *buf, size_t bytes, int lineNo);
+
+void InitITimer(struct itimerval * pITimer, int action);
+
+/* For catching timer interrupts (SIGALRM). */
+void AlarmHandler(int sigNum);
+
+/* For catching Ctrl+C SIGINT. */
+void SignalHandler(int sigNum);
+
+
+
+/***********************************************************************
+ * main function
+ * return: exit code
+ ***********************************************************************/
+int main(
+ int argc,
+ char *argv[])
+{
+ struct sched_param schedParam;
+
+ int mypri;
+ char tmpBuf[200];
+
+
+ strcpy(OutFileName,"jitter.dat");
+ InterruptPeriodMilliSec = MIN_INT_PERIOD_MILLISEC;
+
+ /* Get the minimum and maximum priorities. */
+ MinPriority = sched_get_priority_min(PRIORITY_POLICY);
+ MaxPriority = sched_get_priority_max(PRIORITY_POLICY);
+ if (MinPriority == -1) {
+ printf("\n*** Unable to get minimum priority. ***\n");
+ exit(EXIT_MIN_PRIORITY_ERR);
+ }
+ if (MaxPriority == -1) {
+ printf("\n*** Unable to get maximum priority. ***\n");
+ exit(EXIT_MAX_PRIORITY_ERR);
+ }
+
+ /* Set requested priority to minimum value as default. */
+ RequestedPriority = MinPriority;
+
+ HandleCmdLineArgs(argc, argv);
+
+ if(mlockall(MCL_CURRENT|MCL_FUTURE) < 0){
+ printf("Could not lock task into memory. Bye\n");
+ perror("Error");
+ }
+
+ if(RunAsRTTask){
+
+ /* Set the priority. */
+ schedParam.sched_priority = RequestedPriority;
+ if (sched_setscheduler(
+ 0,
+ PRIORITY_POLICY,
+ &schedParam) != (int) 0) {
+ printf("Exiting: Unsuccessful sched_setscheduler.\n");
+ close(Fd1);
+ exit(EXIT_SET_SCHEDULER_ERR);
+ }
+
+
+ /* Double check as I have some doubts that it's really
+ running at realtime priority! */
+ if((mypri = sched_getscheduler(0)) != RequestedPriority)
+ {
+ printf("Not running with request priority %i. running with priority %i instead!\n",
+ RequestedPriority, mypri);
+ }else
+ {
+ printf("Running with %i priority. Good!\n", mypri);
+ }
+
+ }
+
+ /*------------------------- Initializations --------------------------*/
+ if((Fd1 = open(OutFileName, O_RDWR|O_CREAT|O_SYNC)) <= 0)
+ {
+ perror("Cannot open outfile for write:");
+ exit(1);
+ }
+
+ /* If a request is made to read from a specified file, then create that
+ file and fill with junk data so that there is something there to read.
+ */
+ if(DoRead)
+ {
+
+ if((Fd2 = open(ReadFile, O_RDWR|O_CREAT|O_SYNC|O_TRUNC)) <= 0)
+ {
+ perror("cannot open read file:");
+ exit(1);
+ }else
+ {
+
+ /* Don't really care how much we write here */
+ if(write(Fd2, READ_FILE_MESSAGE, strlen(READ_FILE_MESSAGE)) < 0)
+ {
+ perror("Problems writing to readfile:");
+ exit(1);
+ }
+ lseek(Fd2, 0, SEEK_SET); /* position back to byte #0 */
+ }
+ }
+
+
+
+ /* Also log output to console. This way we can capture it
+ on a serial console to a log file.
+ */
+ if((Cfd = open(LogFile, O_WRONLY|O_SYNC)) <= 0)
+ {
+ perror("cannot open o/p logfile:");
+ exit(1);
+ }
+
+
+ /* Set-up handler for periodic interrupt. */
+ if (signal(SIGALRM, &AlarmHandler) == SIG_ERR) {
+ printf("Couldn't register signal handler for SIGALRM.\n");
+ sprintf(tmpBuf,
+ "Couldn't register signal handler for SIGALRM.\n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ exit(EXIT_REG_SIGALRM_ERR);
+ }
+
+ /* Set-up handler for Ctrl+C to exit the program. */
+ if (signal(SIGINT, &SignalHandler) == SIG_ERR) {
+ printf("Couldn't register signal handler for SIGINT.\n");
+ sprintf(tmpBuf,
+ "Couldn't register signal handler for SIGINT.\n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ exit(EXIT_REG_SIGINT_ERR);
+ }
+
+ printf("Press Ctrl+C to exit the program.\n");
+ printf("Output File: %s\n", OutFileName);
+ printf("Scheduler priority: %d\n", RequestedPriority);
+ sprintf(tmpBuf, "\nScheduler priority: %d\n",
+ RequestedPriority);
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ printf("Interrupt period: %ld milliseconds\n",
+ InterruptPeriodMilliSec);
+ sprintf(tmpBuf, "Interrupt period: %ld milliseconds\n",
+ InterruptPeriodMilliSec);
+
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ fflush(0);
+
+
+
+ /* Initialize the periodic timer. */
+ InitITimer(&ITimer, ONESHOT);
+
+ /* Initialize "previous" time. */
+ if (gettimeofday(&PrevTimeVal, NULL) != (int) 0) {
+ printf("Exiting - ");
+ printf("Unable to initialize previous time of day.\n");
+ sprintf(tmpBuf, "Exiting - ");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ sprintf(tmpBuf,
+ "Unable to initialize previous time of day.\n");
+
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ }
+
+ /* Start the periodic timer. */
+ setitimer(ITIMER_REAL, &ITimer, NULL);
+
+
+ while(TRUE) { /* Intentional infinite loop. */
+ /* Sleep for one second. */
+ sleep((unsigned int) 1);
+ }
+
+ /* Just in case. File should be closed in SignalHandler. */
+ close(Fd1);
+ close(Cfd);
+
+ return 0;
+}
+
+
+
+
+/***********************************************************************
+ * SignalHandler
+ * This is a handler for the SIGINT signal. It is assumed that the
+ * SIGINT is due to the user pressing Ctrl+C to halt the program.
+ * output: N/A
+ ***********************************************************************/
+void SignalHandler(
+ int sigNum)
+{
+
+ char tmpBuf[200];
+
+ /* Note sigNum not used. */
+ printf("Ctrl+C detected. Worst Jitter time was:%fms.\nJitterTest exiting.\n",
+ (float)LastMaxDiff/1000.0);
+
+ sprintf(tmpBuf,
+ "\nCtrl+C detected. Worst Jitter time was:%fms\nJitterTest exiting.\n",
+ (float)LastMaxDiff/1000.0);
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ close(Fd1);
+ close(Cfd);
+ exit(0);
+}
+
+
+
+
+
+/*
+ A snapshot of the /proc/profile needs to be taken.
+ This is stored as a new file every time, and the
+ stats reset by doing a (any) write to the /proc/profile
+ file.
+ */
+void doGrabKProfile(int jitterusec, char *fileName)
+{
+ int fdSnapshot;
+ int fdProfile;
+ int readBytes;
+ char readBuf[4096];
+
+ if((fdSnapshot = open(fileName, O_WRONLY | O_CREAT)) <= 0)
+ {
+ fprintf(stderr, "Could not open file %s.\n", fileName);
+ perror("Error:");
+ return;
+ }
+
+ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0)
+ {
+ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n");
+ close(fdSnapshot);
+ return;
+ }
+
+ while((readBytes = read(fdProfile, readBuf, sizeof(readBuf))) > 0)
+ {
+ write(fdSnapshot, readBuf, readBytes);
+ }
+
+ close(fdSnapshot);
+
+ if(write(fdProfile, readBuf, 1) != 1)
+ {
+ perror("Could Not clear profile by writing to /proc/profile:");
+ }
+
+ close(fdProfile);
+
+
+
+}/* end doGrabKProfile()*/
+
+
+/*
+ Call this routine to clear the kernel profiling buffer /proc/profile
+*/
+void clearProfileBuf(void){
+
+
+ int fdProfile;
+ char readBuf[10];
+
+
+ if((fdProfile = open("/proc/profile", O_RDWR)) <= 0)
+ {
+ fprintf(stderr, "Could not open file /proc/profile. Make sure you booted with profile=2\n");
+ return;
+ }
+
+
+ if(write(fdProfile, readBuf, 1) != 1)
+ {
+ perror("Could Not clear profile by writing to /proc/profile:");
+ }
+
+ close(fdProfile);
+
+
+}/* end clearProfileBuf() */
+
+
+
+
+
+/***********************************************************************
+ * AlarmHandler
+ * This is a handler for the SIGALRM signal (due to the periodic
+ * timer interrupt). It prints the time (seconds) to
+ * the output file.
+ * output: N/A
+ ***********************************************************************/
+void AlarmHandler(
+ int sigNum) /* signal number (not used) */
+{
+
+ long timeDiffusec; /* diff time in micro seconds */
+ long intervalusec;
+
+
+ char tmpBuf[MAX_WRITE_BYTES];
+ int cntr;
+ char padChar;
+
+ static int profileFileNo = 0;
+ char profileFileName[150];
+
+ static int seedStarter = 0; /* carries over rand info to next time
+ where time() will be the same as this time
+ if invoked < 1sec apart.
+ */
+
+ if (gettimeofday(&CurrTimeVal, NULL) == (int) 0) {
+
+ /*----------------------------------------------------------------
+ * Compute the elapsed time between the current and previous
+ * time of day values and store the result.
+ *
+ * Print the elapsed time to the output file.
+ *---------------------------------------------------------------*/
+
+ timeDiffusec = (long)(((((long long)CurrTimeVal.tv_sec) * 1000000L) + CurrTimeVal.tv_usec) -
+ (((long long)PrevTimeVal.tv_sec * 1000000L) + PrevTimeVal.tv_usec));
+
+ sprintf(tmpBuf," %f ms ", (float)timeDiffusec/1000.0);
+
+ intervalusec = InterruptPeriodMilliSec * 1000L;
+
+ timeDiffusec = timeDiffusec - intervalusec;
+
+ sprintf(&tmpBuf[strlen(tmpBuf)]," %f ms", (float)timeDiffusec/1000.0);
+
+
+ /* should we send a SIGSTOP/SIGCONT to the specified PID? */
+ if(SignalGCTask){
+
+ if(kill(GCTaskPID, SIGSTOP) < 0){
+
+ perror("error:");
+ }
+ }
+
+
+ /* Store some historical #'s */
+ if(abs(timeDiffusec) > LastMaxDiff)
+ {
+ LastMaxDiff = abs(timeDiffusec);
+ sprintf(&tmpBuf[strlen(tmpBuf)],"!");
+
+ if((GrabKProfile == TRUE) && (ProfileTriggerMSecs < (abs(timeDiffusec)/1000)))
+ {
+ sprintf(profileFileName, "JitterTest.profilesnap-%i", profileFileNo);
+
+ /* go and grab the kernel performance profile. */
+ doGrabKProfile(timeDiffusec, profileFileName);
+ profileFileNo++; /* unique name next time */
+
+ /* Say so on the console so that a marker gets put in the console log */
+ sprintf(&tmpBuf[strlen(tmpBuf)],"<Profile saved in file:%s>",
+ profileFileName);
+
+ }
+
+ }
+
+
+
+
+ sprintf(&tmpBuf[strlen(tmpBuf)],"\n"); /* CR for the data going out of the console */
+
+ Write(Cfd, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ /* The "-1" below takes out the '\n' at the end that we appended for the msg printed on
+ the console.*/
+ sprintf(&tmpBuf[strlen(tmpBuf)-1]," PadBytes:");
+
+ /* Now pad the output file if requested by user. */
+ if(WriteBytes > MIN_WRITE_BYTES)
+ {
+
+ /* start from a new place every time */
+ srand(time(NULL) + seedStarter);
+
+ /* already written MIN_WRITE_BYTES by now */
+ for(cntr = strlen(tmpBuf); cntr < WriteBytes - 1 ; cntr++) /* "-1" adj for '\n' at end */
+ {
+ /* don't accept any # < 'SPACE' */
+ padChar = (char)(MIN_ASCII+(int)((MAX_ASCII-(float)MIN_ASCII)*rand()/(RAND_MAX+1.0)));
+
+
+ /*
+ padChar = (cntr % (126-33)) + 33;
+ */
+
+ tmpBuf[cntr] = padChar;
+ }
+
+ seedStarter = tmpBuf[cntr-1]; /* save for next time */
+
+ tmpBuf[cntr] = '\n'; /* CR for the data going into the outfile. */
+ tmpBuf[cntr+1] = '\0'; /* NULL terminate the string */
+ }
+
+ /* write out the entire line to the output file. */
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+
+ /* Read a byte from the specified file */
+ if(DoRead)
+ {
+
+ read(Fd2, tmpBuf, 1);
+ lseek(Fd2, 0, SEEK_SET); /* back to start */
+ }
+
+
+ /* Start the periodic timer again. */
+ setitimer(ITIMER_REAL, &ITimer, NULL);
+
+
+ /* Update previous time with current time. */
+ PrevTimeVal.tv_sec = CurrTimeVal.tv_sec;
+ PrevTimeVal.tv_usec = CurrTimeVal.tv_usec;
+ }
+
+ else {
+ sprintf(tmpBuf, "gettimeofday error \n");
+ Write(Fd1, tmpBuf, strlen(tmpBuf), __LINE__);
+
+ printf("gettimeofday error \n");
+ }
+
+ /* now clear the profiling buffer */
+ if(GrabKProfile == TRUE){
+
+ clearProfileBuf();
+ }
+
+ /* should we send a SIGSTOP/SIGCONT to the specified PID? */
+ if(SignalGCTask){
+
+ if(kill(GCTaskPID, SIGCONT) < 0){
+
+ perror("error:");
+ }
+ }
+
+
+ return;
+}
+
+
+
+/***********************************************************************
+ * InitITimer
+ * This function initializes the 'struct itimerval' structure whose
+ * address is passed to interrupt every InterruptPeriodMilliSec.
+ * output: N/A
+ ***********************************************************************/
+void InitITimer(
+ struct itimerval * pITimer, /* pointer to interrupt timer struct*/
+ int action) /* ONESHOT or autosetting? */
+{
+ long seconds; /* seconds portion of int. period */
+ long microSeconds; /* microsec. portion of int. period */
+
+ /*--------------------------------------------------------------------
+ * Divide the desired interrupt period into its seconds and
+ * microseconds components.
+ *-------------------------------------------------------------------*/
+ if (InterruptPeriodMilliSec < MILLISEC_PER_SEC) {
+ seconds = 0L;
+ microSeconds = InterruptPeriodMilliSec * MICROSEC_PER_MILLISEC;
+ }
+ else {
+ seconds = InterruptPeriodMilliSec / MILLISEC_PER_SEC;
+ microSeconds =
+ (InterruptPeriodMilliSec - (seconds * MILLISEC_PER_SEC)) *
+ MICROSEC_PER_MILLISEC;
+ }
+
+ /* Initialize the interrupt period structure. */
+ pITimer->it_value.tv_sec = seconds;
+ pITimer->it_value.tv_usec = microSeconds;
+
+ if(action == ONESHOT)
+ {
+ /* This will (should) prevent the timer from restarting itself */
+ pITimer->it_interval.tv_sec = 0;
+ pITimer->it_interval.tv_usec = 0;
+ }else
+ {
+ pITimer->it_interval.tv_sec = seconds;
+ pITimer->it_interval.tv_usec = microSeconds;
+
+ }
+
+ return;
+}
+
+
+/***********************************************************************
+ * HandleCmdLineArgs
+ * This function handles the command line arguments.
+ * output: stack size
+ ***********************************************************************/
+void HandleCmdLineArgs(
+ int argc, /* number of command-line arguments */
+ char *argv[]) /* ptrs to command-line arguments */
+{
+ int argNum; /* argument number */
+
+ if (argc > (int) 1) {
+
+ for (argNum = (int) 1; argNum < argc; argNum++) {
+
+ /* The command line contains an argument. */
+
+ if ((strcmp(argv[argNum],"--version") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-v") == STRINGS_EQUAL)) {
+ /* Print version information and exit. */
+ PrintVersionInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--help") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-h") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-?") == STRINGS_EQUAL)) {
+ /* Print help information and exit. */
+ PrintHelpInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--file") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-f") == STRINGS_EQUAL)) {
+ /* Set the name of the output file. */
+ ++argNum;
+ if (argNum < argc) {
+ SetFileName(argv[argNum]);
+ }
+ else {
+ printf("*** Output file name not specified. ***\n");
+ printf("Default output file name will be used.\n");
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--time") == STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-t") == STRINGS_EQUAL)) {
+ /* Set the interrupt period. */
+ ++argNum;
+ if (argNum < argc) {
+ SetInterruptPeriod(argv[argNum]);
+ }
+ else {
+ printf("*** Interrupt period not specified. ***\n");
+ printf("Default interrupt period will be used.\n");
+ }
+
+ }
+
+ else if ((strcmp(argv[argNum],"--priority") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-p") == STRINGS_EQUAL)) {
+ /* Set the scheduler priority. */
+ ++argNum;
+ if (argNum < argc) {
+ SetSchedulerPriority(argv[argNum]);
+ }
+ else {
+ printf("*** Scheduler priority not specified. ***\n");
+ printf("Default scheduler priority will be used.\n");
+ }
+
+ }
+
+ else if ((strcmp(argv[argNum],"--readfile") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-r") == STRINGS_EQUAL)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ strncpy(ReadFile, argv[argNum], sizeof(ReadFile));
+ DoRead = TRUE;
+ }
+
+ else if ((strcmp(argv[argNum],"--write_bytes") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-w") == STRINGS_EQUAL)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ WriteBytes = atoi(argv[argNum]);
+
+ if(WriteBytes < MIN_WRITE_BYTES)
+ {
+ printf("Writing less than %i bytes is not allowed. Bye.\n",
+ MIN_WRITE_BYTES);
+ exit(0);
+ }
+
+
+ }
+
+ else if ((strcmp(argv[argNum],"--consolefile") ==
+ STRINGS_EQUAL) ||
+ (strcmp(argv[argNum],"-c") == STRINGS_EQUAL)) {
+ /* Set the file to log console log on. */
+ ++argNum;
+
+ strncpy(LogFile, argv[argNum], sizeof(LogFile));
+ }
+
+ else if ((strcmp(argv[argNum],"--grab_kprofile") ==
+ STRINGS_EQUAL))
+ {
+ /* We will read the /proc/profile file on configurable timeout */
+ GrabKProfile = TRUE;
+
+ ++argNum;
+
+ /* If the jittter is > this #, then the profile is grabbed. */
+ ProfileTriggerMSecs = (long) atoi(argv[argNum]);
+
+ if(ProfileTriggerMSecs <= 0){
+
+ printf("Illegal value for profile trigger threshold.\n");
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--siggc") ==
+ STRINGS_EQUAL))
+ {
+ /* We will SIGSTOP/SIGCONT the specified pid */
+ SignalGCTask = TRUE;
+
+ ++argNum;
+
+ GCTaskPID = atoi(argv[argNum]);
+
+ if(ProfileTriggerMSecs <= 0){
+
+ printf("Illegal value for JFFS(2) GC task pid.\n");
+ exit(0);
+ }
+ }
+
+
+ else {
+ /* Unknown argument. Print help information and exit. */
+ printf("Invalid option %s\n", argv[argNum]);
+ printf("Try 'JitterTest --help' for more information.\n");
+ exit(0);
+ }
+ }
+ }
+
+ return;
+}
+
+
+/***********************************************************************
+ * SetFileName
+ * This function sets the output file name.
+ * output: N/A
+ ***********************************************************************/
+void SetFileName(
+ char * pFileName) /* ptr to desired output file name */
+{
+ size_t fileNameLen; /* file name length (bytes) */
+
+ /* Check file name length. */
+ fileNameLen = strlen(pFileName);
+ if (fileNameLen > (size_t) MAX_FILE_NAME_LEN) {
+ printf("File name %s exceeds maximum length %d.\n",
+ pFileName, MAX_FILE_NAME_LEN);
+ exit(0);
+ }
+
+ /* File name length is OK so save the file name. */
+ strcpy(OutFileName, pFileName);
+
+ return;
+}
+
+
+/***********************************************************************
+ * SetInterruptPeriod
+ * This function sets the interrupt period.
+ * output: N/A
+ ***********************************************************************/
+void SetInterruptPeriod(
+ char * /* ptr to desired interrupt period */
+ pASCIIInterruptPeriodMilliSec)
+{
+ long period; /* interrupt period */
+
+ period = atol(pASCIIInterruptPeriodMilliSec);
+ if ((period < MIN_INT_PERIOD_MILLISEC) ||
+ (period > MAX_INT_PERIOD_MILLISEC)) {
+ printf("Invalid interrupt period: %ld ms.\n", period);
+ exit(EXIT_INV_INT_PERIOD);
+ }
+ else {
+ InterruptPeriodMilliSec = period;
+ }
+ return;
+}
+
+
+/***********************************************************************
+ * SetSchedulerPriority
+ * This function sets the desired scheduler priority.
+ * output: N/A
+ ***********************************************************************/
+void SetSchedulerPriority(
+ char * pASCIISchedulerPriority) /* ptr to desired scheduler priority*/
+{
+ int priority; /* desired scheduler priority value */
+
+ priority = atoi(pASCIISchedulerPriority);
+ if ((priority < MinPriority) ||
+ (priority > MaxPriority)) {
+ printf("Scheduler priority %d outside of range [%d, %d]\n",
+ priority, MinPriority, MaxPriority);
+ exit(EXIT_INV_SCHED_PRIORITY);
+ }
+ else {
+ RequestedPriority = priority;
+ RunAsRTTask = TRUE; /* We shall run as a POSIX real time task */
+ }
+ return;
+}
+
+
+/***********************************************************************
+ * PrintVersionInfo
+ * This function prints version information.
+ * output: N/A
+ ***********************************************************************/
+void PrintVersionInfo(void)
+{
+ printf("JitterTest version %s\n", Version);
+ printf("Copyright (c) 2001, Daniel Industries, Inc.\n");
+ return;
+}
+
+
+/***********************************************************************
+ * PrintHelpInfo
+ * This function prints help information.
+ * output: N/A
+ ***********************************************************************/
+void PrintHelpInfo(void)
+{
+ printf("Usage: JitterTest [options]\n");
+ printf(" *** Requires root privileges. ***\n");
+ printf("Option:\n");
+ printf(" [-h, --help, -?] Print this message and exit.\n");
+ printf(" [-v, --version] ");
+ printf( "Print the version number of JitterTest and exit.\n");
+ printf(" [-f FILE, --file FILE] Set output file name to FILE. Typically you would put this on the fs under test\n");
+ printf(" [-c FILE, --consolefile] Set device or file to write the console log to.\n\tTypically you would set this to /dev/console and save it on another computer.\n");
+ printf(" [-w BYTES, --write_bytes BYTES Write BYTES to FILE each period.\n");
+ printf(" [-r FILE, --readfile FILE] Also read 1 byte every cycle from FILE. FILE will be created and filled with data.\n");
+ printf(" [-t <n>, --time <n>] ");
+ printf( "Set interrupt period to <n> milliseconds.\n");
+ printf(" ");
+ printf( "Range: [%ld, %ld] milliseconds.\n",
+ MIN_INT_PERIOD_MILLISEC, MAX_INT_PERIOD_MILLISEC);
+ printf(" [-p <n>, --priority <n>] ");
+ printf( "Set scheduler priority to <n>.\n");
+ printf(" ");
+ printf( "Range: [%d, %d] (higher number = higher priority)\n",
+ MinPriority, MaxPriority);
+ printf(" [--grab_kprofile <THRESHOLD in ms>] Read the /proc/profile if jitter is > THRESHOLD and store in file.\n");
+ printf(" [--siggc <PID>] Before writing to fs send SIGSTOP to PID. After write send SIGCONT\n");
+ return;
+
+}
+
+
+/* A common write that checks for write errors and exits. Pass it __LINE__ for lineNo */
+int Write(int fd, void *buf, size_t bytes, int lineNo)
+{
+
+ int err;
+
+ err = write(fd, buf, bytes);
+
+ if(err < bytes)
+ {
+
+ printf("Write Error at line %i! Wanted to write %i bytes, but wrote only %i bytes.\n",
+ lineNo, bytes, err);
+ perror("Write did not complete. Error. Bye:"); /* show error from errno. */
+ exit(1);
+
+ }
+
+ return err;
+
+}/* end Write*/
diff --git a/jittertest/Makefile b/jittertest/Makefile
new file mode 100644
index 0000000..2f11329
--- /dev/null
+++ b/jittertest/Makefile
@@ -0,0 +1,46 @@
+CC=gcc
+# uncomment following for performance
+CCFLAGS=-O3 -Wall -m486 -fomit-frame-pointer
+
+# uncomment following for debugging. Uncomment either this or the one above. Not both.
+# CCFLAGS=-Wall -g
+
+
+all: JitterTest plotJittervsFill
+
+JitterTest: JitterTest.c Makefile
+ gcc $(CCFLAGS) -lm JitterTest.c -o JitterTest
+
+plotJittervsFill: plotJittervsFill.c Makefile
+ gcc $(CCFLAGS) plotJittervsFill.c -o plotJittervsFill
+
+clean:
+ rm -rf *~
+ rm -rf core
+ rm -rf *.o
+ rm -rf JitterTest
+
+
+dep:
+ makedepend -I./ *.c
+# DO NOT DELETE
+
+JitterTest.o: /usr/include/stdio.h /usr/include/features.h
+JitterTest.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h
+JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h
+JitterTest.o: /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h
+JitterTest.o: /usr/include/bits/types.h /usr/include/libio.h
+JitterTest.o: /usr/include/_G_config.h /usr/include/bits/stdio_lim.h
+JitterTest.o: /usr/include/string.h /usr/include/stdlib.h
+JitterTest.o: /usr/include/sys/types.h /usr/include/time.h
+JitterTest.o: /usr/include/endian.h /usr/include/bits/endian.h
+JitterTest.o: /usr/include/sys/select.h /usr/include/bits/select.h
+JitterTest.o: /usr/include/bits/sigset.h /usr/include/sys/sysmacros.h
+JitterTest.o: /usr/include/alloca.h /usr/include/sys/time.h
+JitterTest.o: /usr/include/bits/time.h /usr/include/signal.h
+JitterTest.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h
+JitterTest.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+JitterTest.o: /usr/include/asm/sigcontext.h /usr/include/bits/sigstack.h
+JitterTest.o: /usr/include/sched.h /usr/include/bits/sched.h
+JitterTest.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h
+JitterTest.o: /usr/include/bits/confname.h /usr/include/getopt.h
diff --git a/jittertest/README b/jittertest/README
new file mode 100644
index 0000000..f411a2c
--- /dev/null
+++ b/jittertest/README
@@ -0,0 +1,197 @@
+$Id: README,v 1.2 2001/08/10 19:23:11 vipin Exp $
+
+This is the README file for the JitterTest (and friends)
+program.
+
+This program is used to measure what the jitter of a
+real time task would be under "standard" Linux.
+
+More particularly, what is the effect of running
+a real time task under Linux with background
+JFFS file system activity.
+
+The jitter is measured in milli seconds (ms) from
+the expected time of arrival of a signal from a
+periodic timer (set by the task) to when the
+task actually gets the signal.
+
+This jitter is then stored in a file specified
+(or the default output file "jitter.dat").
+
+The data may also be sent out to the console by
+writing to /dev/console (See help options. This is
+highly desirable specially if you have redirected
+your console to the serial port and are storing it
+as a minicom log on another computer for later analysis
+using some tools provided here).
+
+This is particularly useful if you have a serial
+console and are outputting "interesting" info
+from inside some kernel task or driver.
+(or something as simple as a "df" program running
+periodically and redirecting o/p to the console).
+
+One "interesting" thing that I have measured
+is the effect of FLASH chip erases on the jitter
+of a real time task.
+
+One can do that by putting a printk at the
+beginning of the flash erase routine in the MTD
+flash chip driver.
+
+Now you will get jitter data interspersed with
+flash sector erase events. Other printk's can also
+be placed at suspected jitter causing locations in
+the system.
+
+
+
+EXECUTING THE PROGRAM "JitterTest"
+
+You may specify a file to be read by the
+program every time it wakes up (every cycle).
+This file is created and filled with some junk
+data. The purpose of this is to test the jitter
+of the program if it were reading from- say
+a JFFS (Journaling Flash File System) file system.
+
+By specifying the complete paths of the read and write
+(o/p) files you can test the jitter a POSIX type
+real time task will experience under Linux, under
+various conditions.
+
+These can be as follows:
+
+1. O/P file on ram file system, no i/p file.
+
+ In this case you would presumably generate other
+"typical" background activity for your system and
+examine the worst case jitter experienced by
+a task that is neither reading nor writing to
+a file system.
+
+Other cases could be:
+
+2. O/P to ram fs, I/P from JFFS (type) fs:
+
+ This is specially useful to test the proper
+operation of erase suspend type of operation
+in JFFS file systems (with an MTD layer that
+supports it).
+
+ In this test you would generate some background
+write/erase type activity that would generate
+chip erases. Since this program is reading from
+the same file system, you contrast the latencies
+with those obtained with writes going to the same
+fs.
+
+3. Both read and writes to (or just write to) JFFS
+file system:
+
+ Here you would test for latencies experienced by
+a program if it were writing (and optionally also
+reading) from a JFFS fs.
+
+
+
+
+Grabing a kernel profile:
+
+This program can also conditionally grab a kernel profile.
+Specify --grab_kprofile on the cmd line as well as
+a "threshold" parameter (see help options by -?).
+
+Any jitter greater than this "threshold" will cause the
+program to read the /proc/profile file and dump it in
+a local file with increasing file numbers. It will also
+output the filename at that time to the console file specified.
+This will allow you to corelate later in time the various profiles
+with data in your console file and what was going on at that time.
+
+These profile files may then be later examined by running them through
+ksymoops.
+
+Make sure you specify "profile=2" on the kernel command line
+when you boot the kernel if you want to use this functionality.
+
+
+
+Signalling the JFFS[2] GC task:
+
+You can also force this program to send a SIGSTOP/SIGCONT to the
+JFFS (or JFFS2) gc task by specifing --siggc <pid> on the cmd line.
+
+This will let you investigate the effect of forcing the gc task to
+wake up and do its thing when you are not writing to the fs and to
+force it to sleep when you want to write to the fs.
+
+These are just various tools to investigate the possibility of
+achieving minimal read/write latency when using JFFS[2].
+
+You need to manually do a "ps aux" and look up the PID of the gc
+thread and provide it to the program.
+
+
+
+
+EXECUTING THE PROGRAM "plotJittervsFill"
+
+This program is a post processing tool that will extract the jitter
+times as printed by the JitterTest program in its console log file
+as well as the data printed by the "df" command.
+
+This "df" data happens to be in the console log because you will
+run the shell file fillJffs2.sh on a console when you are doing
+your jitter test.
+
+This shell script copies a specified file to another specified file
+every programmable seconds. It also does a "df" and redirects output
+to /dev/console where it is mixed with the output from JitterTest.
+
+All this console data is stored on another computer, as all this data
+is being outputted to the serial port as you have redirected the console
+to the serial port (that is the only guaranteed way to not loose any
+console log or printk data).
+
+You can then run this saved console log through this program and it
+will output a very nice text file with the %fill in one col and
+corrosponding jitter values in the second. gnuplot then does a
+beautifull plot of this resulting file showing you graphically the
+jitters encountered at different fill % of your JFFS[2] fs.
+
+
+
+OTHER COMMENTS:
+
+Use the "-w BYTES" cmd line parameter to simulate your test case.
+Not everyone has the same requirements. Someone may want to measure
+the jitter of JFFS2 with 500 bytes being written every 500ms. Others
+may want to measure the system performance writing 2048 bytes every
+5 seconds.
+
+RUNNING MULTIPLE INSTANCES:
+
+Things get real interesting when you run multiple instances of this
+program *at the same time*.
+
+You could have one version running as a real time task (by specifing
+the priority with the -p cmd line parameter), not interacting with
+any fs or at the very least not reading and writing to JFFS[2].
+
+At the same time you could have another version running as a regular
+task (by not specifing any priority) but reading and writing to JFFS[2].
+
+This way you can easily measure the blocking performance of the real time
+task while another non-real time task interacts with JFFS[2] in the back ground.
+
+You get the idea.
+
+
+WATCH OUT!
+
+Be particularly careful of running this program as a real time task AND
+writing to JFFS[2]. Any blocks will cause your whole system to block till
+any garbage collect initiated by writes by this task complete. I have measured
+these blocks to be of the order of 40-50 seconds on a reasonably powerful
+32 bit embedded system.
diff --git a/jittertest/filljffs2.sh b/jittertest/filljffs2.sh
new file mode 100644
index 0000000..10651f4
--- /dev/null
+++ b/jittertest/filljffs2.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# Pass following cmd line:
+# 1st - file to copy
+# 2nd - file to copy to
+# 3rd - time to sleep between copies
+
+while [ $(( 1 )) -gt $(( 0 )) ]
+do
+ cp $1 $2
+ rm $2
+ df |grep mtd > /dev/console
+ echo "sleeping $3"
+ sleep $3
+done
+
diff --git a/jittertest/plotJittervsFill.c b/jittertest/plotJittervsFill.c
new file mode 100644
index 0000000..f8a5660
--- /dev/null
+++ b/jittertest/plotJittervsFill.c
@@ -0,0 +1,312 @@
+/*
+ ***********************************************************************
+ *
+ * Copyright: Daniel Measurement and Control, Inc.
+ * 9753 Pine Lake Drive
+ * Houston, TX 77055
+ *
+ * Created by: Vipin Malik
+ * Released under GPL by permission of Daniel Industries.
+ *
+ * This software is licensed under the GPL version 2. Plese see the file
+ * COPYING for details on the license.
+ *
+ * NO WARRANTY: Absolutely no claims of warranty or fitness of purpose
+ * are made in this software. Please use at your own risk.
+ *
+ File: plotJittervsFill.c
+ By: Vipin Malik
+
+ About: This program reads in a jitter log file as created
+ by the JitterTest.c program and extracts all the jitters
+ in the file that are greater than a threshold specified
+ as a parameter on the cmd line. It also extracts the
+ amount of disk space at (form the "df" out that should also
+ be present in the log file) after the jitter extracted.
+
+ It writes the data to the stderr (where you may redirect it).
+ It is suitable for plotting, as the data is written as
+ COL1=UsedSpace COL2=Jitter
+
+ $Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $
+ $Log: plotJittervsFill.c,v $
+ Revision 1.6 2005/11/07 11:15:21 gleixner
+ [MTD / JFFS2] Clean up trailing white spaces
+
+ Revision 1.5 2001/08/10 19:23:11 vipin
+ Ready to be released under GPL! Added proper headers etc.
+
+ Revision 1.4 2001/07/02 22:25:40 vipin
+ Fixed couple of minor cosmetic typos.
+
+ Revision 1.3 2001/07/02 14:46:46 vipin
+ Added a debug option where it o/p's line numbers to debug funky values.
+
+ Revision 1.2 2001/06/26 19:48:57 vipin
+ Now prints out jitter values found at end of log file, after which
+ no new "df" disk usage values were encountered. The last "df" disk usage
+ encountered is printed for these orphaned values.
+
+ Revision 1.1 2001/06/25 19:13:55 vipin
+ Added new file- plotJittervsFill.c- that mines the data log file
+ outputed from the fillFlash.sh script file and JitterTest.c file
+ and produces output suitable to be plotted.
+ This output plot may be examined to see visually the relationship
+ of the Jitter vs disk usage of the fs under test.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static char Version_string[] = "$Id: plotJittervsFill.c,v 1.6 2005/11/07 11:15:21 gleixner Exp $";
+static char LogFile[250] = "InputLogFile.log";
+
+static int JitterThreshold_ms = 1000;
+static int Debug = 0; /* Debug level. Each "-d" on the cmd line increases the level */
+
+#define TRUE 1
+#define FALSE 0
+
+#define MIN_JITTER_THRESHOLD 1 /* ms minimum jitter threshold */
+
+void PrintHelpInfo(void)
+{
+ printf("Usage: plotJittervsFill [options] -f [--file] <input log file name> -t [--jitter_threshold] <jitter threshold in ms>\n");
+ printf("[options]:\n-v [--version] Print version and exit\n");
+ printf("-d Debug. Prints input file line number for each data point picked up.\n");
+ printf("-h [--help] [-?] Print this help screen and exit.\n");
+}
+
+
+
+/***********************************************************************
+ * HandleCmdLineArgs
+ * This function handles the command line arguments.
+ * output: stack size
+ ***********************************************************************/
+void HandleCmdLineArgs(
+ int argc, /* number of command-line arguments */
+ char *argv[]) /* ptrs to command-line arguments */
+{
+ int argNum; /* argument number */
+
+ if (argc > (int) 1) {
+
+ for (argNum = (int) 1; argNum < argc; argNum++) {
+
+ /* The command line contains an argument. */
+
+ if ((strcmp(argv[argNum],"--version") == 0) ||
+ (strcmp(argv[argNum],"-v") == 0)) {
+ /* Print version information and exit. */
+ printf("%s\n", Version_string);
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--help") == 0) ||
+ (strcmp(argv[argNum],"-h") == 0) ||
+ (strcmp(argv[argNum],"-?") == 0)) {
+ /* Print help information and exit. */
+ PrintHelpInfo();
+ exit(0);
+ }
+
+ else if ((strcmp(argv[argNum],"--file") == 0) ||
+ (strcmp(argv[argNum],"-f") == 0)) {
+ /* Set the name of the output file. */
+ ++argNum;
+ if (argNum < argc) {
+ strncpy(LogFile, argv[argNum], sizeof(LogFile));
+ }
+ else {
+ printf("*** Input file name not specified. ***\n");
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"--jitter_threshold") == 0) ||
+ (strcmp(argv[argNum],"-t") == 0)) {
+ /* Set the file to read*/
+ ++argNum;
+
+ JitterThreshold_ms = atoi(argv[argNum]);
+
+ if(JitterThreshold_ms < MIN_JITTER_THRESHOLD)
+ {
+ printf("A jitter threshold less than %i ms is not allowed. Bye.\n",
+ MIN_JITTER_THRESHOLD);
+ exit(0);
+ }
+ }
+
+ else if ((strcmp(argv[argNum],"-d") == 0))
+ {
+ /* Increment debug level */
+
+ Debug++;
+ }
+
+ else {
+ /* Unknown argument. Print help information and exit. */
+ printf("Invalid option %s\n", argv[argNum]);
+ printf("Try 'plotJittervsFill --help' for more information.\n");
+ exit(0);
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+
+
+int main(
+ int argc,
+ char *argv[])
+{
+
+ char lineBuf[1024]; /* how long a single line be? */
+ int converted;
+ int lineNo = 0;
+ int cnt;
+
+ FILE *fp;
+
+ int junkInt1, junkInt2, junkInt3;
+ float junkFloat1;
+ float jitter_ms;
+
+#define MAX_SAVE_BUFFER 1000 /* How many values will be picked up while searching for
+ a % disk full line (i.e. before they can be printed out)
+ */
+ int saveJitter[MAX_SAVE_BUFFER]; /* lets us record multiple jitter values that exceed
+ our threshold till we find a "df" field- which is when
+ we can o/p all these values.
+ */
+ int dataLineNo[MAX_SAVE_BUFFER]; /* The saved line #'s for the above. Printed if debug specified. */
+
+ int saveJitterCnt = 0;
+ int lookFor_df = FALSE;
+ int dfPercent = -1; /* will be >= 0 if at least one found. The init value is a flag. */
+
+ char junkStr1[500], junkStr2[500];
+
+ HandleCmdLineArgs(argc, argv);
+
+ if((fp = fopen(LogFile, "r")) == NULL)
+ {
+ printf("Unable to open input log file %s for read.\b", LogFile);
+ perror("Error:");
+ exit(1);
+ }
+
+
+
+ while(fgets(lineBuf, sizeof(lineBuf), fp) != NULL)
+ {
+ lineNo++;
+
+
+ /* Are we looking for a "df" o/p line? (to see how full
+ the flash is?)*/
+
+ /* is there a "%" in this line? */
+ if((strstr(lineBuf, "%") != NULL) && (lookFor_df))
+ {
+ converted = sscanf(lineBuf, "%s %i %i %i %i\n",
+ junkStr1, &junkInt1, &junkInt2, &junkInt3, &dfPercent);
+ if(converted < 5)
+ {
+ printf("Line %i contains \"%%\", but expected fileds not found. Skipping.\n", lineNo);
+ }else
+ {
+ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */
+ for(cnt = 0; cnt < saveJitterCnt; cnt++)
+ {
+ if(Debug)
+ {
+ fprintf(stderr, "%i\t%i\t%i\n", (int)dataLineNo[cnt],
+ dfPercent, (int)saveJitter[cnt]);
+ }else
+ {
+ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]);
+ }
+
+
+ }
+
+ saveJitterCnt = 0; /* all flushed. Reset for next saves. */
+ lookFor_df = FALSE;
+ }
+
+ }
+
+
+ /* is there a "ms" in this line?*/
+ if(strstr(lineBuf, "ms") == NULL)
+ {
+ continue;
+ }
+
+ /* grab the ms jitter value */
+ converted = sscanf(lineBuf, "%f %s %f %s\n", &junkFloat1, junkStr1, &jitter_ms, junkStr2);
+ if(converted < 4)
+ {
+ printf("Line %i contains \"ms\", but expected fileds not found. Converted %i, Skipping.",
+ lineNo, converted);
+ printf("1=%i, 2=%s.\n", junkInt1, junkStr1);
+ continue; /* not our jitter line*/
+ }
+
+ /* Is the jitter value > threshold value? */
+ if(abs(jitter_ms) > JitterThreshold_ms)
+ {
+ /* Found a jitter line that matches our crietrion.
+ Now set flag to be on the look out for the next
+ "df" output so that we can see how full the flash is.
+ */
+
+ if(saveJitterCnt < MAX_SAVE_BUFFER)
+ {
+ saveJitter[saveJitterCnt] = (int)abs(jitter_ms); /* why keep the (ms) jitter in float */
+ dataLineNo[saveJitterCnt] = lineNo;
+ saveJitterCnt++;
+ lookFor_df = TRUE;
+ }
+ else
+ {
+ printf("Oops! I've run out of buffer space before I found a %% use line. Dropping itter value. Increase MAX_SAVE_BUFFER and recompile.\n");
+ }
+
+
+ }
+
+ }
+
+
+ /* Now print out any saved jitter values that were not printed out because we did not find
+ and "df" after these were picked up. Only print if a "df" disk usage was ever found.
+ */
+ if(lookFor_df && (dfPercent >= 0))
+ {
+ /* Now print out the saved jitter values (in col2) with this dfPercent value as the col1. */
+ for(cnt = 0; cnt < saveJitterCnt; cnt++)
+ {
+ fprintf(stderr, "%i\t%i\n", dfPercent, (int)saveJitter[cnt]);
+ }
+ }
+
+ return 0;
+
+
+}/* end main() */
+
+
+
+
diff --git a/mkfs.ffs2.c b/mkfs.ffs2.c
new file mode 100644
index 0000000..36204dd
--- /dev/null
+++ b/mkfs.ffs2.c
@@ -0,0 +1,239 @@
+// Description
+// $Id: mkfs.ffs2.c,v 1.5 2005/11/07 11:15:12 gleixner Exp $
+/* ######################################################################
+
+ Microsoft Flash File System 2
+
+ Information for the FFS2.0 was found in Microsoft's knowledge base,
+ http://msdn.microsoft.com/isapi/msdnlib.idc?theURL=/library/specs/S346A.HTM
+ Try searching for "Flash File System" if it has been moved
+
+ This program creates an empty file system.
+
+ ##################################################################### */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <mtd/mtd-user.h"
+#include <linux/ffs2_fs.h"
+
+static unsigned long BlockSize = 128*1024;
+static int Fd;
+
+// Erase a single block
+int EraseBlock(unsigned Number)
+{
+ unsigned char Blank[512];
+ unsigned I;
+
+ memset(Blank,0xFF,sizeof(Blank));
+
+ if (lseek(Fd,BlockSize*Number,SEEK_SET) != BlockSize*Number)
+ return -1;
+
+ for (I = 0; I*sizeof(Blank) != BlockSize; I++)
+ {
+ if (write(Fd,Blank,sizeof(Blank)) != sizeof(Blank))
+ return -1;
+ }
+
+ return 0;
+}
+
+int main (int argc,char * const argv[])
+{
+ struct mtd_info meminfo;
+ unsigned Device;
+ unsigned int Opt;
+ unsigned I;
+ unsigned long Length;
+ unsigned long Spares = 1;
+ unsigned long Start = 0;
+
+ // Process options
+ while ((Opt = getopt(argc,argv,"b:s:")) != EOF)
+ {
+ switch (Opt)
+ {
+ case 'b':
+ BlockSize = strtol(optarg, NULL, 0);
+ break;
+
+ case 's':
+ Start = strtol(optarg, NULL, 0);
+ break;
+
+ case '?':
+ return 16;
+ }
+ }
+
+ // Find the device name
+ Device = optind;
+ for (;Device < argc && (argv[Device][0] == 0 ||
+ argv[Device][0] == '-'); Device++);
+ if (Device >= argc)
+ {
+ fprintf(stderr,"You must specify a device\n");
+ return 16;
+ }
+
+ // Open and size the device
+ if ((Fd = open(argv[Device],O_RDWR)) < 0)
+ {
+ fprintf(stderr,"File open error\n");
+ return 8;
+ }
+ if (ioctl(Fd,BLKGETSIZE,&Length) < 0)
+ {
+ Length = lseek(Fd,0,SEEK_END);
+ lseek(Fd,0,SEEK_SET);
+ }
+ else
+ Length *= 512;
+
+ if ((Start + 1)*BlockSize > Length)
+ {
+ fprintf(stderr,"The flash is not large enough\n");
+ }
+
+ printf("Total size is %lu, %lu byte erase "
+ "blocks for %lu blocks with %lu spares.\n",Length,BlockSize,
+ Length/BlockSize,Spares);
+ if (Start != 0)
+ printf("Skiping the first %lu bytes\n",Start*BlockSize);
+
+ if (ioctl(Fd,MEMGETINFO,&meminfo) == 0)
+ {
+ struct erase_info erase;
+ printf("Performing Flash Erase");
+ fflush(stdout);
+
+ erase.length = Length - Start*BlockSize;
+ erase.start = Start*BlockSize;
+ if (ioctl(Fd,MEMERASE,&erase) != 0)
+ {
+ perror("\nMTD Erase failure");
+ close(Fd);
+ return 8;
+ }
+ printf(" done\n");
+ }
+ else
+ {
+ for (I = Start; I <= Length/BlockSize; I++)
+ {
+ printf("Erase %u\r",I);
+ fflush(stdout);
+ if (EraseBlock(I) != 0)
+ {
+ perror(argv[Device]);
+ close(Fd);
+ return 8;
+ }
+ }
+ }
+
+ for (I = 0; I != Length/BlockSize; I++)
+ {
+ struct ffs2_block block;
+
+ // Write the block structure
+ memset(&block,0xFF,sizeof(block));
+ block.EraseCount = 1;
+ block.BlockSeq = I;
+ block.BlockSeqChecksum = 0xFFFF ^ block.BlockSeq;
+ block.Status = (block.Status & (~FFS_STATE_MASK)) | FFS_STATE_READY;
+
+ // Is Spare
+ if (I >= Length/BlockSize - Spares)
+ {
+ block.BlockSeq = 0xFFFF;
+ block.BlockSeqChecksum = 0xFFFF;
+ block.Status = (block.Status & (~FFS_STATE_MASK)) | FFS_STATE_SPARE;
+ }
+
+ // Setup the boot record and the root record
+ if (I == 0)
+ {
+ struct ffs2_bootrecord boot;
+ struct ffs2_blockalloc alloc[2];
+ unsigned char Tmp[300];
+ struct ffs2_entry *root = (struct ffs2_entry *)Tmp;
+
+ block.BootRecordPtr = 0;
+ block.Status = (block.Status & (~FFS_BOOTP_MASK)) | FFS_BOOTP_CURRENT;
+
+ boot.Signature = 0xF1A5;
+ boot.SerialNumber = time(0);
+ boot.FFSWriteVersion = 0x200;
+ boot.FFSReadVersion = 0x200;
+ boot.TotalBlockCount = Length/BlockSize;
+ boot.SpareBlockCount = Spares;
+ boot.BlockLen = BlockSize;
+ boot.RootDirectoryPtr = 0x1;
+ boot.BootCodeLen = 0;
+
+ memset(root,0xFF,sizeof(*root));
+ root->Status = (root->Status & (~FFS_ENTRY_TYPEMASK)) | FFS_ENTRY_TYPEDIR;
+ root->NameLen = strlen("root");
+ root->Time = (__u16)boot.SerialNumber;
+ root->Date = (__u16)(boot.SerialNumber >> 16);
+ root->VarStructLen = 0;
+ strcpy(root->Name,"root");
+
+ // Boot Block allocation structure
+ alloc[1].Status = (0xFF & (~FFS_ALLOC_SMASK)) | FFS_ALLOC_ALLOCATED;
+ alloc[1].Status &= 0xFF & (~FFS_ALLOC_EMASK);
+ alloc[1].Offset[0] = 0;
+ alloc[1].Offset[1] = 0;
+ alloc[1].Offset[2] = 0;
+ alloc[1].Len = FFS_SIZEOF_BOOT;
+
+ // Root Dir allocation structure
+ alloc[0].Status = (0xFF & (~FFS_ALLOC_SMASK)) | FFS_ALLOC_ALLOCATED;
+ alloc[0].Offset[0] = FFS_SIZEOF_BOOT;
+ alloc[0].Offset[1] = 0;
+ alloc[0].Offset[2] = 0;
+ alloc[0].Len = FFS_SIZEOF_ENTRY + root->NameLen;
+
+ // Write the two headers
+ if (lseek(Fd,BlockSize*(I+Start),SEEK_SET) < 0 ||
+ write(Fd,&boot,FFS_SIZEOF_BOOT) != FFS_SIZEOF_BOOT ||
+ write(Fd,root,FFS_SIZEOF_ENTRY + root->NameLen) != FFS_SIZEOF_ENTRY + root->NameLen)
+ {
+ perror("Failed writing headers");
+ close(Fd);
+ return 8;
+ }
+
+ // And the two allocation structures
+ if (lseek(Fd,BlockSize*(I+Start+1) - FFS_SIZEOF_BLOCK - sizeof(alloc),
+ SEEK_SET) <= 0 ||
+ write(Fd,alloc,sizeof(alloc)) != sizeof(alloc))
+ {
+ perror("Failed writing allocations");
+ close(Fd);
+ return 8;
+ }
+ }
+
+ if (lseek(Fd,BlockSize*(I+Start+1) - FFS_SIZEOF_BLOCK,
+ SEEK_SET) <= 0 ||
+ write(Fd,&block,FFS_SIZEOF_BLOCK) != FFS_SIZEOF_BLOCK)
+ {
+ perror("Failed writing block");
+ close(Fd);
+ return 8;
+ }
+ }
+ printf("\n");
+
+ return 0;
+}
diff --git a/mkfs.jffs.c b/mkfs.jffs.c
new file mode 100644
index 0000000..942a093
--- /dev/null
+++ b/mkfs.jffs.c
@@ -0,0 +1,739 @@
+/*
+ * Build a JFFS image in a file, from a given directory tree.
+ *
+ * By default, builds an image that is of the same endianness as the
+ * host.
+ * The -a option can be used when building for a target system which
+ * has a different endianness than the host.
+ */
+
+/* $Id: mkfs.jffs.c,v 1.15 2005/11/07 11:15:13 gleixner Exp $ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <ctype.h>
+
+#define swab16(x) \
+ ((__u16)( \
+ (((__u16)(x) & (__u16)0x00ffU) << 8) | \
+ (((__u16)(x) & (__u16)0xff00U) >> 8) ))
+#define swab32(x) \
+ ((__u32)( \
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(x) ({ __u16 _x = x; swab16(_x); })
+#define cpu_to_le32(x) ({ __u32 _x = x; swab32(_x); })
+#define cpu_to_be16(x) (x)
+#define cpu_to_be32(x) (x)
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_be16(x) ({ __u16 _x = x; swab16(_x); })
+#define cpu_to_be32(x) ({ __u32 _x = x; swab32(_x); })
+#endif
+#define le32_to_cpu(x) cpu_to_le32(x)
+#define be32_to_cpu(x) cpu_to_be32(x)
+
+#define BLOCK_SIZE 1024
+#define JFFS_MAGIC 0x34383931 /* "1984" */
+#define JFFS_MAX_NAME_LEN 256
+#define JFFS_MIN_INO 1
+#define JFFS_TRACE_INDENT 4
+#define JFFS_ALIGN_SIZE 4
+static int MAX_CHUNK_SIZE = 32768;
+
+/* How many padding bytes should be inserted between two chunks of data
+ on the flash? */
+#define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \
+ - ((__u32)(size) % JFFS_ALIGN_SIZE)) \
+ % JFFS_ALIGN_SIZE)
+
+
+struct jffs_raw_inode
+{
+ __u32 magic; /* A constant magic number. */
+ __u32 ino; /* Inode number. */
+ __u32 pino; /* Parent's inode number. */
+ __u32 version; /* Version number. */
+ __u32 mode; /* file_type, mode */
+ __u16 uid;
+ __u16 gid;
+ __u32 atime;
+ __u32 mtime;
+ __u32 ctime;
+ __u32 offset; /* Where to begin to write. */
+ __u32 dsize; /* Size of the file data. */
+ __u32 rsize; /* How much are going to be replaced? */
+ __u8 nsize; /* Name length. */
+ __u8 nlink; /* Number of links. */
+ __u8 spare : 6; /* For future use. */
+ __u8 rename : 1; /* Is this a special rename? */
+ __u8 deleted : 1; /* Has this file been deleted? */
+ __u8 accurate; /* The inode is obsolete if accurate == 0. */
+ __u32 dchksum; /* Checksum for the data. */
+ __u16 nchksum; /* Checksum for the name. */
+ __u16 chksum; /* Checksum for the raw_inode. */
+};
+
+
+struct jffs_file
+{
+ struct jffs_raw_inode inode;
+ char *name;
+ unsigned char *data;
+};
+
+
+char *root_directory_name = NULL;
+int fs_pos = 0;
+int verbose = 0;
+
+#define ENDIAN_HOST 0
+#define ENDIAN_BIG 1
+#define ENDIAN_LITTLE 2
+int endian = ENDIAN_HOST;
+
+static __u32 jffs_checksum(void *data, int size);
+void jffs_print_trace(const char *path, int depth);
+int make_root_dir(FILE *fs, int first_ino, const char *root_dir_path,
+ int depth);
+void write_file(struct jffs_file *f, FILE *fs, struct stat st);
+void read_data(struct jffs_file *f, const char *path, int offset);
+int mkfs(FILE *fs, const char *path, int ino, int parent, int depth);
+
+
+static __u32
+jffs_checksum(void *data, int size)
+{
+ __u32 sum = 0;
+ __u8 *ptr = (__u8 *)data;
+
+ while (size-- > 0)
+ {
+ sum += *ptr++;
+ }
+
+ return sum;
+}
+
+
+void
+jffs_print_trace(const char *path, int depth)
+{
+ int path_len = strlen(path);
+ int out_pos = depth * JFFS_TRACE_INDENT;
+ int pos = path_len - 1;
+ char *out = (char *)alloca(depth * JFFS_TRACE_INDENT + path_len + 1);
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "jffs_print_trace(): path: \"%s\"\n", path);
+ }
+
+ if (!out) {
+ fprintf(stderr, "jffs_print_trace(): Allocation failed.\n");
+ fprintf(stderr, " path: \"%s\"\n", path);
+ fprintf(stderr, "depth: %d\n", depth);
+ exit(1);
+ }
+
+ memset(out, ' ', depth * JFFS_TRACE_INDENT);
+
+ if (path[pos] == '/')
+ {
+ pos--;
+ }
+ while (path[pos] && (path[pos] != '/'))
+ {
+ pos--;
+ }
+ for (pos++; path[pos] && (path[pos] != '/'); pos++)
+ {
+ out[out_pos++] = path[pos];
+ }
+ out[out_pos] = '\0';
+ fprintf(stderr, "%s\n", out);
+}
+
+
+/* Print the contents of a raw inode. */
+void
+jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
+{
+ fprintf(stderr, "jffs_raw_inode: inode number: %u\n", raw_inode->ino);
+ fprintf(stderr, "{\n");
+ fprintf(stderr, " 0x%08x, /* magic */\n", raw_inode->magic);
+ fprintf(stderr, " 0x%08x, /* ino */\n", raw_inode->ino);
+ fprintf(stderr, " 0x%08x, /* pino */\n", raw_inode->pino);
+ fprintf(stderr, " 0x%08x, /* version */\n", raw_inode->version);
+ fprintf(stderr, " 0x%08x, /* mode */\n", raw_inode->mode);
+ fprintf(stderr, " 0x%04x, /* uid */\n", raw_inode->uid);
+ fprintf(stderr, " 0x%04x, /* gid */\n", raw_inode->gid);
+ fprintf(stderr, " 0x%08x, /* atime */\n", raw_inode->atime);
+ fprintf(stderr, " 0x%08x, /* mtime */\n", raw_inode->mtime);
+ fprintf(stderr, " 0x%08x, /* ctime */\n", raw_inode->ctime);
+ fprintf(stderr, " 0x%08x, /* offset */\n", raw_inode->offset);
+ fprintf(stderr, " 0x%08x, /* dsize */\n", raw_inode->dsize);
+ fprintf(stderr, " 0x%08x, /* rsize */\n", raw_inode->rsize);
+ fprintf(stderr, " 0x%02x, /* nsize */\n", raw_inode->nsize);
+ fprintf(stderr, " 0x%02x, /* nlink */\n", raw_inode->nlink);
+ fprintf(stderr, " 0x%02x, /* spare */\n",
+ raw_inode->spare);
+ fprintf(stderr, " %u, /* rename */\n",
+ raw_inode->rename);
+ fprintf(stderr, " %u, /* deleted */\n",
+ raw_inode->deleted);
+ fprintf(stderr, " 0x%02x, /* accurate */\n",
+ raw_inode->accurate);
+ fprintf(stderr, " 0x%08x, /* dchksum */\n", raw_inode->dchksum);
+ fprintf(stderr, " 0x%04x, /* nchksum */\n", raw_inode->nchksum);
+ fprintf(stderr, " 0x%04x, /* chksum */\n", raw_inode->chksum);
+ fprintf(stderr, "}\n");
+}
+
+static void write_val32(__u32 *adr, __u32 val)
+{
+ switch(endian) {
+ case ENDIAN_HOST:
+ *adr = val;
+ break;
+ case ENDIAN_LITTLE:
+ *adr = cpu_to_le32(val);
+ break;
+ case ENDIAN_BIG:
+ *adr = cpu_to_be32(val);
+ break;
+ }
+}
+
+static void write_val16(__u16 *adr, __u16 val)
+{
+ switch(endian) {
+ case ENDIAN_HOST:
+ *adr = val;
+ break;
+ case ENDIAN_LITTLE:
+ *adr = cpu_to_le16(val);
+ break;
+ case ENDIAN_BIG:
+ *adr = cpu_to_be16(val);
+ break;
+ }
+}
+
+static __u32 read_val32(__u32 *adr)
+{
+ __u32 val = 0;
+
+ switch(endian) {
+ case ENDIAN_HOST:
+ val = *adr;
+ break;
+ case ENDIAN_LITTLE:
+ val = le32_to_cpu(*adr);
+ break;
+ case ENDIAN_BIG:
+ val = be32_to_cpu(*adr);
+ break;
+ }
+ return val;
+}
+
+
+/* This function constructs a root inode with no name and
+ no data. The inode is then written to the filesystem
+ image. */
+int
+make_root_dir(FILE *fs, int first_ino, const char *root_dir_path, int depth)
+{
+ struct jffs_file f;
+ struct stat st;
+
+ if (stat(root_dir_path, &st) < 0)
+ {
+ perror("stat");
+ exit(1);
+ }
+
+ write_val32(&f.inode.magic, JFFS_MAGIC);
+ write_val32(&f.inode.ino, first_ino);
+ write_val32(&f.inode.pino, 0);
+ write_val32(&f.inode.version, 1);
+ write_val32(&f.inode.mode, st.st_mode);
+ write_val16(&f.inode.uid, 0); /* root */
+ write_val16(&f.inode.gid, 0); /* root */
+ write_val32(&f.inode.atime, st.st_atime);
+ write_val32(&f.inode.mtime, st.st_mtime);
+ write_val32(&f.inode.ctime, st.st_ctime);
+ write_val32(&f.inode.offset, 0);
+ write_val32(&f.inode.dsize, 0);
+ write_val32(&f.inode.rsize,0);
+ f.inode.nsize = 0;
+ /*f.inode.nlink = st.st_nlink;*/
+ f.inode.nlink = 1;
+ f.inode.spare = 0;
+ f.inode.rename = 0;
+ f.inode.deleted = 0;
+ f.inode.accurate = 0;
+ write_val32(&f.inode.dchksum, 0);
+ write_val16(&f.inode.nchksum, 0);
+ write_val16(&f.inode.chksum, 0);
+ f.name = 0;
+ f.data = 0;
+ write_val16(&f.inode.chksum, jffs_checksum(&f.inode, sizeof(struct jffs_raw_inode)));
+ f.inode.accurate = 0xff;
+ write_file(&f, fs, st);
+ if (verbose >= 1)
+ {
+ jffs_print_trace(root_dir_path, depth);
+ }
+ if (verbose >= 2)
+ {
+ jffs_print_raw_inode(&f.inode);
+ }
+ return first_ino;
+}
+
+
+/* This function writes a chunks of data. A data chunk consists of a
+ raw inode, perhaps a name and perhaps some data. */
+void
+write_file(struct jffs_file *f, FILE *fs, struct stat st)
+{
+ int npad = JFFS_GET_PAD_BYTES(f->inode.nsize);
+ int dpad = JFFS_GET_PAD_BYTES(read_val32(&f->inode.dsize));
+ int size = sizeof(struct jffs_raw_inode) + f->inode.nsize + npad
+ + read_val32(&f->inode.dsize) + dpad;
+ unsigned char ff_data[] = { 0xff, 0xff, 0xff, 0xff };
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "***write_file()\n");
+ }
+
+ /* Write the raw inode. */
+ fwrite((void *)&f->inode, sizeof(struct jffs_raw_inode), 1, fs);
+
+ /* Write the name. */
+ if (f->inode.nsize)
+ {
+ fwrite(f->name, 1, f->inode.nsize, fs);
+ if (npad)
+ {
+ fwrite(ff_data, 1, npad, fs);
+ }
+ }
+
+ /* Write the data. */
+ if (read_val32(&f->inode.dsize))
+ {
+ if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ {
+ __u16 tmp;
+
+ switch(endian) {
+ case ENDIAN_HOST:
+ tmp = st.st_rdev;
+ break;
+ case ENDIAN_LITTLE:
+ tmp = cpu_to_le16(st.st_rdev);
+ break;
+ case ENDIAN_BIG:
+ tmp = cpu_to_be16(st.st_rdev);
+ break;
+ }
+ fwrite((char *)&tmp, sizeof(st.st_rdev) / 4, 1, fs);
+ }
+ else
+ {
+ fwrite(f->data, 1, read_val32(&f->inode.dsize), fs);
+ }
+ if (dpad)
+ {
+ fwrite(ff_data, 1, dpad, fs);
+ }
+ }
+
+ fs_pos += size;
+ /* If the space left on the block is smaller than the size of an
+ inode, then skip it. */
+}
+
+
+void
+read_data(struct jffs_file *f, const char *path, int offset)
+{
+ FILE *file;
+ char *tot_path;
+ int pos = 0;
+ int r;
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "***read_data(): f: 0x%08x, path: \"%s\", offset: %u\r\n",
+ (unsigned int)f, path, offset);
+ fprintf(stderr, " file's size: %u\n", read_val32(&f->inode.dsize));
+ }
+
+ if (!(f->data = (unsigned char *)malloc(read_val32(&f->inode.dsize))))
+ {
+ fprintf(stderr, "read_data(): malloc() failed! (*data)\n");
+ exit(1);
+ }
+
+ if (!(tot_path = (char *)alloca(strlen(path) + f->inode.nsize + 1)))
+ {
+ fprintf(stderr, "read_data(): alloca() failed! (tot_path)\n");
+ exit(1);
+ }
+ strcpy(tot_path, path);
+ strncat(tot_path, f->name, f->inode.nsize);
+
+ if (!(file = fopen(tot_path, "r")))
+ {
+ fprintf(stderr, "read_data(): Couldn't open \"%s\".\n", tot_path);
+ exit(1);
+ }
+
+ if (fseek(file, offset, SEEK_SET) < 0)
+ {
+ fprintf(stderr, "read_data(): fseek failure: path = %s, offset = %u.\n",
+ path, offset);
+ exit(1);
+ }
+
+ while (pos < read_val32(&f->inode.dsize))
+ {
+ if ((r = fread(&f->data[pos], 1, read_val32(&f->inode.dsize) - pos, file)) < 0)
+ {
+ fprintf(stderr, "read_data(): fread failure (%s).\n", path);
+ exit(1);
+ }
+ pos += r;
+ }
+
+ fclose(file);
+}
+
+
+/* This is the routine that constructs the filesystem image. */
+int
+mkfs(FILE *fs, const char *path, int ino, int parent, int depth)
+{
+ struct dirent *dir_entry;
+ DIR *dir;
+ struct stat st;
+ struct jffs_file f;
+ int name_len;
+ int pos = 0;
+ int new_ino = ino;
+ char *filename;
+ int path_len = strlen(path);
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "***mkfs(): path: \"%s\"\r\n", path);
+ }
+
+ if (!(dir = opendir(path)))
+ {
+ perror("opendir");
+ fprintf(stderr, "mkfs(): opendir() failed! (%s)\n", path);
+ exit(1);
+ }
+
+ while ((dir_entry = readdir(dir)))
+ {
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "mkfs(): name: %s\n", dir_entry->d_name);
+ }
+ name_len = strlen(dir_entry->d_name);
+
+ if (((name_len == 1)
+ && (dir_entry->d_name[0] == '.'))
+ || ((name_len == 2)
+ && (dir_entry->d_name[0] == '.')
+ && (dir_entry->d_name[1] == '.')))
+ {
+ continue;
+ }
+
+ if (!(filename = (char *)alloca(path_len + name_len + 1)))
+ {
+ fprintf(stderr, "mkfs(): Allocation failed!\n");
+ exit(0);
+ }
+ strcpy(filename, path);
+ strcat(filename, dir_entry->d_name);
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "mkfs(): filename: %s\n", filename);
+ }
+
+ if (lstat(filename, &st) < 0)
+ {
+ perror("lstat");
+ exit(1);
+ }
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "mkfs(): filename: \"%s\", ino: %d, parent: %d\n",
+ filename, new_ino, parent);
+ }
+
+ write_val32(&f.inode.magic, JFFS_MAGIC);
+ write_val32(&f.inode.ino, new_ino);
+ write_val32(&f.inode.pino, parent);
+ write_val32(&f.inode.version, 1);
+ write_val32(&f.inode.mode, st.st_mode);
+ write_val16(&f.inode.uid, st.st_uid);
+ write_val16(&f.inode.gid, st.st_gid);
+ write_val32(&f.inode.atime, st.st_atime);
+ write_val32(&f.inode.mtime, st.st_mtime);
+ write_val32(&f.inode.ctime, st.st_ctime);
+ write_val32(&f.inode.dsize, 0);
+ write_val32(&f.inode.rsize, 0);
+ f.inode.nsize = name_len;
+ /*f.inode.nlink = st.st_nlink;*/
+ f.inode.nlink = 1;
+ f.inode.spare = 0;
+ f.inode.rename = 0;
+ f.inode.deleted = 0;
+ f.inode.accurate = 0;
+ write_val32(&f.inode.dchksum, 0);
+ write_val16(&f.inode.nchksum, 0);
+ write_val16(&f.inode.chksum, 0);
+ if (dir_entry->d_name)
+ {
+ f.name = strdup(dir_entry->d_name);
+ }
+ else
+ {
+ f.name = 0;
+ }
+
+ repeat:
+ write_val32(&f.inode.offset, pos);
+ f.data = 0;
+ f.inode.accurate = 0;
+ if (S_ISREG(st.st_mode) && st.st_size)
+ {
+ if (st.st_size - pos < MAX_CHUNK_SIZE)
+ {
+ write_val32(&f.inode.dsize, st.st_size - pos);
+ }
+ else
+ {
+ write_val32(&f.inode.dsize, MAX_CHUNK_SIZE);
+ }
+
+ read_data(&f, path, pos);
+ pos += read_val32(&f.inode.dsize);
+ }
+ else if (S_ISLNK(st.st_mode))
+ {
+ int linklen;
+ char *linkdata = malloc(1000);
+ if (!linkdata)
+ {
+ fprintf(stderr, "mkfs(): malloc() failed! (linkdata)\n");
+ exit(1);
+ }
+ if ((linklen = readlink(filename, linkdata, 1000)) < 0)
+ {
+ free(linkdata);
+ fprintf(stderr, "mkfs(): readlink() failed! f.name = \"%s\"\n",
+ f.name);
+ exit(1);
+ }
+
+ write_val32(&f.inode.dsize, linklen);
+ f.data = (unsigned char *)linkdata;
+ f.data[linklen] = '\0';
+ }
+ else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ {
+ write_val32(&f.inode.dsize, sizeof(st.st_rdev) / 4);
+ }
+
+ write_val16(&f.inode.chksum, 0);
+ if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+ {
+ write_val32(&f.inode.dchksum, jffs_checksum((void *)f.data, read_val32(&f.inode.dsize)));
+ }
+ else
+ {
+ write_val32(&f.inode.dchksum, jffs_checksum((void *)&st.st_rdev, sizeof(st.st_rdev) / 4));
+ }
+
+ write_val16(&f.inode.nchksum, jffs_checksum((void *)f.name, f.inode.nsize));
+ write_val16(&f.inode.chksum, jffs_checksum((void *)&f.inode, sizeof(struct jffs_raw_inode)));
+ f.inode.accurate = 0xff;
+
+ write_file(&f, fs, st);
+ if (S_ISREG(st.st_mode) && st.st_size)
+ {
+ if (pos < st.st_size)
+ {
+ write_val32(&f.inode.version, read_val32(&f.inode.version) + 1);
+ goto repeat;
+ }
+ }
+
+ new_ino++;
+ pos = 0;
+ if (verbose >= 1)
+ {
+ jffs_print_trace(f.name, depth);
+ }
+ if (verbose >= 2)
+ {
+ jffs_print_raw_inode(&f.inode);
+ }
+
+ if (S_ISDIR(st.st_mode))
+ {
+ char *new_path;
+
+ if (!(new_path = (char *)alloca(strlen(path) + name_len + 1 + 1)))
+ {
+ fprintf(stderr, "mkfs(): alloca() failed! (new_path)\n");
+ exit(1);
+ }
+ strcpy(new_path, path);
+ strncat(new_path, f.name, f.inode.nsize);
+ strcat(new_path, "/");
+
+ if (verbose >= 2)
+ {
+ fprintf(stderr, "mkfs(): new_path: \"%s\"\n", new_path);
+ }
+ new_ino = mkfs(fs, new_path, new_ino, new_ino - 1, depth + 1);
+ }
+ if (f.name)
+ {
+ free(f.name);
+ }
+ if (f.data)
+ {
+ free(f.data);
+ }
+ }
+
+ closedir(dir);
+ return new_ino;
+}
+
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: mkfs.jffs -d root_directory [-a little|big] [-e erase_size] [-o output_file] [-v[0-9]]\n");
+ fprintf(stderr, " By default, the file system is built using the same endianness as the\n");
+ fprintf(stderr, " host. If building for a different target, use the -a option.\n");
+}
+
+
+int
+main(int argc, char **argv)
+{
+ FILE *fs;
+ int root_ino;
+ int len;
+ int ch;
+ extern int optind;
+ extern char *optarg;
+
+ fs = stdout; /* Send constructed file system to stdout by default */
+
+ while ((ch = getopt(argc, argv, "a:d:e:v::o:h?")) != -1) {
+ switch((char)ch) {
+ case 'd':
+ len = strlen(optarg);
+ root_directory_name = (char *)malloc(len + 2);
+ memcpy(root_directory_name, optarg, len);
+ if (root_directory_name[len - 1] != '/')
+ {
+ root_directory_name[len++] = '/';
+ }
+ root_directory_name[len] = '\0';
+ break;
+ case 'v':
+ if (!optarg || strlen(optarg) == 0) {
+ verbose = 1;
+ }
+ else if (strlen(optarg) > 1 || !isdigit(optarg[0])) {
+ fprintf(stderr, "verbose level must be between 0 and 9!\n");
+ usage();
+ exit(1);
+ }
+ else {
+ verbose = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'o':
+ fs = fopen(optarg, "w");
+ if (!fs) {
+ fprintf(stderr, "unable to open file %s for output.\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'a':
+ if (strcmp(optarg, "little") == 0) {
+ endian = ENDIAN_LITTLE;
+ }
+ else if (strcmp(optarg, "big") == 0) {
+ endian = ENDIAN_BIG;
+ }
+ else {
+ usage();
+ exit(1);
+ }
+ break;
+ case 'e':
+ MAX_CHUNK_SIZE = strtol(optarg, NULL, 0) / 2;
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if ((argc -= optind)) {
+ usage();
+ exit(1);
+ }
+
+ if (root_directory_name == NULL) {
+ fprintf(stderr, "Error: must specify a root directory\n");
+ usage();
+ exit(1);
+ }
+
+ if (verbose >= 1)
+ {
+ fprintf(stderr, "Constructing JFFS filesystem...\n");
+ }
+ root_ino = make_root_dir(fs, JFFS_MIN_INO, root_directory_name, 0);
+ mkfs(fs, root_directory_name, root_ino + 1, root_ino, 1);
+
+ fclose(fs);
+ free(root_directory_name);
+ exit(0);
+}
diff --git a/mkfs.jffs2.1 b/mkfs.jffs2.1
new file mode 100644
index 0000000..7e32c92
--- /dev/null
+++ b/mkfs.jffs2.1
@@ -0,0 +1,242 @@
+.TH MKFS.JFFS2 1
+.\" $Id: mkfs.jffs2.1,v 1.6 2004/11/26 14:30:15 havasi Exp $
+.SH NAME
+mkfs.jffs2 \- Create a JFFS2 file system image from directory
+.SH SYNOPSIS
+.B mkfs.jffs2
+[
+.B -p,--pad[=SIZE]
+]
+[
+.B -r,-d,--root
+.I directory
+]
+[
+.B -s,--pagesize=SIZE
+]
+[
+.B -e,--eraseblock=SIZE
+]
+[
+.B -c,--cleanmarker=SIZE
+]
+[
+.B -n,--no-cleanmarkers
+]
+[
+.B -o,--output
+.I image.jffs2
+]
+[
+.B -l,--little-endian
+]
+[
+.B -b,--big-endian
+]
+[
+.B -D,--devtable=FILE
+]
+[
+.B -f,--faketime
+]
+[
+.B -q,--squash
+]
+[
+.B -U,--squash-uids
+]
+[
+.B -P,--squash-perms
+]
+[
+.B -m,--compression-mode=MODE
+]
+[
+.B -x,--disable-compressor=NAME
+]
+[
+.B -X,--enable-compressor=NAME
+]
+[
+.B -y,--compressor-priority=PRIORITY:NAME
+]
+[
+.B -L,--list-compressors
+]
+[
+.B -t,--test-compression
+]
+[
+.B -h,--help
+]
+[
+.B -v,--verbose
+]
+[
+.B -V,--version
+]
+[
+.B -i,--incremental
+.I image.jffs2
+]
+
+.SH DESCRIPTION
+The program
+.B mkfs.jffs2
+creates a JFFS2 (Second Journalling Flash File System) file system
+image and writes the resulting image to the file specified by the
+.B -o
+option or by default to the standard output, unless the standard
+output is a terminal device in which case mkfs.jffs2 will abort.
+
+The file system image is created using the files and directories
+contained in the directory specified by the option
+.B -r
+or the present directory, if the
+.B -r
+option is not specified.
+
+Each block of the files to be placed into the file system image
+are compressed using one of the avaiable compressors depending
+on the selected compression mode.
+
+File systems are created with the same endianness as the host,
+unless the
+.B -b
+or
+.B -l
+options are specified. JFFS2 driver in the 2.4 Linux kernel only
+supported images having the same endianness as the CPU. As of 2.5.48,
+the kernel can be changed with a #define to accept images of the
+non-native endianness. Full bi-endian support in the kernel is not
+planned.
+
+It is unlikely that JFFS2 images are useful except in conjuction
+with the MTD (Memory Technology Device) drivers in the Linux
+kernel, since the JFFS2 file system driver in the kernel requires
+MTD devices.
+.SH OPTIONS
+Options that take SIZE arguments can be specified as either
+decimal (e.g., 65536), octal (0200000), or hexidecimal (0x1000).
+.TP
+.B -p, --pad[=SIZE]
+Pad output to SIZE bytes with 0xFF. If SIZE is not specified,
+the output is padded to the end of the final erase block.
+.TP
+.B -r, -d, --root=DIR
+Build file system from directory DIR. The default is the current
+directory.
+.TP
+.B -s, --pagesize=SIZE
+Use page size SIZE. The default is 4 KiB. This size is the
+maximum size of a data node.
+.TP
+.B -e, --eraseblock=SIZE
+Use erase block size SIZE. The default is 64 KiB. If you use a erase
+block size different than the erase block size of the target MTD
+device, JFFS2 may not perform optimally. If the SIZE specified is
+below 4096, the units are assumed to be KiB.
+.TP
+.B -c, --cleanmarker=SIZE
+Write \'CLEANMARKER\' nodes with the size specified. It is not
+normally appropriate to specify a size other than the default 12
+bytes.
+.TP
+.B -n, --no-cleanmarkers
+Do not write \'CLEANMARKER\' nodes to the beginning of each erase
+block. This option can be useful for creating JFFS2 images for
+use on NAND flash, and for creating images which are to be used
+on a variety of hardware with differing eraseblock sizes.
+.TP
+.B -o, --output=FILE
+Write JFFS2 image to file FILE. Default is the standard output.
+.TP
+.B -l, --little-endian
+Create a little-endian JFFS2 image. Default is to make an image
+with the same endianness as the host.
+.TP
+.B -b, --big-endian
+Create a big-endian JFFS2 image. Default is to make an image
+with the same endianness as the host.
+.TP
+.B -D, --devtable=FILE
+Use the named FILE as a device table file, for including devices and
+changing permissions in the created image when the user does not have
+appropriate permissions to create them on the file system used as
+source.
+.TP
+.B -f, --faketime
+Change all file timestamps to \'0\' for regression testing.
+.TP
+.B -q, --squash
+Squash permissions and owners, making all files be owned by root and
+removing write permission for \'group\' and \'other\'.
+.TP
+.B -U, --squash-uids
+Squash owners making all files be owned by root.
+.TP
+.B -P, --squash-perms
+Squash permissions, removing write permission for \'group\' and \'other\'.
+.TP
+.B -m, --compression-mode=MODE
+Set the default compression mode. The default mode is
+.B priority
+which tries the compressors in a predefinied order and chooses the first
+successful one. The alternatives are:
+.B none
+(mkfs will not compress) and
+.B size
+(mkfs will try all compressor and chooses the one which have the smallest result).
+.TP
+.B -x, --disable-compressor=NAME
+Disable a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default states.
+.TP
+.B -X, --enable-compressor=NAME
+Enable a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default states.
+.TP
+.B -y, --compressor-priority=PRIORITY:NAME
+Set the priority of a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default priority.
+Priorities are used by priority compression mode.
+.TP
+.B -L, --list-compressors
+Show the list of the avaiable compressors and their states.
+.TP
+.B -t, --test-compression
+Call decompress after every compress - and compare the result with the original data -, and
+some other check.
+.TP
+.B -h, --help
+Display help text.
+.TP
+.B -v, --verbose
+Verbose operation.
+.TP
+.B -V, --version
+Display version information.
+.TP
+.B -i, --incremental=FILE
+Generate an appendage image for FILE. If FILE is written to flash and flash
+is appended with the output, then it seems as if it was one thing.
+
+.SH BUGS
+JFFS2 limits device major and minor numbers to 8 bits each. Some
+consider this a bug.
+
+.B mkfs.jffs2
+does not properly handle hard links in the input directory structure.
+Currently, hard linked files will be expanded to multiple identical
+files in the output image.
+.SH AUTHORS
+David Woodhouse
+.br
+Manual page written by David Schleef <ds@schleef.org>
+.SH SEE ALSO
+.BR mkfs (8),
+.BR mkfs.jffs (1),
+.BR fakeroot (1)
diff --git a/mkfs.jffs2.c b/mkfs.jffs2.c
new file mode 100644
index 0000000..82439e5
--- /dev/null
+++ b/mkfs.jffs2.c
@@ -0,0 +1,1583 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Build a JFFS2 image in a file, from a given directory tree.
+ *
+ * Copyright 2001, 2002 Red Hat, Inc.
+ * 2001 David A. Schleef <ds@lineo.com>
+ * 2002 Axis Communications AB
+ * 2001, 2002 Erik Andersen <andersen@codepoet.org>
+ * 2004 University of Szeged, Hungary
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Cross-endian support added by David Schleef <ds@schleef.org>.
+ *
+ * Major architectural rewrite by Erik Andersen <andersen@codepoet.org>
+ * to allow support for making hard links (though hard links support is
+ * not yet implemented), and for munging file permissions and ownership
+ * on the fly using --faketime, --squash, --devtable. And I plugged a
+ * few memory leaks, adjusted the error handling and fixed some little
+ * nits here and there.
+ *
+ * I also added a sample device table file. See device_table.txt
+ * -Erik, September 2001
+ *
+ * Cleanmarkers support added by Axis Communications AB
+ *
+ * Rewritten again. Cleanly separated host and target filsystem
+ * activities (mainly so I can reuse all the host handling stuff as I
+ * rewrite other mkfs utils). Added a verbose option to list types
+ * and attributes as files are added to the file system. Major cleanup
+ * and scrubbing of the code so it can be read, understood, and
+ * modified by mere mortals.
+ *
+ * -Erik, November 2002
+ */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <byteswap.h>
+#define crc32 __complete_crap
+#include <zlib.h>
+#undef crc32
+#include "crc32.h"
+
+/* Do not use the wierd XPG version of basename */
+#undef basename
+
+//#define DMALLOC
+//#define mkfs_debug_msg error_msg
+#define mkfs_debug_msg(a...) { }
+#define min(x,y) ({ typeof((x)) _x = (x); typeof((y)) _y = (y); (_x>_y)?_y:_x; })
+
+#define PAD(x) (((x)+3)&~3)
+
+struct filesystem_entry {
+ char *name; /* Name of this directory (think basename) */
+ char *path; /* Path of this directory (think dirname) */
+ char *fullname; /* Full name of this directory (i.e. path+name) */
+ char *hostname; /* Full path to this file on the host filesystem */
+ struct stat sb; /* Stores directory permissions and whatnot */
+ char *link; /* Target a symlink points to. */
+ struct filesystem_entry *parent; /* Parent directory */
+ struct filesystem_entry *prev; /* Only relevant to non-directories */
+ struct filesystem_entry *next; /* Only relevant to non-directories */
+ struct filesystem_entry *files; /* Only relevant to directories */
+};
+
+
+static int out_fd = -1;
+static int in_fd = -1;
+static char default_rootdir[] = ".";
+static char *rootdir = default_rootdir;
+static int verbose = 0;
+static int squash_uids = 0;
+static int squash_perms = 0;
+static int fake_times = 0;
+int target_endian = __BYTE_ORDER;
+static const char *const app_name = "mkfs.jffs2";
+static const char *const memory_exhausted = "memory exhausted";
+
+static void verror_msg(const char *s, va_list p)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s: ", app_name);
+ vfprintf(stderr, s, p);
+}
+static void error_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+}
+
+static void error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void vperror_msg(const char *s, va_list p)
+{
+ int err = errno;
+
+ if (s == 0)
+ s = "";
+ verror_msg(s, p);
+ if (*s)
+ s = ": ";
+ fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+static void perror_msg(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+}
+
+static void perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+ exit(EXIT_FAILURE);
+}
+
+#ifndef DMALLOC
+extern void *xmalloc(size_t size)
+{
+ void *ptr = malloc(size);
+
+ if (ptr == NULL && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern void *xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+
+ if (ptr == NULL && nmemb != 0 && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern void *xrealloc(void *ptr, size_t size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr == NULL && size != 0)
+ error_msg_and_die(memory_exhausted);
+ return ptr;
+}
+
+extern char *xstrdup(const char *s)
+{
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+ t = strdup(s);
+ if (t == NULL)
+ error_msg_and_die(memory_exhausted);
+ return t;
+}
+#endif
+
+extern char *xreadlink(const char *path)
+{
+ static const int GROWBY = 80; /* how large we will grow strings by */
+
+ char *buf = NULL;
+ int bufsize = 0, readsize = 0;
+
+ do {
+ buf = xrealloc(buf, bufsize += GROWBY);
+ readsize = readlink(path, buf, bufsize); /* 1st try */
+ if (readsize == -1) {
+ perror_msg("%s:%s", app_name, path);
+ return NULL;
+ }
+ }
+ while (bufsize < readsize + 1);
+
+ buf[readsize] = '\0';
+
+ return buf;
+}
+static FILE *xfopen(const char *path, const char *mode)
+{
+ FILE *fp;
+ if ((fp = fopen(path, mode)) == NULL)
+ perror_msg_and_die("%s", path);
+ return fp;
+}
+
+static struct filesystem_entry *find_filesystem_entry(
+ struct filesystem_entry *dir, char *fullname, uint32_t type)
+{
+ struct filesystem_entry *e = dir;
+
+ if (S_ISDIR(dir->sb.st_mode)) {
+ e = dir->files;
+ }
+ while (e) {
+ /* Only bother to do the expensive strcmp on matching file types */
+ if (type == (e->sb.st_mode & S_IFMT)) {
+ if (S_ISDIR(e->sb.st_mode)) {
+ int len = strlen(e->fullname);
+
+ /* Check if we are a parent of the correct path */
+ if (strncmp(e->fullname, fullname, len) == 0) {
+ /* Is this an _exact_ match? */
+ if (strcmp(fullname, e->fullname) == 0) {
+ return (e);
+ }
+ /* Looks like we found a parent of the correct path */
+ if (fullname[len] == '/') {
+ if (e->files) {
+ return (find_filesystem_entry (e, fullname, type));
+ } else {
+ return NULL;
+ }
+ }
+ }
+ } else {
+ if (strcmp(fullname, e->fullname) == 0) {
+ return (e);
+ }
+ }
+ }
+ e = e->next;
+ }
+ return (NULL);
+}
+
+static struct filesystem_entry *add_host_filesystem_entry(
+ char *name, char *path, unsigned long uid, unsigned long gid,
+ unsigned long mode, dev_t rdev, struct filesystem_entry *parent)
+{
+ int status;
+ char *tmp;
+ struct stat sb;
+ time_t timestamp = time(NULL);
+ struct filesystem_entry *entry;
+
+ memset(&sb, 0, sizeof(struct stat));
+ status = lstat(path, &sb);
+
+ if (status >= 0) {
+ /* It is ok for some types of files to not exit on disk (such as
+ * device nodes), but if they _do_ exist the specified mode had
+ * better match the actual file or strange things will happen.... */
+ if ((mode & S_IFMT) != (sb.st_mode & S_IFMT)) {
+ error_msg_and_die ("%s: file type does not match specified type!", path);
+ }
+ timestamp = sb.st_mtime;
+ } else {
+ /* If this is a regular file, it _must_ exist on disk */
+ if ((mode & S_IFMT) == S_IFREG) {
+ error_msg_and_die("%s: does not exist!", path);
+ }
+ }
+
+ /* Squash all permissions so files are owned by root, all
+ * timestamps are _right now_, and file permissions
+ * have group and other write removed */
+ if (squash_uids) {
+ uid = gid = 0;
+ }
+ if (squash_perms) {
+ if (!S_ISLNK(mode)) {
+ mode &= ~(S_IWGRP | S_IWOTH);
+ mode &= ~(S_ISUID | S_ISGID);
+ }
+ }
+ if (fake_times) {
+ timestamp = 0;
+ }
+
+ entry = xcalloc(1, sizeof(struct filesystem_entry));
+
+ entry->hostname = xstrdup(path);
+ entry->fullname = xstrdup(name);
+ tmp = xstrdup(name);
+ entry->name = xstrdup(basename(tmp));
+ free(tmp);
+ tmp = xstrdup(name);
+ entry->path = xstrdup(dirname(tmp));
+ free(tmp);
+
+ entry->sb.st_uid = uid;
+ entry->sb.st_gid = gid;
+ entry->sb.st_mode = mode;
+ entry->sb.st_rdev = rdev;
+ entry->sb.st_atime = entry->sb.st_ctime =
+ entry->sb.st_mtime = timestamp;
+ if (S_ISREG(mode)) {
+ entry->sb.st_size = sb.st_size;
+ }
+ if (S_ISLNK(mode)) {
+ entry->link = xreadlink(path);
+ entry->sb.st_size = strlen(entry->link);
+ }
+
+ /* This happens only for root */
+ if (!parent)
+ return (entry);
+
+ /* Hook the file into the parent directory */
+ entry->parent = parent;
+ if (!parent->files) {
+ parent->files = entry;
+ } else {
+ struct filesystem_entry *prev;
+ for (prev = parent->files; prev->next; prev = prev->next);
+ prev->next = entry;
+ entry->prev = prev;
+ }
+
+ return (entry);
+}
+
+static struct filesystem_entry *recursive_add_host_directory(
+ struct filesystem_entry *parent, char *targetpath, char *hostpath)
+{
+ int i, n;
+ struct stat sb;
+ char *hpath, *tpath;
+ struct dirent *dp, **namelist;
+ struct filesystem_entry *entry;
+
+
+ if (lstat(hostpath, &sb)) {
+ perror_msg_and_die("%s", hostpath);
+ }
+
+ entry = add_host_filesystem_entry(targetpath, hostpath,
+ sb.st_uid, sb.st_gid, sb.st_mode, 0, parent);
+
+ n = scandir(hostpath, &namelist, 0, alphasort);
+ if (n < 0) {
+ perror_msg_and_die("opening directory %s", hostpath);
+ }
+
+ for (i=0; i<n; i++)
+ {
+ dp = namelist[i];
+ if (dp->d_name[0] == '.' && (dp->d_name[1] == 0 ||
+ (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
+ {
+ free(dp);
+ continue;
+ }
+
+ asprintf(&hpath, "%s/%s", hostpath, dp->d_name);
+ if (lstat(hpath, &sb)) {
+ perror_msg_and_die("%s", hpath);
+ }
+ if (strcmp(targetpath, "/") == 0) {
+ asprintf(&tpath, "%s%s", targetpath, dp->d_name);
+ } else {
+ asprintf(&tpath, "%s/%s", targetpath, dp->d_name);
+ }
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ recursive_add_host_directory(entry, tpath, hpath);
+ break;
+
+ case S_IFREG:
+ case S_IFSOCK:
+ case S_IFIFO:
+ case S_IFLNK:
+ case S_IFCHR:
+ case S_IFBLK:
+ add_host_filesystem_entry(tpath, hpath, sb.st_uid,
+ sb.st_gid, sb.st_mode, sb.st_rdev, entry);
+ break;
+
+ default:
+ error_msg("Unknown file type %o for %s", sb.st_mode, hpath);
+ break;
+ }
+ free(dp);
+ free(hpath);
+ free(tpath);
+ }
+ free(namelist);
+ return (entry);
+}
+
+/* the GNU C library has a wonderful scanf("%as", string) which will
+ allocate the string with the right size, good to avoid buffer overruns.
+ the following macros use it if available or use a hacky workaround...
+ */
+
+#ifdef __GNUC__
+#define SCANF_PREFIX "a"
+#define SCANF_STRING(s) (&s)
+#define GETCWD_SIZE 0
+#else
+#define SCANF_PREFIX "511"
+#define SCANF_STRING(s) (s = malloc(512))
+#define GETCWD_SIZE -1
+inline int snprintf(char *str, size_t n, const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vsprintf(str, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#endif
+
+/* device table entries take the form of:
+ <path> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>
+ /dev/mem c 640 0 0 1 1 0 0 -
+
+ type can be one of:
+ f A regular file
+ d Directory
+ c Character special device file
+ b Block special device file
+ p Fifo (named pipe)
+
+ I don't bother with symlinks (permissions are irrelevant), hard
+ links (special cases of regular files), or sockets (why bother).
+
+ Regular files must exist in the target root directory. If a char,
+ block, fifo, or directory does not exist, it will be created.
+*/
+static int interpret_table_entry(struct filesystem_entry *root, char *line)
+{
+ char *hostpath;
+ char type, *name = NULL, *tmp, *dir;
+ unsigned long mode = 0755, uid = 0, gid = 0, major = 0, minor = 0;
+ unsigned long start = 0, increment = 1, count = 0;
+ struct filesystem_entry *parent, *entry;
+
+ if (sscanf (line, "%" SCANF_PREFIX "s %c %lo %lu %lu %lu %lu %lu %lu %lu",
+ SCANF_STRING(name), &type, &mode, &uid, &gid, &major, &minor,
+ &start, &increment, &count) < 0)
+ {
+ return 1;
+ }
+
+ if (!strcmp(name, "/")) {
+ error_msg_and_die("Device table entries require absolute paths");
+ }
+
+ asprintf(&hostpath, "%s%s", rootdir, name);
+
+ /* Check if this file already exists... */
+ switch (type) {
+ case 'd':
+ mode |= S_IFDIR;
+ break;
+ case 'f':
+ mode |= S_IFREG;
+ break;
+ case 'p':
+ mode |= S_IFIFO;
+ break;
+ case 'c':
+ mode |= S_IFCHR;
+ break;
+ case 'b':
+ mode |= S_IFBLK;
+ break;
+ default:
+ error_msg_and_die("Unsupported file type");
+ }
+ entry = find_filesystem_entry(root, name, mode);
+ if (entry) {
+ /* Ok, we just need to fixup the existing entry
+ * and we will be all done... */
+ entry->sb.st_uid = uid;
+ entry->sb.st_gid = gid;
+ entry->sb.st_mode = mode;
+ if (major && minor) {
+ entry->sb.st_rdev = makedev(major, minor);
+ }
+ } else {
+ /* If parent is NULL (happens with device table entries),
+ * try and find our parent now) */
+ tmp = strdup(name);
+ dir = dirname(tmp);
+ parent = find_filesystem_entry(root, dir, S_IFDIR);
+ free(tmp);
+ if (parent == NULL) {
+ error_msg ("skipping device_table entry '%s': no parent directory!", name);
+ free(name);
+ free(hostpath);
+ return 1;
+ }
+
+ switch (type) {
+ case 'd':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'f':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'p':
+ add_host_filesystem_entry(name, hostpath, uid, gid, mode, 0, parent);
+ break;
+ case 'c':
+ case 'b':
+ if (count > 0) {
+ dev_t rdev;
+ unsigned long i;
+ char *dname, *hpath;
+
+ for (i = start; i < count; i++) {
+ asprintf(&dname, "%s%lu", name, i);
+ asprintf(&hpath, "%s/%s%lu", rootdir, name, i);
+ rdev = makedev(major, minor + (i * increment - start));
+ add_host_filesystem_entry(dname, hpath, uid, gid,
+ mode, rdev, parent);
+ free(dname);
+ free(hpath);
+ }
+ } else {
+ dev_t rdev = makedev(major, minor);
+ add_host_filesystem_entry(name, hostpath, uid, gid,
+ mode, rdev, parent);
+ }
+ break;
+ default:
+ error_msg_and_die("Unsupported file type");
+ }
+ }
+ free(name);
+ free(hostpath);
+ return 0;
+}
+
+static int parse_device_table(struct filesystem_entry *root, FILE * file)
+{
+ char *line;
+ int status = 0;
+ size_t length = 0;
+
+ /* Turn off squash, since we must ensure that values
+ * entered via the device table are not squashed */
+ squash_uids = 0;
+ squash_perms = 0;
+
+ /* Looks ok so far. The general plan now is to read in one
+ * line at a time, check for leading comment delimiters ('#'),
+ * then try and parse the line as a device table. If we fail
+ * to parse things, try and help the poor fool to fix their
+ * device table with a useful error msg... */
+ line = NULL;
+ while (getline(&line, &length, file) != -1) {
+ /* First trim off any whitespace */
+ int len = strlen(line);
+
+ /* trim trailing whitespace */
+ while (len > 0 && isspace(line[len - 1]))
+ line[--len] = '\0';
+ /* trim leading whitespace */
+ memmove(line, &line[strspn(line, " \n\r\t\v")], len);
+
+ /* How long are we after trimming? */
+ len = strlen(line);
+
+ /* If this is NOT a comment line, try to interpret it */
+ if (len && *line != '#') {
+ if (interpret_table_entry(root, line))
+ status = 1;
+ }
+
+ free(line);
+ line = NULL;
+ }
+ fclose(file);
+
+ return status;
+}
+
+static void cleanup(struct filesystem_entry *dir)
+{
+ struct filesystem_entry *e, *prev;
+
+ e = dir->files;
+ while (e) {
+ if (e->name)
+ free(e->name);
+ if (e->path)
+ free(e->path);
+ if (e->fullname)
+ free(e->fullname);
+ e->next = NULL;
+ e->name = NULL;
+ e->path = NULL;
+ e->fullname = NULL;
+ e->prev = NULL;
+ prev = e;
+ if (S_ISDIR(e->sb.st_mode)) {
+ cleanup(e);
+ }
+ e = e->next;
+ free(prev);
+ }
+}
+
+/* Here is where we do the actual creation of the file system */
+#include "mtd/jffs2-user.h"
+
+#define JFFS2_MAX_FILE_SIZE 0xFFFFFFFF
+#ifndef JFFS2_MAX_SYMLINK_LEN
+#define JFFS2_MAX_SYMLINK_LEN 254
+#endif
+
+static uint32_t ino = 0;
+static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/
+static int out_ofs = 0;
+static int erase_block_size = 65536;
+static int pad_fs_size = 0;
+static int add_ebhs = 1;
+static struct jffs2_raw_ebh ebh;
+static int ebh_size = sizeof(ebh);
+static unsigned char ffbuf[16] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* We default to 4096, per x86. When building a fs for
+ * 64-bit arches and whatnot, use the --pagesize=SIZE option */
+int page_size = 4096;
+
+#include "compr.h"
+
+static void full_write(int fd, const void *buf, int len)
+{
+ int ret;
+
+ while (len > 0) {
+ ret = write(fd, buf, len);
+
+ if (ret < 0)
+ perror_msg_and_die("write");
+
+ if (ret == 0)
+ perror_msg_and_die("write returned zero");
+
+ len -= ret;
+ buf += ret;
+ out_ofs += ret;
+ }
+}
+
+static void padblock(void)
+{
+ while (out_ofs % erase_block_size) {
+ full_write(out_fd, ffbuf, min(sizeof(ffbuf),
+ erase_block_size - (out_ofs % erase_block_size)));
+ }
+}
+
+static void pad(int req)
+{
+ while (req) {
+ if (req > sizeof(ffbuf)) {
+ full_write(out_fd, ffbuf, sizeof(ffbuf));
+ req -= sizeof(ffbuf);
+ } else {
+ full_write(out_fd, ffbuf, req);
+ req = 0;
+ }
+ }
+}
+
+static inline void padword(void)
+{
+ if (out_ofs % 4) {
+ full_write(out_fd, ffbuf, 4 - (out_ofs % 4));
+ }
+}
+
+static inline void pad_block_if_less_than(int req)
+{
+ if (add_ebhs) {
+ if ((out_ofs % erase_block_size) == 0) {
+ full_write(out_fd, &ebh, sizeof(ebh));
+ pad(ebh_size - sizeof(ebh));
+ padword();
+ }
+ }
+ if ((out_ofs % erase_block_size) + req > erase_block_size) {
+ padblock();
+ }
+ if (add_ebhs) {
+ if ((out_ofs % erase_block_size) == 0) {
+ full_write(out_fd, &ebh, sizeof(ebh));
+ pad(ebh_size - sizeof(ebh));
+ padword();
+ }
+ }
+}
+
+static void write_dirent(struct filesystem_entry *e)
+{
+ char *name = e->name;
+ struct jffs2_raw_dirent rd;
+ struct stat *statbuf = &(e->sb);
+ static uint32_t version = 0;
+
+ memset(&rd, 0, sizeof(rd));
+
+ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+ rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name));
+ rd.hdr_crc = cpu_to_je32(crc32(0, &rd,
+ sizeof(struct jffs2_unknown_node) - 4));
+ rd.pino = cpu_to_je32((e->parent) ? e->parent->sb.st_ino : 1);
+ rd.version = cpu_to_je32(version++);
+ rd.ino = cpu_to_je32(statbuf->st_ino);
+ rd.mctime = cpu_to_je32(statbuf->st_mtime);
+ rd.nsize = strlen(name);
+ rd.type = IFTODT(statbuf->st_mode);
+ //rd.unused[0] = 0;
+ //rd.unused[1] = 0;
+ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd) - 8));
+ rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name)));
+
+ pad_block_if_less_than(sizeof(rd) + rd.nsize);
+ full_write(out_fd, &rd, sizeof(rd));
+ full_write(out_fd, name, rd.nsize);
+ padword();
+}
+
+static void write_regular_file(struct filesystem_entry *e)
+{
+ int fd, len;
+ uint32_t ver;
+ unsigned int offset;
+ unsigned char *buf, *cbuf, *wbuf;
+ struct jffs2_raw_inode ri;
+ struct stat *statbuf;
+
+
+ statbuf = &(e->sb);
+ if (statbuf->st_size >= JFFS2_MAX_FILE_SIZE) {
+ error_msg("Skipping file \"%s\" too large.", e->path);
+ return;
+ }
+ fd = open(e->hostname, O_RDONLY);
+ if (fd == -1) {
+ perror_msg_and_die("%s: open file", e->hostname);
+ }
+
+ statbuf->st_ino = ++ino;
+ mkfs_debug_msg("writing file '%s' ino=%lu parent_ino=%lu",
+ e->name, (unsigned long) statbuf->st_ino,
+ (unsigned long) e->parent->sb.st_ino);
+ write_dirent(e);
+
+ buf = xmalloc(page_size);
+ cbuf = NULL;
+
+ ver = 0;
+ offset = 0;
+
+ memset(&ri, 0, sizeof(ri));
+ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+
+ ri.ino = cpu_to_je32(statbuf->st_ino);
+ ri.mode = cpu_to_jemode(statbuf->st_mode);
+ ri.uid = cpu_to_je16(statbuf->st_uid);
+ ri.gid = cpu_to_je16(statbuf->st_gid);
+ ri.atime = cpu_to_je32(statbuf->st_atime);
+ ri.ctime = cpu_to_je32(statbuf->st_ctime);
+ ri.mtime = cpu_to_je32(statbuf->st_mtime);
+ ri.isize = cpu_to_je32(statbuf->st_size);
+
+ while ((len = read(fd, buf, page_size))) {
+ unsigned char *tbuf = buf;
+
+ if (len < 0) {
+ perror_msg_and_die("read");
+ }
+
+ while (len) {
+ uint32_t dsize, space;
+ uint16_t compression;
+
+ pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN);
+
+ dsize = len;
+ space =
+ erase_block_size - (out_ofs % erase_block_size) -
+ sizeof(ri);
+ if (space > dsize)
+ space = dsize;
+
+ compression = jffs2_compress(tbuf, &cbuf, &dsize, &space);
+ ri.compr = compression & 0xff;
+ ri.usercompr = (compression >> 8) & 0xff;
+ if (ri.compr) {
+ wbuf = cbuf;
+ } else {
+ wbuf = tbuf;
+ dsize = space;
+ }
+
+ ri.totlen = cpu_to_je32(sizeof(ri) + space);
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.version = cpu_to_je32(++ver);
+ ri.offset = cpu_to_je32(offset);
+ ri.csize = cpu_to_je32(space);
+ ri.dsize = cpu_to_je32(dsize);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, wbuf, space));
+
+ full_write(out_fd, &ri, sizeof(ri));
+ full_write(out_fd, wbuf, space);
+ padword();
+
+ if (tbuf != cbuf) {
+ free(cbuf);
+ cbuf = NULL;
+ }
+
+ tbuf += dsize;
+ len -= dsize;
+ offset += dsize;
+ }
+ }
+ if (!je32_to_cpu(ri.version)) {
+ /* Was empty file */
+ pad_block_if_less_than(sizeof(ri));
+
+ ri.version = cpu_to_je32(++ver);
+ ri.totlen = cpu_to_je32(sizeof(ri));
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+ ri.csize = cpu_to_je32(0);
+ ri.dsize = cpu_to_je32(0);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+
+ full_write(out_fd, &ri, sizeof(ri));
+ padword();
+ }
+ free(buf);
+ close(fd);
+}
+
+static void write_symlink(struct filesystem_entry *e)
+{
+ int len;
+ struct stat *statbuf;
+ struct jffs2_raw_inode ri;
+
+ statbuf = &(e->sb);
+ statbuf->st_ino = ++ino;
+ mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu",
+ e->name, (unsigned long) statbuf->st_ino,
+ (unsigned long) e->parent->sb.st_ino);
+ write_dirent(e);
+
+ len = strlen(e->link);
+ if (len > JFFS2_MAX_SYMLINK_LEN) {
+ error_msg("symlink too large. Truncated to %d chars.",
+ JFFS2_MAX_SYMLINK_LEN);
+ len = JFFS2_MAX_SYMLINK_LEN;
+ }
+
+ memset(&ri, 0, sizeof(ri));
+
+ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+ ri.totlen = cpu_to_je32(sizeof(ri) + len);
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.ino = cpu_to_je32(statbuf->st_ino);
+ ri.mode = cpu_to_jemode(statbuf->st_mode);
+ ri.uid = cpu_to_je16(statbuf->st_uid);
+ ri.gid = cpu_to_je16(statbuf->st_gid);
+ ri.atime = cpu_to_je32(statbuf->st_atime);
+ ri.ctime = cpu_to_je32(statbuf->st_ctime);
+ ri.mtime = cpu_to_je32(statbuf->st_mtime);
+ ri.isize = cpu_to_je32(statbuf->st_size);
+ ri.version = cpu_to_je32(1);
+ ri.csize = cpu_to_je32(len);
+ ri.dsize = cpu_to_je32(len);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, e->link, len));
+
+ pad_block_if_less_than(sizeof(ri) + len);
+ full_write(out_fd, &ri, sizeof(ri));
+ full_write(out_fd, e->link, len);
+ padword();
+}
+
+static void write_pipe(struct filesystem_entry *e)
+{
+ struct stat *statbuf;
+ struct jffs2_raw_inode ri;
+
+ statbuf = &(e->sb);
+ statbuf->st_ino = ++ino;
+ if (S_ISDIR(statbuf->st_mode)) {
+ mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu",
+ e->name, (unsigned long) statbuf->st_ino,
+ (unsigned long) (e->parent) ? e->parent->sb. st_ino : 1);
+ }
+ write_dirent(e);
+
+ memset(&ri, 0, sizeof(ri));
+
+ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+ ri.totlen = cpu_to_je32(sizeof(ri));
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.ino = cpu_to_je32(statbuf->st_ino);
+ ri.mode = cpu_to_jemode(statbuf->st_mode);
+ ri.uid = cpu_to_je16(statbuf->st_uid);
+ ri.gid = cpu_to_je16(statbuf->st_gid);
+ ri.atime = cpu_to_je32(statbuf->st_atime);
+ ri.ctime = cpu_to_je32(statbuf->st_ctime);
+ ri.mtime = cpu_to_je32(statbuf->st_mtime);
+ ri.isize = cpu_to_je32(0);
+ ri.version = cpu_to_je32(1);
+ ri.csize = cpu_to_je32(0);
+ ri.dsize = cpu_to_je32(0);
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(0);
+
+ pad_block_if_less_than(sizeof(ri));
+ full_write(out_fd, &ri, sizeof(ri));
+ padword();
+}
+
+static void write_special_file(struct filesystem_entry *e)
+{
+ jint16_t kdev;
+ struct stat *statbuf;
+ struct jffs2_raw_inode ri;
+
+ statbuf = &(e->sb);
+ statbuf->st_ino = ++ino;
+ write_dirent(e);
+
+ kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) +
+ minor(statbuf->st_rdev));
+
+ memset(&ri, 0, sizeof(ri));
+
+ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+ ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev));
+ ri.hdr_crc = cpu_to_je32(crc32(0,
+ &ri, sizeof(struct jffs2_unknown_node) - 4));
+
+ ri.ino = cpu_to_je32(statbuf->st_ino);
+ ri.mode = cpu_to_jemode(statbuf->st_mode);
+ ri.uid = cpu_to_je16(statbuf->st_uid);
+ ri.gid = cpu_to_je16(statbuf->st_gid);
+ ri.atime = cpu_to_je32(statbuf->st_atime);
+ ri.ctime = cpu_to_je32(statbuf->st_ctime);
+ ri.mtime = cpu_to_je32(statbuf->st_mtime);
+ ri.isize = cpu_to_je32(statbuf->st_size);
+ ri.version = cpu_to_je32(1);
+ ri.csize = cpu_to_je32(sizeof(kdev));
+ ri.dsize = cpu_to_je32(sizeof(kdev));
+ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8));
+ ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev)));
+
+ pad_block_if_less_than(sizeof(ri) + sizeof(kdev));
+ full_write(out_fd, &ri, sizeof(ri));
+ full_write(out_fd, &kdev, sizeof(kdev));
+ padword();
+}
+
+static void recursive_populate_directory(struct filesystem_entry *dir)
+{
+ struct filesystem_entry *e;
+
+ if (verbose) {
+ printf("%s\n", dir->fullname);
+ }
+ e = dir->files;
+ while (e) {
+
+ switch (e->sb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ if (verbose) {
+ printf("\td %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) (e->sb.st_uid), (int) (e->sb.st_gid),
+ e->name);
+ }
+ write_pipe(e);
+ break;
+ case S_IFSOCK:
+ if (verbose) {
+ printf("\ts %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+ }
+ write_pipe(e);
+ break;
+ case S_IFIFO:
+ if (verbose) {
+ printf("\tp %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+ }
+ write_pipe(e);
+ break;
+ case S_IFCHR:
+ if (verbose) {
+ printf("\tc %04o %4d,%4d %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
+ minor(e->sb.st_rdev), (int) e->sb.st_uid,
+ (int) e->sb.st_gid, e->name);
+ }
+ write_special_file(e);
+ break;
+ case S_IFBLK:
+ if (verbose) {
+ printf("\tb %04o %4d,%4d %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, major(e->sb.st_rdev),
+ minor(e->sb.st_rdev), (int) e->sb.st_uid,
+ (int) e->sb.st_gid, e->name);
+ }
+ write_special_file(e);
+ break;
+ case S_IFLNK:
+ if (verbose) {
+ printf("\tl %04o %9lu %5d:%-3d %s -> %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name,
+ e->link);
+ }
+ write_symlink(e);
+ break;
+ case S_IFREG:
+ if (verbose) {
+ printf("\tf %04o %9lu %5d:%-3d %s\n",
+ e->sb.st_mode & ~S_IFMT, e->sb.st_size,
+ (int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
+ }
+ write_regular_file(e);
+ break;
+ default:
+ error_msg("Unknown mode %o for %s", e->sb.st_mode,
+ e->fullname);
+ break;
+ }
+ e = e->next;
+ }
+
+ e = dir->files;
+ while (e) {
+ if (S_ISDIR(e->sb.st_mode)) {
+ if (e->files) {
+ recursive_populate_directory(e);
+ } else if (verbose) {
+ printf("%s\n", e->fullname);
+ }
+ }
+ e = e->next;
+ }
+}
+
+static void create_target_filesystem(struct filesystem_entry *root)
+{
+ ebh.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ebh.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASEBLOCK_HEADER);
+ ebh.totlen = cpu_to_je32(sizeof(ebh));
+ ebh.hdr_crc = cpu_to_je32(crc32(0, &ebh, sizeof(struct jffs2_unknown_node)-4));
+ ebh.reserved = 0;
+ ebh.compat_fset = JFFS2_EBH_COMPAT_FSET;
+ ebh.incompat_fset = JFFS2_EBH_INCOMPAT_FSET;
+ ebh.rocompat_fset = JFFS2_EBH_ROCOMPAT_FSET;
+ ebh.erase_count = cpu_to_je32(0);
+ ebh.node_crc = cpu_to_je32(crc32(0, (unsigned char *)&ebh + sizeof(struct jffs2_unknown_node) + 4,
+ sizeof(struct jffs2_raw_ebh) - sizeof(struct jffs2_unknown_node) - 4));
+
+ if (ino == 0)
+ ino = 1;
+
+ root->sb.st_ino = 1;
+ recursive_populate_directory(root);
+
+ if (pad_fs_size == -1) {
+ padblock();
+ } else {
+ if (pad_fs_size && add_ebhs){
+ padblock();
+ while (out_ofs < pad_fs_size) {
+ full_write(out_fd, &ebh, sizeof(ebh));
+ pad(ebh_size - sizeof(ebh));
+ padblock();
+ }
+ } else {
+ while (out_ofs < pad_fs_size) {
+ full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs));
+ }
+
+ }
+ }
+}
+
+static struct option long_options[] = {
+ {"pad", 2, NULL, 'p'},
+ {"root", 1, NULL, 'r'},
+ {"pagesize", 1, NULL, 's'},
+ {"eraseblock", 1, NULL, 'e'},
+ {"output", 1, NULL, 'o'},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"version", 0, NULL, 'V'},
+ {"big-endian", 0, NULL, 'b'},
+ {"little-endian", 0, NULL, 'l'},
+ {"no-eraseblock-headers", 0, NULL, 'n'},
+ {"eraseblock-header", 1, NULL, 'c'},
+ {"squash", 0, NULL, 'q'},
+ {"squash-uids", 0, NULL, 'U'},
+ {"squash-perms", 0, NULL, 'P'},
+ {"faketime", 0, NULL, 'f'},
+ {"devtable", 1, NULL, 'D'},
+ {"compression-mode", 1, NULL, 'm'},
+ {"disable-compressor", 1, NULL, 'x'},
+ {"test-compression", 0, NULL, 't'},
+ {"compressor-priority", 1, NULL, 'y'},
+ {"incremental", 1, NULL, 'i'},
+ {NULL, 0, NULL, 0}
+};
+
+static char *helptext =
+ "Usage: mkfs.jffs2 [OPTIONS]\n"
+ "Make a JFFS2 file system image from an existing directory tree\n\n"
+ "Options:\n"
+ " -p, --pad[=SIZE] Pad output to SIZE bytes with 0xFF. If SIZE is\n"
+ " not specified, the output is padded to the end of\n"
+ " the final erase block\n"
+ " -r, -d, --root=DIR Build file system from directory DIR (default: cwd)\n"
+ " -s, --pagesize=SIZE Use page size (max data node size) SIZE (default: 4KiB)\n"
+ " -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n"
+ " -c, --eraseblock-header=SIZE Size of eraseblock header (default 28)\n"
+ " -m, --compr-mode=MODE Select compression mode (default: priortiry)\n"
+ " -x, --disable-compressor=COMPRESSOR_NAME\n"
+ " Disable a compressor\n"
+ " -X, --enable-compressor=COMPRESSOR_NAME\n"
+ " Enable a compressor\n"
+ " -y, --compressor-priority=PRIORITY:COMPRESSOR_NAME\n"
+ " Set the priority of a compressor\n"
+ " -L, --list-compressors Show the list of the avaiable compressors\n"
+ " -t, --test-compression Call decompress and compare with the original (for test)\n"
+ " -n, --no-eraseblock-headers Don't add a eraseblock header to every eraseblock\n"
+ " -o, --output=FILE Output to FILE (default: stdout)\n"
+ " -l, --little-endian Create a little-endian filesystem\n"
+ " -b, --big-endian Create a big-endian filesystem\n"
+ " -D, --devtable=FILE Use the named FILE as a device table file\n"
+ " -f, --faketime Change all file times to '0' for regression testing\n"
+ " -q, --squash Squash permissions and owners making all files be owned by root\n"
+ " -U, --squash-uids Squash owners making all files be owned by root\n"
+ " -P, --squash-perms Squash permissions on all files\n"
+ " -h, --help Display this help text\n"
+ " -v, --verbose Verbose operation\n"
+ " -V, --version Display version information\n"
+ " -i, --incremental=FILE Parse FILE and generate appendage output for it\n\n";
+
+static char *revtext = "$Revision: 1.50 $";
+
+int load_next_block() {
+
+ int ret;
+ ret = read(in_fd, file_buffer, erase_block_size);
+
+ if(verbose)
+ printf("Load next block : %d bytes read\n",ret);
+
+ return ret;
+}
+
+void process_buffer(int inp_size) {
+ uint8_t *p = file_buffer;
+ union jffs2_node_union *node;
+ uint16_t type;
+ int bitchbitmask = 0;
+ int obsolete;
+
+ char name[256];
+
+ while ( p < (file_buffer + inp_size)) {
+
+ node = (union jffs2_node_union *) p;
+
+ /* Skip empty space */
+ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
+ p += 4;
+ continue;
+ }
+
+ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
+ if (!bitchbitmask++)
+ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
+ p += 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;
+
+ node->u.nodetype = cpu_to_je16(type);
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE:
+ if(verbose)
+ 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 - file_buffer, 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));
+
+ if ( je32_to_cpu (node->i.ino) > ino )
+ ino = je32_to_cpu (node->i.ino);
+
+ 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;
+
+ if(verbose)
+ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, 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);
+
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (verbose) {
+ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case JFFS2_NODETYPE_ERASEBLOCK_HEADER:
+ if (verbose) {
+ printf ("%8s Eraseblock header at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->eh.totlen));
+ }
+ p += PAD(je32_to_cpu (node->eh.totlen));
+ break;
+
+ case JFFS2_NODETYPE_PADDING:
+ if (verbose) {
+ printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case 0xffff:
+ p += 4;
+ break;
+
+ default:
+ if (verbose) {
+ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ }
+ }
+}
+
+void parse_image(){
+ int ret;
+
+ file_buffer = malloc(erase_block_size);
+
+ if (!file_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+
+ while ((ret = load_next_block())) {
+ process_buffer(ret);
+ }
+
+ if (file_buffer)
+ free(file_buffer);
+
+ close(in_fd);
+}
+
+int main(int argc, char **argv)
+{
+ int c, opt;
+ char *cwd;
+ struct stat sb;
+ FILE *devtable = NULL;
+ struct filesystem_entry *root;
+ char *compr_name = NULL;
+ int compr_prior = -1;
+
+ jffs2_compressors_init();
+
+ while ((opt = getopt_long(argc, argv,
+ "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0)
+ {
+ switch (opt) {
+ case 'D':
+ devtable = xfopen(optarg, "r");
+ if (fstat(fileno(devtable), &sb) < 0)
+ perror_msg_and_die(optarg);
+ if (sb.st_size < 10)
+ error_msg_and_die("%s: not a proper device table file", optarg);
+ break;
+
+ case 'r':
+ case 'd': /* for compatibility with mkfs.jffs, genext2fs, etc... */
+ if (rootdir != default_rootdir) {
+ error_msg_and_die("root directory specified more than once");
+ }
+ rootdir = xstrdup(optarg);
+ break;
+
+ case 's':
+ page_size = strtol(optarg, NULL, 0);
+ break;
+
+ case 'o':
+ if (out_fd != -1) {
+ error_msg_and_die("output filename specified more than once");
+ }
+ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (out_fd == -1) {
+ perror_msg_and_die("open output file");
+ }
+ break;
+
+ case 'q':
+ squash_uids = 1;
+ squash_perms = 1;
+ break;
+
+ case 'U':
+ squash_uids = 1;
+ break;
+
+ case 'P':
+ squash_perms = 1;
+ break;
+
+ case 'f':
+ fake_times = 1;
+ break;
+
+ case 'h':
+ case '?':
+ error_msg_and_die(helptext);
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ error_msg_and_die("revision %.*s\n",
+ (int) strlen(revtext) - 13, revtext + 11);
+
+ case 'e': {
+ char *next;
+ unsigned units = 0;
+ erase_block_size = strtol(optarg, &next, 0);
+ if (!erase_block_size)
+ error_msg_and_die("Unrecognisable erase size\n");
+
+ if (*next) {
+ if (!strcmp(next, "KiB")) {
+ units = 1024;
+ } else if (!strcmp(next, "MiB")) {
+ units = 1024 * 1024;
+ } else {
+ error_msg_and_die("Unknown units in erasesize\n");
+ }
+ } else {
+ if (erase_block_size < 0x1000)
+ units = 1024;
+ else
+ units = 1;
+ }
+ erase_block_size *= units;
+
+ /* If it's less than 8KiB, they're not allowed */
+ if (erase_block_size < 0x2000) {
+ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
+ erase_block_size);
+ erase_block_size = 0x2000;
+ }
+ break;
+ }
+
+ case 'l':
+ target_endian = __LITTLE_ENDIAN;
+ break;
+
+ case 'b':
+ target_endian = __BIG_ENDIAN;
+ break;
+
+ case 'p':
+ if (optarg)
+ pad_fs_size = strtol(optarg, NULL, 0);
+ else
+ pad_fs_size = -1;
+ break;
+ case 'n':
+ add_ebhs = 0;
+ break;
+ case 'c':
+ ebh_size = strtol(optarg, NULL, 0);
+ if (ebh_size < sizeof(ebh)) {
+ error_msg_and_die("ebh size must be >= 28");
+ }
+ if (ebh_size >= erase_block_size) {
+ error_msg_and_die("ebh size must be < eraseblock size");
+ }
+ break;
+ case 'm':
+ if (jffs2_set_compression_mode_name(optarg)) {
+ error_msg_and_die("Unknown compression mode %s", optarg);
+ }
+ break;
+ case 'x':
+ if (jffs2_disable_compressor_name(optarg)) {
+ error_msg_and_die("Unknown compressor name %s",optarg);
+ }
+ break;
+ case 'X':
+ if (jffs2_enable_compressor_name(optarg)) {
+ error_msg_and_die("Unknown compressor name %s",optarg);
+ }
+ break;
+ case 'L':
+ error_msg_and_die("\n%s",jffs2_list_compressors());
+ break;
+ case 't':
+ jffs2_compression_check_set(1);
+ break;
+ case 'y':
+ compr_name = malloc(strlen(optarg));
+ sscanf(optarg,"%d:%s",&compr_prior,compr_name);
+ if ((compr_prior>=0)&&(compr_name)) {
+ if (jffs2_set_compressor_priority(compr_name, compr_prior))
+ exit(EXIT_FAILURE);
+ }
+ else {
+ error_msg_and_die("Cannot parse %s",optarg);
+ }
+ free(compr_name);
+ break;
+ case 'i':
+ if (in_fd != -1) {
+ error_msg_and_die("(incremental) filename specified more than once");
+ }
+ in_fd = open(optarg, O_RDONLY);
+ if (in_fd == -1) {
+ perror_msg_and_die("cannot open (incremental) file");
+ }
+ break;
+ }
+ }
+ if (out_fd == -1) {
+ if (isatty(1)) {
+ error_msg_and_die(helptext);
+ }
+ out_fd = 1;
+ }
+ if (lstat(rootdir, &sb)) {
+ perror_msg_and_die("%s", rootdir);
+ }
+ if (chdir(rootdir))
+ perror_msg_and_die("%s", rootdir);
+
+ if (!(cwd = getcwd(0, GETCWD_SIZE)))
+ perror_msg_and_die("getcwd failed");
+
+ if(in_fd != -1)
+ parse_image();
+
+ root = recursive_add_host_directory(NULL, "/", cwd);
+
+ if (devtable)
+ parse_device_table(root, devtable);
+
+ create_target_filesystem(root);
+
+ cleanup(root);
+
+ if (rootdir != default_rootdir)
+ free(rootdir);
+
+ close(out_fd);
+
+ if (verbose) {
+ char *s = jffs2_stats();
+ fprintf(stderr,"\n\n%s",s);
+ free(s);
+ }
+ if ((verbose)||(jffs2_compression_check_get()&&(jffs2_compression_check_errorcnt_get()))) {
+ fprintf(stderr,"Compression errors: %d\n",jffs2_compression_check_errorcnt_get());
+ }
+
+ jffs2_compressors_exit();
+
+ return 0;
+}
diff --git a/mtd-utils.spec b/mtd-utils.spec
new file mode 100644
index 0000000..606a6a2
--- /dev/null
+++ b/mtd-utils.spec
@@ -0,0 +1,40 @@
+Summary: Tools for maintaining Memory Technology Devices
+Name: mtd-utils
+Version: 1.0
+Release: 1
+License: GPL
+Group: Applications/System
+URL: http://www.linux-mtd.infradead.org/
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+This package contains tools for erasing and formatting flash devices,
+including JFFS2, M-Systems DiskOnChip devices, etc.
+
+%prep
+%setup -q
+
+%build
+make -C util
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR=$RPM_BUILD_ROOT -C util install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+/usr/sbin
+/usr/man/man1/mkfs.jffs2.1.gz
+/usr/include/mtd
+%doc
+
+
+%changelog
+* Wed May 5 2004 <dwmw2@infradead.org> - 1.0
+- Initial build.
+
diff --git a/mtd_debug.c b/mtd_debug.c
new file mode 100644
index 0000000..8f5214e
--- /dev/null
+++ b/mtd_debug.c
@@ -0,0 +1,445 @@
+
+/*
+ * Copyright (c) 2d3D, Inc.
+ * Written by Abraham vd Merwe <abraham@2d3d.co.za>
+ * All rights reserved.
+ *
+ * $Id: mtd_debug.c,v 1.5 2004/05/05 11:57:55 dwmw2 Exp $
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of other contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <mtd/mtd-user.h>
+
+/*
+ * MEMGETINFO
+ */
+static int getmeminfo (int fd,struct mtd_info_user *mtd)
+{
+ return (ioctl (fd,MEMGETINFO,mtd));
+}
+
+/*
+ * MEMERASE
+ */
+static int memerase (int fd,struct erase_info_user *erase)
+{
+ return (ioctl (fd,MEMERASE,erase));
+}
+
+/*
+ * MEMGETREGIONCOUNT
+ * MEMGETREGIONINFO
+ */
+static int getregions (int fd,struct region_info_user *regions,int *n)
+{
+ int i,err;
+ err = ioctl (fd,MEMGETREGIONCOUNT,n);
+ if (err) return (err);
+ for (i = 0; i < *n; i++)
+ {
+ regions[i].regionindex = i;
+ err = ioctl (fd,MEMGETREGIONINFO,&regions[i]);
+ if (err) return (err);
+ }
+ return (0);
+}
+
+int erase_flash (int fd,u_int32_t offset,u_int32_t bytes)
+{
+ int err;
+ struct erase_info_user erase;
+ erase.start = offset;
+ erase.length = bytes;
+ err = memerase (fd,&erase);
+ if (err < 0)
+ {
+ perror ("MEMERASE");
+ return (1);
+ }
+ fprintf (stderr,"Erased %d bytes from address 0x%.8x in flash\n",bytes,offset);
+ return (0);
+}
+
+void printsize (u_int32_t x)
+{
+ int i;
+ static const char *flags = "KMGT";
+ printf ("%u ",x);
+ for (i = 0; x >= 1024 && flags[i] != '\0'; i++) x /= 1024;
+ i--;
+ if (i >= 0) printf ("(%u%c)",x,flags[i]);
+}
+
+int flash_to_file (int fd,u_int32_t offset,size_t len,const char *filename)
+{
+ u_int8_t *buf = NULL;
+ int outfd,err;
+ int size = len * sizeof (u_int8_t);
+ int n = len;
+
+ if (offset != lseek (fd,offset,SEEK_SET))
+ {
+ perror ("lseek()");
+ goto err0;
+ }
+ outfd = creat (filename,O_WRONLY);
+ if (outfd < 0)
+ {
+ perror ("creat()");
+ goto err1;
+ }
+
+retry:
+ if ((buf = (u_int8_t *) malloc (size)) == NULL)
+ {
+#define BUF_SIZE (64 * 1024 * sizeof (u_int8_t))
+ fprintf (stderr, "%s: malloc(%#x)\n", __FUNCTION__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size);
+ goto retry;
+ }
+ perror ("malloc()");
+ goto err0;
+ }
+ do {
+ if (n <= size)
+ size = n;
+ err = read (fd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: read, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("read()");
+ goto err2;
+ }
+ err = write (outfd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("write()");
+ goto err2;
+ }
+ if (err != size)
+ {
+ fprintf (stderr,"Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n",filename,err,size);
+ goto err2;
+ }
+ n -= size;
+ } while (n > 0);
+
+ if (buf != NULL)
+ free (buf);
+ close (outfd);
+ printf ("Copied %d bytes from address 0x%.8x in flash to %s\n",len,offset,filename);
+ return (0);
+
+ err2:
+ close (outfd);
+ err1:
+ if (buf != NULL)
+ free (buf);
+ err0:
+ return (1);
+}
+
+int file_to_flash (int fd,u_int32_t offset,u_int32_t len,const char *filename)
+{
+ u_int8_t *buf = NULL;
+ FILE *fp;
+ int err;
+ int size = len * sizeof (u_int8_t);
+ int n = len;
+
+ if (offset != lseek (fd,offset,SEEK_SET))
+ {
+ perror ("lseek()");
+ return (1);
+ }
+ if ((fp = fopen (filename,"r")) == NULL)
+ {
+ perror ("fopen()");
+ return (1);
+ }
+retry:
+ if ((buf = (u_int8_t *) malloc (size)) == NULL)
+ {
+ fprintf (stderr, "%s: malloc(%#x) failed\n", __FUNCTION__, size);
+ if (size != BUF_SIZE) {
+ size = BUF_SIZE;
+ fprintf (stderr, "%s: trying buffer size %#x\n", __FUNCTION__, size);
+ goto retry;
+ }
+ perror ("malloc()");
+ fclose (fp);
+ return (1);
+ }
+ do {
+ if (n <= size)
+ size = n;
+ if (fread (buf,size,1,fp) != 1 || ferror (fp))
+ {
+ fprintf (stderr, "%s: fread, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("fread()");
+ free (buf);
+ fclose (fp);
+ return (1);
+ }
+ err = write (fd,buf,size);
+ if (err < 0)
+ {
+ fprintf (stderr, "%s: write, size %#x, n %#x\n", __FUNCTION__, size, n);
+ perror ("write()");
+ free (buf);
+ fclose (fp);
+ return (1);
+ }
+ n -= size;
+} while (n > 0);
+
+ if (buf != NULL)
+ free (buf);
+ fclose (fp);
+ printf ("Copied %d bytes from %s to address 0x%.8x in flash\n",len,filename,offset);
+ return (0);
+}
+
+int showinfo (int fd)
+{
+ int i,err,n;
+ struct mtd_info_user mtd;
+ static struct region_info_user region[1024];
+
+ err = getmeminfo (fd,&mtd);
+ if (err < 0)
+ {
+ perror ("MEMGETINFO");
+ return (1);
+ }
+
+ err = getregions (fd,region,&n);
+ if (err < 0)
+ {
+ perror ("MEMGETREGIONCOUNT");
+ return (1);
+ }
+
+ printf ("mtd.type = ");
+ switch (mtd.type)
+ {
+ case MTD_ABSENT:
+ printf ("MTD_ABSENT");
+ break;
+ case MTD_RAM:
+ printf ("MTD_RAM");
+ break;
+ case MTD_ROM:
+ printf ("MTD_ROM");
+ break;
+ case MTD_NORFLASH:
+ printf ("MTD_NORFLASH");
+ break;
+ case MTD_NANDFLASH:
+ printf ("MTD_NANDFLASH");
+ break;
+ case MTD_PEROM:
+ printf ("MTD_PEROM");
+ break;
+ case MTD_OTHER:
+ printf ("MTD_OTHER");
+ break;
+ case MTD_UNKNOWN:
+ printf ("MTD_UNKNOWN");
+ break;
+ default:
+ printf ("(unknown type - new MTD API maybe?)");
+ }
+
+ printf ("\nmtd.flags = ");
+ if (mtd.flags == MTD_CAP_ROM)
+ printf ("MTD_CAP_ROM");
+ else if (mtd.flags == MTD_CAP_RAM)
+ printf ("MTD_CAP_RAM");
+ else if (mtd.flags == MTD_CAP_NORFLASH)
+ printf ("MTD_CAP_NORFLASH");
+ else if (mtd.flags == MTD_CAP_NANDFLASH)
+ printf ("MTD_CAP_NANDFLASH");
+ else if (mtd.flags == MTD_WRITEABLE)
+ printf ("MTD_WRITEABLE");
+ else
+ {
+ int first = 1;
+ static struct
+ {
+ const char *name;
+ int value;
+ } flags[] =
+ {
+ { "MTD_CLEAR_BITS", MTD_CLEAR_BITS },
+ { "MTD_SET_BITS", MTD_SET_BITS },
+ { "MTD_ERASEABLE", MTD_ERASEABLE },
+ { "MTD_WRITEB_WRITEABLE", MTD_WRITEB_WRITEABLE },
+ { "MTD_VOLATILE", MTD_VOLATILE },
+ { "MTD_XIP", MTD_XIP },
+ { "MTD_OOB", MTD_OOB },
+ { "MTD_ECC", MTD_ECC },
+ { NULL, -1 }
+ };
+ for (i = 0; flags[i].name != NULL; i++)
+ if (mtd.flags & flags[i].value)
+ {
+ if (first)
+ {
+ printf (flags[i].name);
+ first = 0;
+ }
+ else printf (" | %s",flags[i].name);
+ }
+ }
+
+ printf ("\nmtd.size = ");
+ printsize (mtd.size);
+
+ printf ("\nmtd.erasesize = ");
+ printsize (mtd.erasesize);
+
+ printf ("\nmtd.oobblock = ");
+ printsize (mtd.oobblock);
+
+ printf ("\nmtd.oobsize = ");
+ printsize (mtd.oobsize);
+
+ printf ("\nmtd.ecctype = ");
+ switch (mtd.ecctype)
+ {
+ case MTD_ECC_NONE:
+ printf ("MTD_ECC_NONE");
+ break;
+ case MTD_ECC_RS_DiskOnChip:
+ printf ("MTD_ECC_RS_DiskOnChip");
+ break;
+ case MTD_ECC_SW:
+ printf ("MTD_ECC_SW");
+ break;
+ default:
+ printf ("(unknown ECC type - new MTD API maybe?)");
+ }
+
+ printf ("\n"
+ "regions = %d\n"
+ "\n",
+ n);
+
+ for (i = 0; i < n; i++)
+ {
+ printf ("region[%d].offset = 0x%.8x\n"
+ "region[%d].erasesize = ",
+ i,region[i].offset,i);
+ printsize (region[i].erasesize);
+ printf ("\nregion[%d].numblocks = %d\n"
+ "region[%d].regionindex = %d\n",
+ i,region[i].numblocks,
+ i,region[i].regionindex);
+ }
+ return (0);
+}
+
+void showusage (const char *progname)
+{
+ fprintf (stderr,
+ "usage: %s info <device>\n"
+ " %s read <device> <offset> <len> <dest-filename>\n"
+ " %s write <device> <offset> <len> <source-filename>\n"
+ " %s erase <device> <offset> <len>\n",
+ progname,
+ progname,
+ progname,
+ progname);
+ exit (1);
+}
+
+#define OPT_INFO 1
+#define OPT_READ 2
+#define OPT_WRITE 3
+#define OPT_ERASE 4
+
+int main (int argc,char *argv[])
+{
+ const char *progname;
+ int err = 0,fd,option = OPT_INFO;
+ int open_flag;
+ (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
+
+ /* parse command-line options */
+ if (argc == 3 && !strcmp (argv[1],"info"))
+ option = OPT_INFO;
+ else if (argc == 6 && !strcmp (argv[1],"read"))
+ option = OPT_READ;
+ else if (argc == 6 && !strcmp (argv[1],"write"))
+ option = OPT_WRITE;
+ else if (argc == 5 && !strcmp (argv[1],"erase"))
+ option = OPT_ERASE;
+ else
+ showusage (progname);
+
+ /* open device */
+ open_flag = (option==OPT_INFO || option==OPT_READ) ? O_RDONLY : O_RDWR;
+ if ((fd = open (argv[2],O_SYNC | open_flag)) < 0)
+ {
+ perror ("open()");
+ exit (1);
+ }
+
+ switch (option)
+ {
+ case OPT_INFO:
+ showinfo (fd);
+ break;
+ case OPT_READ:
+ err = flash_to_file (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]);
+ break;
+ case OPT_WRITE:
+ err = file_to_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0),argv[5]);
+ break;
+ case OPT_ERASE:
+ err = erase_flash (fd,strtol (argv[3],NULL,0),strtol (argv[4],NULL,0));
+ break;
+ }
+
+ /* close device */
+ if (close (fd) < 0)
+ perror ("close()");
+
+ exit (err);
+}
+
diff --git a/nanddump.c b/nanddump.c
new file mode 100644
index 0000000..a942f27
--- /dev/null
+++ b/nanddump.c
@@ -0,0 +1,312 @@
+/*
+ * nanddump.c
+ *
+ * Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org)
+ * Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * $Id: nanddump.c,v 1.29 2005/11/07 11:15:13 gleixner Exp $
+ *
+ * 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 raw NAND chips or NAND
+ * chips contained in DoC devices.
+ */
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+
+#define PROGRAM "nanddump"
+#define VERSION "$Revision: 1.29 $"
+
+void display_help (void)
+{
+ printf("Usage: nanddump [OPTIONS] MTD-device\n"
+ "Dumps the contents of a nand mtd partition.\n"
+ "\n"
+ " --help display this help and exit\n"
+ " --version output version information and exit\n"
+ "-f file --file=file dump to file\n"
+ "-i --ignoreerrors ignore errors\n"
+ "-l length --length=length length\n"
+ "-o --omitoob omit oob data\n"
+ "-b --omitbad omit bad blocks from the dump\n"
+ "-p --prettyprint print nice (hexdump)\n"
+ "-s addr --startaddress=addr start address\n");
+ exit(0);
+}
+
+void display_version (void)
+{
+ printf(PROGRAM " " VERSION "\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 ignoreerrors; // ignore errors
+int pretty_print; // print nice in ascii
+int omitoob; // omit oob data
+unsigned long start_addr; // start address
+unsigned long length; // dump length
+char *mtddev; // mtd device name
+char *dumpfile; // dump file name
+int omitbad;
+
+void process_options (int argc, char *argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "bs:f:il:op";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"ignoreerrors", no_argument, 0, 'i'},
+ {"prettyprint", no_argument, 0, 'p'},
+ {"omitoob", no_argument, 0, 'o'},
+ {"omitbad", no_argument, 0, 'b'},
+ {"startaddress", required_argument, 0, 's'},
+ {"length", required_argument, 0, 'l'},
+ {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 'b':
+ omitbad = 1;
+ break;
+ case 's':
+ start_addr = atol(optarg);
+ break;
+ case 'f':
+ if (!(dumpfile = strdup(optarg))) {
+ perror("stddup");
+ exit(1);
+ }
+ break;
+ case 'i':
+ ignoreerrors = 1;
+ break;
+ case 'l':
+ length = atol(optarg);
+ break;
+ case 'o':
+ omitoob = 1;
+ break;
+ case 'p':
+ pretty_print = 1;
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1 || error)
+ display_help ();
+
+ mtddev = argv[optind];
+}
+
+/*
+ * Buffers for reading data from flash
+ */
+unsigned char readbuf[2048];
+unsigned char oobbuf[64];
+
+/*
+ * Main program
+ */
+int main(int argc, char **argv)
+{
+ unsigned long ofs, end_addr = 0;
+ unsigned long long blockstart = 1;
+ int i, fd, ofd, bs, badblock = 0;
+ struct mtd_oob_buf oob = {0, 16, oobbuf};
+ mtd_info_t meminfo;
+ char pretty_buf[80];
+
+ process_options(argc, argv);
+
+ /* Open MTD device */
+ if ((fd = open(mtddev, O_RDONLY)) == -1) {
+ perror("open flash");
+ exit (1);
+ }
+
+ /* Fill in MTD device capability structure */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("MEMGETINFO");
+ close(fd);
+ exit (1);
+ }
+
+ /* Make sure device page sizes are valid */
+ if (!(meminfo.oobsize == 64 && meminfo.oobblock == 2048) &&
+ !(meminfo.oobsize == 16 && meminfo.oobblock == 512) &&
+ !(meminfo.oobsize == 8 && meminfo.oobblock == 256)) {
+ fprintf(stderr, "Unknown flash (not normal NAND)\n");
+ close(fd);
+ exit(1);
+ }
+ /* Read the real oob length */
+ oob.length = meminfo.oobsize;
+
+ /* Open output file for writing. If file name is "-", write to standard output. */
+ if (!dumpfile) {
+ ofd = STDOUT_FILENO;
+ } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
+ perror ("open outfile");
+ close(fd);
+ exit(1);
+ }
+
+ /* Initialize start/end addresses and block size */
+ if (length)
+ end_addr = start_addr + length;
+ if (!length || end_addr > meminfo.size)
+ end_addr = meminfo.size;
+
+ bs = meminfo.oobblock;
+
+ /* Print informative message */
+ fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", meminfo.erasesize, meminfo.oobblock, meminfo.oobsize);
+ fprintf(stderr, "Dumping data starting at 0x%08x and ending at 0x%08x...\n",
+ (unsigned int) start_addr, (unsigned int) end_addr);
+
+ /* Dump the flash contents */
+ for (ofs = start_addr; ofs < end_addr ; ofs+=bs) {
+
+ // new eraseblock , check for bad block
+ if (blockstart != (ofs & (~meminfo.erasesize + 1))) {
+ blockstart = ofs & (~meminfo.erasesize + 1);
+ if ((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0) {
+ perror("ioctl(MEMGETBADBLOCK)");
+ goto closeall;
+ }
+ }
+
+ if (badblock) {
+ if (omitbad)
+ continue;
+ memset (readbuf, 0xff, bs);
+ } else {
+ /* Read page data and exit on failure */
+ if (pread(fd, readbuf, bs, ofs) != bs) {
+ perror("pread");
+ goto closeall;
+ }
+ }
+
+ /* Write out page data */
+ if (pretty_print) {
+ for (i = 0; i < bs; i += 16) {
+ sprintf(pretty_buf,
+ "0x%08x: %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ (unsigned int) (ofs + i), readbuf[i],
+ readbuf[i+1], readbuf[i+2],
+ readbuf[i+3], readbuf[i+4],
+ readbuf[i+5], readbuf[i+6],
+ readbuf[i+7], readbuf[i+8],
+ readbuf[i+9], readbuf[i+10],
+ readbuf[i+11], readbuf[i+12],
+ readbuf[i+13], readbuf[i+14],
+ readbuf[i+15]);
+ write(ofd, pretty_buf, 60);
+ }
+ } else
+ write(ofd, readbuf, bs);
+
+ if (omitoob)
+ continue;
+
+ if (badblock) {
+ memset (readbuf, 0xff, meminfo.oobsize);
+ } else {
+ /* Read OOB data and exit on failure */
+ oob.start = ofs;
+ if (ioctl(fd, MEMREADOOB, &oob) != 0) {
+ perror("ioctl(MEMREADOOB)");
+ goto closeall;
+ }
+ }
+
+ /* Write out OOB data */
+ if (pretty_print) {
+ if (meminfo.oobsize < 16) {
+ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x "
+ "%02x %02x\n",
+ oobbuf[0], oobbuf[1], oobbuf[2],
+ oobbuf[3], oobbuf[4], oobbuf[5],
+ oobbuf[6], oobbuf[7]);
+ write(ofd, pretty_buf, 48);
+ continue;
+ }
+
+ for (i = 0; i < meminfo.oobsize; i += 16) {
+ sprintf(pretty_buf, " OOB Data: %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ oobbuf[i], oobbuf[i+1], oobbuf[i+2],
+ oobbuf[i+3], oobbuf[i+4], oobbuf[i+5],
+ oobbuf[i+6], oobbuf[i+7], oobbuf[i+8],
+ oobbuf[i+9], oobbuf[i+10], oobbuf[i+11],
+ oobbuf[i+12], oobbuf[i+13], oobbuf[i+14],
+ oobbuf[i+15]);
+ write(ofd, pretty_buf, 60);
+ }
+ } else
+ write(ofd, oobbuf, meminfo.oobsize);
+ }
+
+ /* Close the output file and MTD device */
+ close(fd);
+ close(ofd);
+
+ /* Exit happy */
+ return 0;
+
+ closeall:
+ close(fd);
+ close(ofd);
+ exit(1);
+
+}
diff --git a/nandwrite.c b/nandwrite.c
new file mode 100644
index 0000000..a75ee0f
--- /dev/null
+++ b/nandwrite.c
@@ -0,0 +1,460 @@
+/*
+ * nandwrite.c
+ *
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2003 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: nandwrite.c,v 1.32 2005/11/07 11:15:13 gleixner Exp $
+ *
+ * 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 writes a binary image directly to a NAND flash
+ * chip or NAND chips contained in DoC devices. This is the
+ * "inverse operation" of nanddump.
+ *
+ * tglx: Major rewrite to handle bad blocks, write data with or without ECC
+ * write oob data only on request
+ *
+ * Bug/ToDo:
+ */
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <getopt.h>
+
+#include <asm/types.h>
+#include "mtd/mtd-user.h"
+
+#define PROGRAM "nandwrite"
+#define VERSION "$Revision: 1.32 $"
+
+#define MAX_PAGE_SIZE 2048
+#define MAX_OOB_SIZE 64
+
+/*
+ * Buffer array used for writing data
+ */
+unsigned char writebuf[MAX_PAGE_SIZE];
+unsigned char oobbuf[MAX_OOB_SIZE];
+unsigned char oobreadbuf[MAX_OOB_SIZE];
+
+// oob layouts to pass into the kernel as default
+struct nand_oobinfo none_oobinfo = {
+ .useecc = MTD_NANDECC_OFF,
+};
+
+struct nand_oobinfo jffs2_oobinfo = {
+ .useecc = MTD_NANDECC_PLACE,
+ .eccbytes = 6,
+ .eccpos = { 0, 1, 2, 3, 6, 7 }
+};
+
+struct nand_oobinfo yaffs_oobinfo = {
+ .useecc = MTD_NANDECC_PLACE,
+ .eccbytes = 6,
+ .eccpos = { 8, 9, 10, 13, 14, 15}
+};
+
+struct nand_oobinfo autoplace_oobinfo = {
+ .useecc = MTD_NANDECC_AUTOPLACE
+};
+
+void display_help (void)
+{
+ printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n"
+ "Writes to the specified MTD device.\n"
+ "\n"
+ " -a, --autoplace Use auto oob layout\n"
+ " -j, --jffs2 force jffs2 oob layout (legacy support)\n"
+ " -y, --yaffs force yaffs oob layout (legacy support)\n"
+ " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n"
+ " -n, --noecc write without ecc\n"
+ " -o, --oob image contains oob data\n"
+ " -s addr, --start=addr set start address (default is 0)\n"
+ " -p, --pad pad to page size\n"
+ " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n"
+ " -q, --quiet don't display progress messages\n"
+ " --help display this help and exit\n"
+ " --version output version information and exit\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);
+}
+
+char *mtd_device, *img;
+int mtdoffset = 0;
+int quiet = 0;
+int writeoob = 0;
+int autoplace = 0;
+int forcejffs2 = 0;
+int forceyaffs = 0;
+int forcelegacy = 0;
+int noecc = 0;
+int pad = 0;
+int blockalign = 1; /*default to using 16K block size */
+
+void process_options (int argc, char *argv[])
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "ab:fjnopqs:y";
+ static const struct option long_options[] = {
+ {"help", no_argument, 0, 0},
+ {"version", no_argument, 0, 0},
+ {"autoplace", no_argument, 0, 'a'},
+ {"blockalign", required_argument, 0, 'b'},
+ {"forcelegacy", no_argument, 0, 'f'},
+ {"jffs2", no_argument, 0, 'j'},
+ {"noecc", no_argument, 0, 'n'},
+ {"oob", no_argument, 0, 'o'},
+ {"pad", no_argument, 0, 'p'},
+ {"quiet", no_argument, 0, 'q'},
+ {"start", required_argument, 0, 's'},
+ {"yaffs", no_argument, 0, 'y'},
+ {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 'q':
+ quiet = 1;
+ break;
+ case 'a':
+ autoplace = 1;
+ break;
+ case 'j':
+ forcejffs2 = 1;
+ break;
+ case 'y':
+ forceyaffs = 1;
+ break;
+ case 'f':
+ forcelegacy = 1;
+ break;
+ case 'n':
+ noecc = 1;
+ break;
+ case 'o':
+ writeoob = 1;
+ break;
+ case 'p':
+ pad = 1;
+ break;
+ case 's':
+ mtdoffset = atoi (optarg);
+ break;
+ case 'b':
+ blockalign = atoi (optarg);
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 2 || error)
+ display_help ();
+
+ mtd_device = argv[optind++];
+ img = argv[optind];
+}
+
+/*
+ * Main program
+ */
+int main(int argc, char **argv)
+{
+ int cnt, fd, ifd, imglen = 0, pagelen, baderaseblock, blockstart = -1;
+ struct mtd_info_user meminfo;
+ struct mtd_oob_buf oob;
+ loff_t offs;
+ int ret, readlen;
+ int oobinfochanged = 0;
+ struct nand_oobinfo old_oobinfo;
+
+ process_options(argc, argv);
+
+ memset(oobbuf, 0xff, sizeof(oobbuf));
+
+ if (pad && writeoob) {
+ fprintf(stderr, "Can't pad when oob data is present.\n");
+ exit(1);
+ }
+
+ /* Open the device */
+ if ((fd = open(mtd_device, O_RDWR)) == -1) {
+ perror("open flash");
+ exit(1);
+ }
+
+ /* Fill in MTD device capability structure */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("MEMGETINFO");
+ close(fd);
+ exit(1);
+ }
+
+ /* Set erasesize to specified number of blocks - to match jffs2 (virtual) block size */
+ meminfo.erasesize *= blockalign;
+
+ /* Make sure device page sizes are valid */
+ if (!(meminfo.oobsize == 16 && meminfo.oobblock == 512) &&
+ !(meminfo.oobsize == 8 && meminfo.oobblock == 256) &&
+ !(meminfo.oobsize == 64 && meminfo.oobblock == 2048)) {
+ fprintf(stderr, "Unknown flash (not normal NAND)\n");
+ close(fd);
+ exit(1);
+ }
+
+ /* Read the current oob info */
+ if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMGETOOBSEL");
+ close (fd);
+ exit (1);
+ }
+
+ // write without ecc ?
+ if (noecc) {
+ if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (1);
+ }
+ oobinfochanged = 1;
+ }
+
+ // autoplace ECC ?
+ if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
+
+ if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (1);
+ }
+ oobinfochanged = 1;
+ }
+
+ /*
+ * force oob layout for jffs2 or yaffs ?
+ * Legacy support
+ */
+ if (forcejffs2 || forceyaffs) {
+ struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
+
+ if (autoplace) {
+ fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n");
+ goto restoreoob;
+ }
+ if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) {
+ fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n");
+ goto restoreoob;
+ }
+ if (meminfo.oobsize == 8) {
+ if (forceyaffs) {
+ fprintf (stderr, "YAFSS cannot operate on 256 Byte page size");
+ goto restoreoob;
+ }
+ /* Adjust number of ecc bytes */
+ jffs2_oobinfo.eccbytes = 3;
+ }
+
+ if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) {
+ perror ("MEMSETOOBSEL");
+ goto restoreoob;
+ }
+ }
+
+ oob.length = meminfo.oobsize;
+ oob.ptr = noecc ? oobreadbuf : oobbuf;
+
+ /* Open the input file */
+ if ((ifd = open(img, O_RDONLY)) == -1) {
+ perror("open input file");
+ goto restoreoob;
+ }
+
+ // get image length
+ imglen = lseek(ifd, 0, SEEK_END);
+ lseek (ifd, 0, SEEK_SET);
+
+ pagelen = meminfo.oobblock + ((writeoob == 1) ? meminfo.oobsize : 0);
+
+ // Check, if file is pagealigned
+ if ((!pad) && ((imglen % pagelen) != 0)) {
+ fprintf (stderr, "Input file is not page aligned\n");
+ goto closeall;
+ }
+
+ // Check, if length fits into device
+ if ( ((imglen / pagelen) * meminfo.oobblock) > (meminfo.size - mtdoffset)) {
+ fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
+ imglen, pagelen, meminfo.oobblock, meminfo.size);
+ perror ("Input file does not fit into device");
+ goto closeall;
+ }
+
+ /* Get data from input and write to the device */
+ while (imglen && (mtdoffset < meminfo.size)) {
+ // new eraseblock , check for bad block(s)
+ // Stay in the loop to be sure if the mtdoffset changes because
+ // of a bad block, that the next block that will be written to
+ // is also checked. Thus avoiding errors if the block(s) after the
+ // skipped block(s) is also bad (number of blocks depending on
+ // the blockalign
+ while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
+ blockstart = mtdoffset & (~meminfo.erasesize + 1);
+ offs = blockstart;
+ baderaseblock = 0;
+ if (!quiet)
+ fprintf (stdout, "Writing data to block %x\n", blockstart);
+
+ /* Check all the blocks in an erase block for bad blocks */
+ do {
+ if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
+ perror("ioctl(MEMGETBADBLOCK)");
+ goto closeall;
+ }
+ if (ret == 1) {
+ baderaseblock = 1;
+ if (!quiet)
+ fprintf (stderr, "Bad block at %x, %u block(s) from %x will be skipped\n", (int) offs, blockalign, blockstart);
+ }
+
+ if (baderaseblock) {
+ mtdoffset = blockstart + meminfo.erasesize;
+ }
+ offs += meminfo.erasesize / blockalign ;
+ } while ( offs < blockstart + meminfo.erasesize );
+
+ }
+
+ readlen = meminfo.oobblock;
+ if (pad && (imglen < readlen))
+ {
+ readlen = imglen;
+ memset(writebuf + readlen, 0xff, meminfo.oobblock - readlen);
+ }
+
+ /* Read Page Data from input file */
+ if ((cnt = read(ifd, writebuf, readlen)) != readlen) {
+ if (cnt == 0) // EOF
+ break;
+ perror ("File I/O error on input file");
+ goto closeall;
+ }
+
+ if (writeoob) {
+ /* Read OOB data from input file, exit on failure */
+ if ((cnt = read(ifd, oobreadbuf, meminfo.oobsize)) != meminfo.oobsize) {
+ perror ("File I/O error on input file");
+ goto closeall;
+ }
+ if (!noecc) {
+ int i, start, len;
+ /*
+ * We use autoplacement and have the oobinfo with the autoplacement
+ * information from the kernel available
+ *
+ * Modified to support out of order oobfree segments,
+ * such as the layout used by diskonchip.c
+ */
+ if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) {
+ for (i = 0;old_oobinfo.oobfree[i][1]; i++) {
+ /* Set the reserved bytes to 0xff */
+ start = old_oobinfo.oobfree[i][0];
+ len = old_oobinfo.oobfree[i][1];
+ memcpy(oobbuf + start,
+ oobreadbuf + start,
+ len);
+ }
+ } else {
+ /* Set at least the ecc byte positions to 0xff */
+ start = old_oobinfo.eccbytes;
+ len = meminfo.oobsize - start;
+ memcpy(oobbuf + start,
+ oobreadbuf + start,
+ len);
+ }
+ }
+ /* Write OOB data first, as ecc will be placed in there*/
+ oob.start = mtdoffset;
+ if (ioctl(fd, MEMWRITEOOB, &oob) != 0) {
+ perror ("ioctl(MEMWRITEOOB)");
+ goto closeall;
+ }
+ imglen -= meminfo.oobsize;
+ }
+
+ /* Write out the Page data */
+ if (pwrite(fd, writebuf, meminfo.oobblock, mtdoffset) != meminfo.oobblock) {
+ perror ("pwrite");
+ goto closeall;
+ }
+ imglen -= readlen;
+ mtdoffset += meminfo.oobblock;
+ }
+
+ closeall:
+ close(ifd);
+
+ restoreoob:
+ if (oobinfochanged) {
+ if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
+ perror ("MEMSETOOBSEL");
+ close (fd);
+ exit (1);
+ }
+ }
+
+ close(fd);
+
+ if (imglen > 0) {
+ perror ("Data did not fit into device, due to bad blocks\n");
+ exit (1);
+ }
+
+ /* Return happy */
+ return 0;
+}
diff --git a/nftl_format.c b/nftl_format.c
new file mode 100644
index 0000000..a04411a
--- /dev/null
+++ b/nftl_format.c
@@ -0,0 +1,442 @@
+/*
+ * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device
+ *
+ *
+ * $Id: nftl_format.c,v 1.24 2005/11/07 11:15:13 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ToDo:
+ * 1. UnitSizeFactor != 0xFF cases
+ * 2. test, test, and test !!!
+ */
+
+#define _XOPEN_SOURCE 500 /* for pread/pwrite */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <string.h>
+
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+#include <mtd/nftl-user.h>
+#include <mtd/inftl-user.h>
+
+#define swab16(x) \
+ ((__u16)( \
+ (((__u16)(x) & (__u16)0x00ffU) << 8) | \
+ (((__u16)(x) & (__u16)0xff00U) >> 8) ))
+#define swab32(x) \
+ ((__u32)( \
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(x) ({ __u16 _x = x; swab16(_x); })
+#define cpu_to_le32(x) ({ __u32 _x = x; swab32(_x); })
+#else
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#endif
+#define le32_to_cpu(x) cpu_to_le32(x)
+#define le16_to_cpu(x) cpu_to_le16(x)
+
+unsigned char BadUnitTable[MAX_ERASE_ZONES];
+unsigned char *readbuf;
+unsigned char *writebuf[4];
+
+mtd_info_t meminfo;
+erase_info_t erase;
+int fd;
+struct NFTLMediaHeader *NFTLhdr;
+struct INFTLMediaHeader *INFTLhdr;
+
+static int do_oobcheck = 1;
+static int do_rwecheck = 1;
+
+static unsigned char check_block_1(unsigned long block)
+{
+ unsigned char oobbuf[16];
+ struct mtd_oob_buf oob = { 0, 16, oobbuf };
+
+ oob.start = block * meminfo.erasesize;
+ if (ioctl(fd, MEMREADOOB, &oob))
+ return ZONE_BAD_ORIGINAL;
+
+ if(oobbuf[5] == 0)
+ return ZONE_BAD_ORIGINAL;
+
+ oob.start = block * meminfo.erasesize + 512 /* FIXME */;
+ if (ioctl(fd, MEMREADOOB, &oob))
+ return ZONE_BAD_ORIGINAL;
+
+ if(oobbuf[5] == 0)
+ return ZONE_BAD_ORIGINAL;
+
+ return ZONE_GOOD;
+}
+
+static unsigned char check_block_2(unsigned long block)
+{
+ unsigned long ofs = block * meminfo.erasesize;
+ unsigned long blockofs;
+
+ /* Erase test */
+ erase.start = ofs;
+
+ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) {
+ pread(fd, readbuf, 512, ofs + blockofs);
+ if (memcmp(readbuf, writebuf[0], 512)) {
+ /* Block wasn't 0xff after erase */
+ printf(": Block not 0xff after erase\n");
+ return ZONE_BAD_ORIGINAL;
+ }
+
+ pwrite(fd, writebuf[1], 512, blockofs + ofs);
+ pread(fd, readbuf, 512, blockofs + ofs);
+ if (memcmp(readbuf, writebuf[1], 512)) {
+ printf(": Block not zero after clearing\n");
+ return ZONE_BAD_ORIGINAL;
+ }
+ }
+
+ /* Write test */
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ printf(": Second erase failed (%s)\n", strerror(errno));
+ return ZONE_BAD_ORIGINAL;
+ }
+ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) {
+ pwrite(fd, writebuf[2], 512, blockofs + ofs);
+ pread(fd, readbuf, 512, blockofs + ofs);
+ if (memcmp(readbuf, writebuf[2], 512)) {
+ printf(": Block not 0x5a after writing\n");
+ return ZONE_BAD_ORIGINAL;
+ }
+ }
+
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ printf(": Third erase failed (%s)\n", strerror(errno));
+ return ZONE_BAD_ORIGINAL;
+ }
+ for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) {
+ pwrite(fd, writebuf[3], 512, blockofs + ofs);
+ pread(fd, readbuf, 512, blockofs + ofs);
+ if (memcmp(readbuf, writebuf[3], 512)) {
+ printf(": Block not 0xa5 after writing\n");
+ return ZONE_BAD_ORIGINAL;
+ }
+ }
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ printf(": Fourth erase failed (%s)\n", strerror(errno));
+ return ZONE_BAD_ORIGINAL;
+ }
+ return ZONE_GOOD;
+}
+
+static unsigned char erase_block(unsigned long block)
+{
+ unsigned char status;
+ int ret;
+
+ status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD;
+ erase.start = block * meminfo.erasesize;
+
+ if (status != ZONE_GOOD) {
+ printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start);
+ fflush(stdout);
+ return status;
+ }
+
+ printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start);
+ fflush(stdout);
+
+ if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) {
+ printf(": Erase failed (%s)\n", strerror(errno));
+ return ZONE_BAD_ORIGINAL;
+ }
+
+ if (do_rwecheck) {
+ printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start);
+ fflush(stdout);
+ status = check_block_2(block);
+ if (status != ZONE_GOOD) {
+ printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start);
+ fflush(stdout);
+ }
+ }
+ return status;
+}
+
+static int checkbbt(void)
+{
+ unsigned char bbt[512];
+ unsigned char bits;
+ int i, addr;
+
+ if (pread(fd, bbt, 512, 0x800) < 0) {
+ printf("nftl_format: failed to read BBT, errno=%d\n", errno);
+ return (-1);
+ }
+
+
+ for (i = 0; (i < 512); i++) {
+ addr = i / 4;
+ bits = 0x3 << ((i % 4) * 2);
+ if ((bbt[addr] & bits) == 0) {
+ BadUnitTable[i] = ZONE_BAD_ORIGINAL;
+ }
+ }
+
+ return (0);
+}
+
+void usage(int rc)
+{
+ fprintf(stderr, "Usage: nftl_format [-ib] <mtddevice> [<start offset> [<size>]]\n");
+ exit(rc);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned long startofs = 0, part_size = 0;
+ unsigned long ezones = 0, ezone = 0, bad_zones = 0;
+ unsigned char unit_factor = 0xFF;
+ long MediaUnit1 = -1, MediaUnit2 = -1;
+ long MediaUnitOff1 = 0, MediaUnitOff2 = 0;
+ unsigned char oobbuf[16];
+ struct mtd_oob_buf oob = {0, 16, oobbuf};
+ char *mtddevice, *nftl;
+ int c, do_inftl = 0, do_bbt = 0;
+
+
+ printf("$Id: nftl_format.c,v 1.24 2005/11/07 11:15:13 gleixner Exp $\n");
+
+ if (argc < 2)
+ usage(1);
+
+ nftl = "NFTL";
+
+ while ((c = getopt(argc, argv, "?hib")) > 0) {
+ switch (c) {
+ case 'i':
+ nftl = "INFTL";
+ do_inftl = 1;
+ break;
+ case 'b':
+ do_bbt = 1;
+ break;
+ case 'h':
+ case '?':
+ usage(0);
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+
+ mtddevice = argv[optind++];
+ if (argc > optind) {
+ startofs = strtoul(argv[optind++], NULL, 0);
+ }
+ if (argc > optind) {
+ part_size = strtoul(argv[optind++], NULL, 0);
+ }
+
+ // Open and size the device
+ if ((fd = open(mtddevice, O_RDWR)) < 0) {
+ perror("Open flash device");
+ return 1;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ close(fd);
+ return 1;
+ }
+
+ switch (meminfo.erasesize) {
+ case 0x1000:
+ case 0x2000:
+ case 0x4000:
+ case 0x8000:
+ break;
+ default:
+ printf("Unrecognized Erase size, 0x%x - I'm confused\n",
+ meminfo.erasesize);
+ close(fd);
+ return 1;
+ }
+ writebuf[0] = malloc(meminfo.erasesize * 5);
+ if (!writebuf[0]) {
+ printf("Malloc failed\n");
+ close(fd);
+ return 1;
+ }
+ writebuf[1] = writebuf[0] + meminfo.erasesize;
+ writebuf[2] = writebuf[1] + meminfo.erasesize;
+ writebuf[3] = writebuf[2] + meminfo.erasesize;
+ readbuf = writebuf[3] + meminfo.erasesize;
+ memset(writebuf[0], 0xff, meminfo.erasesize);
+ memset(writebuf[1], 0x00, meminfo.erasesize);
+ memset(writebuf[2], 0x5a, meminfo.erasesize);
+ memset(writebuf[3], 0xa5, meminfo.erasesize);
+ memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES);
+
+ if (part_size == 0 || (part_size > meminfo.size - startofs))
+ /* the user doest not or incorrectly specify NFTL partition size */
+ part_size = meminfo.size - startofs;
+
+ erase.length = meminfo.erasesize;
+ ezones = part_size / meminfo.erasesize;
+
+ if (ezones > MAX_ERASE_ZONES) {
+ /* Ought to change the UnitSizeFactor. But later. */
+ part_size = meminfo.erasesize * MAX_ERASE_ZONES;
+ ezones = MAX_ERASE_ZONES;
+ unit_factor = 0xFF;
+ }
+
+ /* If using device BBT then parse that now */
+ if (do_bbt) {
+ checkbbt();
+ do_oobcheck = 0;
+ do_rwecheck = 0;
+ }
+
+ /* Phase 1. Erasing and checking each erase zones in the NFTL partition.
+ N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */
+ printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n",
+ startofs, startofs + part_size);
+ for (ezone = startofs / meminfo.erasesize;
+ ezone < (ezones + startofs / meminfo.erasesize); ezone++) {
+ if (BadUnitTable[ezone] != ZONE_GOOD)
+ continue;
+ if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) {
+ if (MediaUnit1 == -1) {
+ MediaUnit1 = ezone;
+ } else if (MediaUnit2 == -1) {
+ MediaUnit2 = ezone;
+ }
+ } else {
+ bad_zones++;
+ }
+ }
+ printf("\n");
+
+ /* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used
+ by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */
+ if (do_inftl) {
+ unsigned long maxzones, pezstart, pezend, numvunits;
+
+ INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]);
+ strcpy(INFTLhdr->bootRecordID, "BNAND");
+ INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0);
+ INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0);
+ INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1);
+ INFTLhdr->BlockMultiplierBits = cpu_to_le32(0);
+ INFTLhdr->FormatFlags = cpu_to_le32(0);
+ INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION);
+ INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED);
+ /*
+ * Calculate number of virtual units we will have to work
+ * with. I am calculating out the known bad units here, not
+ * sure if that is what M-Systems do...
+ */
+ MediaUnit2 = MediaUnit1;
+ MediaUnitOff2 = 4096;
+ maxzones = meminfo.size / meminfo.erasesize;
+ pezstart = startofs / meminfo.erasesize + 1;
+ pezend = startofs / meminfo.erasesize + ezones - 1;
+ numvunits = (ezones - 2) * PERCENTUSED / 100;
+ for (ezone = pezstart; ezone < maxzones; ezone++) {
+ if (BadUnitTable[ezone] != ZONE_GOOD) {
+ if (numvunits > 1)
+ numvunits--;
+ }
+ }
+
+ INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits);
+ INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart);
+ INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend);
+ INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL);
+ INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0);
+ INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit;
+ INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0);
+
+ } else {
+
+ NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]);
+ strcpy(NFTLhdr->DataOrgID, "ANAND");
+ NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize);
+ NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1);
+ /* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */
+ NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize));
+ NFTLhdr->UnitSizeFactor = unit_factor;
+ }
+
+ /* Phase 2. Writing NFTL Media Headers and Bad Unit Table */
+ printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl);
+ pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1);
+ for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) {
+ pwrite(fd, BadUnitTable + ezone, 512,
+ (MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512));
+ }
+
+#if 0
+ printf(" MediaHeader contents:\n");
+ printf(" NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits));
+ printf(" FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN));
+ printf(" FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize),
+ le32_to_cpu(NFTLhdr->FormattedSize)/512);
+#endif
+ printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl);
+ pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2);
+ for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) {
+ pwrite(fd, BadUnitTable + ezone, 512,
+ (MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512));
+ }
+
+ /* UCI #1 for newly erased Erase Unit */
+ memset(oobbuf, 0xff, 16);
+ oobbuf[11] = oobbuf[10] = oobbuf[9] = 0;
+ oobbuf[8] = (do_inftl) ? 0x00 : 0x03;
+ oobbuf[12] = oobbuf[14] = 0x69;
+ oobbuf[13] = oobbuf[15] = 0x3c;
+
+ /* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit
+ by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0,
+ but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */
+ /* Phase 3. Writing Unit Control Information for each Erase Unit */
+ printf("Phase 3. Writing Unit Control Information to each Erase Unit\n");
+ for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) {
+ /* write UCI #1 to each Erase Unit */
+ if (BadUnitTable[ezone] != ZONE_GOOD)
+ continue;
+ oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512);
+ if (ioctl(fd, MEMWRITEOOB, &oob))
+ printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno));
+ }
+
+ exit(0);
+}
diff --git a/nftldump.c b/nftldump.c
new file mode 100644
index 0000000..fc97b58
--- /dev/null
+++ b/nftldump.c
@@ -0,0 +1,299 @@
+/*
+ * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk"
+ *
+ *
+ * $Id: nftldump.c,v 1.17 2005/11/07 11:15:13 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ToDo:
+ * 1. UnitSizeFactor != 0xFF cases
+ * 2. test, test, and test !!!
+ */
+
+#define _XOPEN_SOURCE 500 /* For pread */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <asm/types.h>
+#include <mtd/mtd-user.h>
+#include <mtd/nftl-user.h>
+
+static struct NFTLMediaHeader MedHead[2];
+static mtd_info_t meminfo;
+
+static struct nftl_oob oobbuf;
+static struct mtd_oob_buf oob = {0, 16, (unsigned char *)&oobbuf};
+
+static int fd, ofd = -1;;
+static int NumMedHeads;
+
+static unsigned char BadUnitTable[MAX_ERASE_ZONES];
+
+/* some byte swabbing stuff from include/linux/byteorder/ */
+#define swab16(x) \
+ ((__u16)( \
+ (((__u16)(x) & (__u16)0x00ffU) << 8) | \
+ (((__u16)(x) & (__u16)0xff00U) >> 8) ))
+#define swab32(x) \
+ ((__u32)( \
+ (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+ (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
+ (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
+ (((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(x) (x)
+#define cpu_to_le32(x) (x)
+#define SWAP16(x) do { ; } while(0)
+#define SWAP32(x) do { ; } while(0)
+#else
+#define cpu_to_le16(x) swab16(x)
+#define cpu_to_le32(x) swab32(x)
+#define SWAP16(x) do { x = swab16(x); } while(0)
+#define SWAP32(x) do { x = swab32(x); } while(0)
+#endif
+
+/* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */
+static unsigned short *VUCtable;
+
+/* FixMe: make this dynamic allocated */
+#define ERASESIZE 0x2000
+#define NUMVUNITS ((40*1024*1024) / ERASESIZE)
+static union nftl_uci UCItable[NUMVUNITS][3];
+
+static unsigned short nextEUN(unsigned short curEUN)
+{
+ return UCItable[curEUN][0].a.ReplUnitNum;
+}
+
+static unsigned int find_media_headers(void)
+{
+ int i;
+ static unsigned long ofs = 0;
+
+ NumMedHeads = 0;
+ while (ofs < meminfo.size) {
+ pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs);
+ if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) {
+ SWAP16(MedHead[NumMedHeads].NumEraseUnits);
+ SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN);
+ SWAP32(MedHead[NumMedHeads].FormattedSize);
+
+ if (NumMedHeads == 0) {
+ printf("NFTL Media Header found at offset 0x%08lx:\n", ofs);
+ printf("NumEraseUnits: %d\n",
+ MedHead[NumMedHeads].NumEraseUnits);
+ printf("FirstPhysicalEUN: %d\n",
+ MedHead[NumMedHeads].FirstPhysicalEUN);
+ printf("Formatted Size: %d\n",
+ MedHead[NumMedHeads].FormattedSize);
+ printf("UnitSizeFactor: 0x%x\n",
+ MedHead[NumMedHeads].UnitSizeFactor);
+
+ /* read BadUnitTable, I don't know why pread() does not work for
+ larger (7680 bytes) chunks */
+ for (i = 0; i < MAX_ERASE_ZONES; i += 512)
+ pread(fd, &BadUnitTable[i], 512, ofs + 512 + i);
+ } else
+ printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs);
+ NumMedHeads++;
+ }
+
+ ofs += meminfo.erasesize;
+ if (NumMedHeads == 2) {
+ if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) {
+ printf("warning: NFTL Media Header is not consistent with "
+ "Spare NFTL Media Header\n");
+ }
+ break;
+ }
+ }
+
+ /* allocate Virtual Unit Chain table for this NFTL partition */
+ VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short));
+ return NumMedHeads;
+}
+
+static void dump_erase_units(void)
+{
+ int i, j;
+ unsigned long ofs;
+
+ for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN +
+ MedHead[0].NumEraseUnits; i++) {
+ /* For each Erase Unit */
+ ofs = i * meminfo.erasesize;
+
+ /* read the Unit Control Information */
+ for (j = 0; j < 3; j++) {
+ oob.start = ofs + (j * 512);
+ if (ioctl(fd, MEMREADOOB, &oob))
+ printf("MEMREADOOB at %lx: %s\n",
+ (unsigned long) oob.start, strerror(errno));
+ memcpy(&UCItable[i][j], &oobbuf.u, 8);
+ }
+ if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) {
+ printf("EraseMark not present in unit %d: %x\n",
+ i, UCItable[i][1].b.EraseMark);
+ } else {
+ /* a properly formatted unit */
+ SWAP16(UCItable[i][0].a.VirtUnitNum);
+ SWAP16(UCItable[i][0].a.ReplUnitNum);
+ SWAP16(UCItable[i][0].a.SpareVirtUnitNum);
+ SWAP16(UCItable[i][0].a.SpareReplUnitNum);
+ SWAP32(UCItable[i][1].b.WearInfo);
+ SWAP16(UCItable[i][1].b.EraseMark);
+ SWAP16(UCItable[i][1].b.EraseMark1);
+ SWAP16(UCItable[i][2].c.FoldMark);
+ SWAP16(UCItable[i][2].c.FoldMark1);
+
+ if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) {
+ /* If this is the first in a chain, store the EUN in the VUC table */
+ if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) {
+ printf("Duplicate start of chain for VUC %d: "
+ "Unit %d replaces Unit %d\n",
+ UCItable[i][0].a.VirtUnitNum & 0x7fff,
+ i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]);
+ }
+ VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i;
+ }
+ }
+
+ switch (BadUnitTable[i]) {
+ case ZONE_BAD_ORIGINAL:
+ printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i);
+ continue;
+ case ZONE_BAD_MARKED:
+ printf("Unit %d is marked as ZONE_BAD_MARKED\n", i);
+ continue;
+ }
+
+ /* ZONE_GOOD */
+ if (UCItable[i][0].a.VirtUnitNum == 0xffff)
+ printf("Unit %d is free\n", i);
+ else
+ printf("Unit %d is in chain %d and %s a replacement\n", i,
+ UCItable[i][0].a.VirtUnitNum & 0x7fff,
+ UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not");
+ }
+}
+
+static void dump_virtual_units(void)
+{
+ int i, j;
+ char readbuf[512];
+
+ for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) {
+ unsigned short curEUN = VUCtable[i];
+
+ printf("Virtual Unit #%d: ", i);
+ if (!curEUN) {
+ printf("Not present\n");
+ continue;
+ }
+ printf("%d", curEUN);
+
+ /* walk through the Virtual Unit Chain */
+ while ((curEUN = nextEUN(curEUN)) != 0xffff) {
+ printf(", %d", curEUN & 0x7fff);
+ }
+ printf("\n");
+
+ if (ofd != -1) {
+ /* Actually write out the data */
+ for (j = 0; j < meminfo.erasesize / 512; j++) {
+ /* For each sector in the block */
+ unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i];
+ unsigned int status;
+
+ if (thisEUN == 0xffff) thisEUN = 0;
+
+ while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) {
+ oob.start = (thisEUN * ERASESIZE) + (j * 512);
+ ioctl(fd, MEMREADOOB, &oob);
+ status = oobbuf.b.Status | oobbuf.b.Status1;
+
+ switch (status) {
+ case SECTOR_FREE:
+ /* This is still free. Don't look any more */
+ thisEUN = 0;
+ break;
+
+ case SECTOR_USED:
+ /* SECTOR_USED. This is a good one. */
+ lastgoodEUN = thisEUN;
+ break;
+ }
+
+ /* Find the next erase unit in this chain, if any */
+ if (thisEUN)
+ thisEUN = nextEUN(thisEUN) & 0x7fff;
+ }
+
+ if (lastgoodEUN == 0xffff)
+ memset(readbuf, 0, 512);
+ else
+ pread(fd, readbuf, 512,
+ (lastgoodEUN * ERASESIZE) + (j * 512));
+
+ write(ofd, readbuf, 512);
+ }
+
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ printf("Usage: %s <device> [<outfile>]\n", argv[0]);
+ exit(1);
+ }
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ perror("open flash");
+ exit (1);
+ }
+
+ if (argc > 2) {
+ ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ if (ofd == -1)
+ perror ("open outfile");
+ }
+
+ /* get size information of the MTD device */
+ if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
+ perror("ioctl(MEMGETINFO)");
+ close(fd);
+ return 1;
+ }
+
+ while (find_media_headers() != 0) {
+ dump_erase_units();
+ dump_virtual_units();
+ free(VUCtable);
+ }
+
+ exit(0);
+}
diff --git a/rfddump.c b/rfddump.c
new file mode 100644
index 0000000..268e610
--- /dev/null
+++ b/rfddump.c
@@ -0,0 +1,338 @@
+/*
+ * rfddump.c
+ *
+ * Copyright (C) 2005 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: rfddump.c,v 1.3 2005/11/07 11:15:13 gleixner Exp $
+ */
+
+#define _XOPEN_SOURCE 500 /* For pread */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* next is an array of mapping for each corresponding sector */
+#define RFD_MAGIC 0x9193
+#define HEADER_MAP_OFFSET 3
+#define SECTOR_DELETED 0x0000
+#define SECTOR_ZERO 0xfffe
+#define SECTOR_FREE 0xffff
+
+#define SECTOR_SIZE 512
+
+#define SECTORS_PER_TRACK 63
+
+
+struct rfd {
+ int block_size;
+ int block_count;
+ int header_sectors;
+ int data_sectors;
+ int header_size;
+ __u16 *header;
+ int sector_count;
+ int *sector_map;
+ const char *mtd_filename;
+ const char *out_filename;
+ int verbose;
+};
+
+#define PROGRAM "rfddump"
+#define VERSION "$Revision 1.0 $"
+
+void display_help(void)
+{
+ printf("Usage: " PROGRAM " [OPTIONS] MTD-device filename\n"
+ "Dumps the contents of a resident flash disk\n"
+ "\n"
+ "-h --help display this help and exit\n"
+ "-V --version output version information and exit\n"
+ "-v --verbose Be verbose\n"
+ "-b size --blocksize Block size (defaults to erase unit)\n");
+ exit(0);
+}
+
+void display_version(void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+
+ exit(0);
+}
+
+void process_options(int argc, char *argv[], struct rfd *rfd)
+{
+ int error = 0;
+
+ rfd->block_size = 0;
+ rfd->verbose = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hvVb:";
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V', },
+ { "blocksize", required_argument, 0, 'b' },
+ { "verbose", no_argument, 0, 'v' },
+ { NULL, 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();
+ break;
+ case 'V':
+ display_version();
+ break;
+ case 'v':
+ rfd->verbose = 1;
+ break;
+ case 'b':
+ rfd->block_size = atoi(optarg);
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 2 || error)
+ display_help();
+
+ rfd->mtd_filename = argv[optind];
+ rfd->out_filename = argv[optind + 1];
+}
+
+int build_block_map(struct rfd *rfd, int fd, int block)
+{
+ int i;
+ int sectors;
+
+ if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size)
+ != rfd->header_size) {
+ return -1;
+ }
+
+ if (__le16_to_cpu(rfd->header[0]) != RFD_MAGIC) {
+ if (rfd->verbose)
+ printf("Block #%02d: Magic missing\n", block);
+
+ return 0;
+ }
+
+ sectors = 0;
+ for (i=0; i<rfd->data_sectors; i++) {
+ __u16 entry = __le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]);
+
+ if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
+ continue;
+
+ if (entry == SECTOR_ZERO)
+ entry = 0;
+
+ if (entry >= rfd->sector_count) {
+ fprintf(stderr, "%s: warning: sector %d out of range\n",
+ rfd->mtd_filename, entry);
+ continue;
+ }
+
+ if (rfd->sector_map[entry] != -1) {
+ fprintf(stderr, "%s: warning: more than one entry "
+ "for sector %d\n", rfd->mtd_filename, entry);
+ continue;
+ }
+
+ rfd->sector_map[entry] = rfd->block_size * block +
+ (i + rfd->header_sectors) * SECTOR_SIZE;
+ sectors++;
+ }
+
+ if (rfd->verbose)
+ printf("Block #%02d: %d sectors\n", block, sectors);
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, sectors_per_block;
+ mtd_info_t mtd_info;
+ struct rfd rfd;
+ int i, blocks_found;
+ int out_fd = 0;
+ __u8 sector[512];
+ int blank, rc, cylinders;
+
+ process_options(argc, argv, &rfd);
+
+ fd = open(rfd.mtd_filename, O_RDONLY);
+ if (fd == -1) {
+ perror(rfd.mtd_filename);
+ return 1;
+ }
+
+ if (rfd.block_size == 0) {
+ if (ioctl(fd, MEMGETINFO, &mtd_info)) {
+ perror(rfd.mtd_filename);
+ close(fd);
+ return 1;
+ }
+
+ if (mtd_info.type != MTD_NORFLASH) {
+ fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename);
+ close(fd);
+ return 2;
+ }
+
+ sectors_per_block = mtd_info.erasesize / SECTOR_SIZE;
+
+ rfd.block_size = mtd_info.erasesize;
+ rfd.block_count = mtd_info.size / mtd_info.erasesize;
+ } else {
+ struct stat st;
+
+ if (fstat(fd, &st) == -1) {
+ perror(rfd.mtd_filename);
+ close(fd);
+ return 1;
+ }
+
+ if (st.st_size % SECTOR_SIZE)
+ fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename);
+
+ sectors_per_block = rfd.block_size / SECTOR_SIZE;
+
+ if (st.st_size % rfd.block_size)
+ fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename);
+
+ rfd.block_count = st.st_size / rfd.block_size;
+
+ if (!rfd.block_count) {
+ fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename);
+ close(fd);
+ return 2;
+ }
+ }
+
+ rfd.header_sectors =
+ ((HEADER_MAP_OFFSET + sectors_per_block) *
+ sizeof(__u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ rfd.data_sectors = sectors_per_block - rfd.header_sectors;
+ cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1)
+ / SECTORS_PER_TRACK;
+ rfd.sector_count = cylinders * SECTORS_PER_TRACK;
+ rfd.header_size =
+ (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(__u16);
+
+ rfd.header = malloc(rfd.header_size);
+ if (!rfd.header) {
+ perror(PROGRAM);
+ close(fd);
+ return 2;
+ }
+ rfd.sector_map = malloc(rfd.sector_count * sizeof(int));
+ if (!rfd.sector_map) {
+ perror(PROGRAM);
+ close(fd);
+ free(rfd.sector_map);
+ return 2;
+ }
+
+ rfd.mtd_filename = rfd.mtd_filename;
+
+ for (i=0; i<rfd.sector_count; i++)
+ rfd.sector_map[i] = -1;
+
+ for (blocks_found=i=0; i<rfd.block_count; i++) {
+ rc = build_block_map(&rfd, fd, i);
+ if (rc > 0)
+ blocks_found++;
+ if (rc < 0)
+ goto err;
+ }
+
+ if (!blocks_found) {
+ fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] != -1)
+ break;
+ }
+
+ if (i == rfd.sector_count) {
+ fprintf(stderr, "%s: no sectors found\n", rfd.mtd_filename);
+ goto err;
+ }
+
+ out_fd = open(rfd.out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+ if (out_fd == -1) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+
+ blank = 0;
+ for (i=0; i<rfd.sector_count; i++) {
+ if (rfd.sector_map[i] == -1) {
+ memset(sector, 0, SECTOR_SIZE);
+ blank++;
+ } else {
+ if (pread(fd, sector, SECTOR_SIZE, rfd.sector_map[i])
+ != SECTOR_SIZE) {
+ perror(rfd.mtd_filename);
+ goto err;
+ }
+ }
+
+ if (write(out_fd, sector, SECTOR_SIZE) != SECTOR_SIZE) {
+ perror(rfd.out_filename);
+ goto err;
+ }
+ }
+
+ if (rfd.verbose)
+ printf("Copied %d sectors (%d blank)\n", rfd.sector_count, blank);
+
+ close(out_fd);
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 0;
+
+err:
+ if (out_fd)
+ close(out_fd);
+
+ close(fd);
+ free(rfd.header);
+ free(rfd.sector_map);
+
+ return 2;
+}
+
diff --git a/rfdformat.c b/rfdformat.c
new file mode 100644
index 0000000..ee84a21
--- /dev/null
+++ b/rfdformat.c
@@ -0,0 +1,160 @@
+/*
+ * rfdformat.c
+ *
+ * Copyright (C) 2005 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: rfdformat.c,v 1.3 2005/11/07 11:15:14 gleixner Exp $
+ *
+ * This is very easy: just erase all the blocks and put the magic at
+ * the beginning of each block.
+ */
+
+#define _XOPEN_SOURCE 500 /* For pread/pwrite */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <mtd/mtd-user.h>
+#include <linux/types.h>
+
+#define PROGRAM "rfdformat"
+#define VERSION "$Revision 1.0 $"
+
+void display_help(void)
+{
+ printf("Usage: " PROGRAM " [OPTIONS] MTD-device\n"
+ "Formats NOR flash for resident flash disk\n"
+ "\n"
+ "-h --help display this help and exit\n"
+ "-V --version output version information and exit\n");
+ exit(0);
+}
+
+void display_version(void)
+{
+ printf(PROGRAM " " VERSION "\n"
+ "\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+
+ exit(0);
+}
+
+void process_options(int argc, char *argv[], const char **mtd_filename)
+{
+ int error = 0;
+
+ for (;;) {
+ int option_index = 0;
+ static const char *short_options = "hV";
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V', },
+ { NULL, 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();
+ break;
+ case 'V':
+ display_version();
+ break;
+ case '?':
+ error = 1;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1 || error)
+ display_help();
+
+ *mtd_filename = argv[optind];
+}
+
+int main(int argc, char *argv[])
+{
+ static const __u8 magic[] = { 0x93, 0x91 };
+ int fd, block_count, i;
+ struct mtd_info_user mtd_info;
+ char buf[512];
+ const char *mtd_filename;
+
+ process_options(argc, argv, &mtd_filename);
+
+ fd = open(mtd_filename, O_RDWR);
+ if (fd == -1) {
+ perror(mtd_filename);
+ return 1;
+ }
+
+ if (ioctl(fd, MEMGETINFO, &mtd_info)) {
+ perror(mtd_filename);
+ close(fd);
+ return 1;
+ }
+
+ if (mtd_info.type != MTD_NORFLASH) {
+ fprintf(stderr, "%s: not NOR flash\n", mtd_filename);
+ close(fd);
+ return 2;
+ }
+
+ if (mtd_info.size > 32*1024*1024) {
+ fprintf(stderr, "%s: flash larger than 32MiB not supported\n",
+ mtd_filename);
+ close(fd);
+ return 2;
+ }
+
+ block_count = mtd_info.size / mtd_info.erasesize;
+
+ if (block_count < 2) {
+ fprintf(stderr, "%s: at least two erase units required\n",
+ mtd_filename);
+ close(fd);
+ return 2;
+ }
+
+ for (i=0; i<block_count; i++) {
+ struct erase_info_user erase_info;
+
+ erase_info.start = i * mtd_info.erasesize;
+ erase_info.length = mtd_info.erasesize;
+
+ if (ioctl(fd, MEMERASE, &erase_info) != 0) {
+ snprintf(buf, sizeof(buf), "%s: erase", mtd_filename);
+ perror(buf);
+ close(fd);
+ return 2;
+ }
+
+ if (pwrite(fd, magic, sizeof(magic), i * mtd_info.erasesize)
+ != sizeof(magic)) {
+ snprintf(buf, sizeof(buf), "%s: write", mtd_filename);
+ perror(buf);
+ close(fd);
+ return 2;
+ }
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/summary.h b/summary.h
new file mode 100644
index 0000000..198597f
--- /dev/null
+++ b/summary.h
@@ -0,0 +1,143 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ * Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Patrik Kluba <pajko@halom.u-szeged.hu>,
+ * University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: summary.h,v 1.7 2006/02/15 09:42:34 havasi Exp $
+ *
+ */
+
+#ifndef JFFS2_SUMMARY_H
+#define JFFS2_SUMMARY_H
+
+#include <linux/uio.h>
+#include <linux/jffs2.h>
+
+#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->dirty_size += _x; \
+ jeb->free_size -= _x ; jeb->dirty_size += _x; \
+ }while(0)
+#define USED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->used_size += _x; \
+ jeb->free_size -= _x ; jeb->used_size += _x; \
+ }while(0)
+#define WASTED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->wasted_size += _x; \
+ jeb->free_size -= _x ; jeb->wasted_size += _x; \
+ }while(0)
+#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->unchecked_size += _x; \
+ jeb->free_size -= _x ; jeb->unchecked_size += _x; \
+ }while(0)
+
+#define BLK_STATE_ALLFF 0
+#define BLK_STATE_CLEAN 1
+#define BLK_STATE_PARTDIRTY 2
+#define BLK_STATE_CLEANMARKER 3
+#define BLK_STATE_ALLDIRTY 4
+#define BLK_STATE_BADBLOCK 5
+
+#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
+#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
+#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+
+/* Summary structures used on flash */
+
+struct jffs2_sum_unknown_flash
+{
+ jint16_t nodetype; /* node type */
+} __attribute__((packed));
+
+struct jffs2_sum_inode_flash
+{
+ jint16_t nodetype; /* node type */
+ jint32_t inode; /* inode number */
+ jint32_t version; /* inode version */
+ jint32_t offset; /* offset on jeb */
+ jint32_t totlen; /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_flash
+{
+ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
+ jint32_t totlen; /* record length */
+ jint32_t offset; /* ofset on jeb */
+ jint32_t pino; /* parent inode */
+ jint32_t version; /* dirent version */
+ jint32_t ino; /* == zero for unlink */
+ uint8_t nsize; /* dirent name size */
+ uint8_t type; /* dirent type */
+ uint8_t name[0]; /* dirent name */
+} __attribute__((packed));
+
+union jffs2_sum_flash
+{
+ struct jffs2_sum_unknown_flash u;
+ struct jffs2_sum_inode_flash i;
+ struct jffs2_sum_dirent_flash d;
+};
+
+/* Summary structures used in the memory */
+
+struct jffs2_sum_unknown_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype; /* node type */
+} __attribute__((packed));
+
+struct jffs2_sum_inode_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype; /* node type */
+ jint32_t inode; /* inode number */
+ jint32_t version; /* inode version */
+ jint32_t offset; /* offset on jeb */
+ jint32_t totlen; /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
+ jint32_t totlen; /* record length */
+ jint32_t offset; /* ofset on jeb */
+ jint32_t pino; /* parent inode */
+ jint32_t version; /* dirent version */
+ jint32_t ino; /* == zero for unlink */
+ uint8_t nsize; /* dirent name size */
+ uint8_t type; /* dirent type */
+ uint8_t name[0]; /* dirent name */
+} __attribute__((packed));
+
+union jffs2_sum_mem
+{
+ struct jffs2_sum_unknown_mem u;
+ struct jffs2_sum_inode_mem i;
+ struct jffs2_sum_dirent_mem d;
+};
+
+struct jffs2_summary
+{
+ uint32_t sum_size;
+ uint32_t sum_num;
+ uint32_t sum_padded;
+ union jffs2_sum_mem *sum_list_head;
+ union jffs2_sum_mem *sum_list_tail;
+};
+
+/* Summary marker is stored at the end of every sumarized erase block */
+
+struct jffs2_sum_marker
+{
+ jint32_t offset; /* offset of the summary node in the jeb */
+ jint32_t magic; /* == JFFS2_SUM_MAGIC */
+};
+
+#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_raw_summary) + sizeof(struct jffs2_sum_marker))
+
+#endif
diff --git a/sumtool.c b/sumtool.c
new file mode 100644
index 0000000..b2e5d2e
--- /dev/null
+++ b/sumtool.c
@@ -0,0 +1,822 @@
+/*
+ * sumtool.c
+ *
+ * Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>,
+ * Ferenc Havasi <havasi@inf.u-szeged.hu>
+ * University of Szeged, Hungary
+ *
+ * $Id: sumtool.c,v 1.9 2006/01/23 08:22:45 havasi Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Overview:
+ * This is a utility insert summary information into JFFS2 image for
+ * faster mount time
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.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 PAD(x) (((x)+3)&~3)
+
+static const char *const app_name = "sumtool";
+
+static struct jffs2_summary *sum_collected = NULL;
+
+static int verbose = 0;
+static int padto = 0; /* pad the output with 0xFF to the end of the final eraseblock */
+static int add_cleanmarkers = 1; /* add cleanmarker to output */
+static int use_input_cleanmarker_size = 1; /* use input file's cleanmarker size (default) */
+static int found_cleanmarkers = 0; /* cleanmarker found in input file */
+static struct jffs2_unknown_node cleanmarker;
+static int cleanmarker_size = sizeof(cleanmarker);
+static const char *short_options = "o:i:e:hvVblnc:p";
+static int erase_block_size = 65536;
+static int out_fd = -1;
+static int in_fd = -1;
+
+static uint8_t *data_buffer = NULL; /* buffer for inodes */
+static unsigned int data_ofs = 0; /* inode buffer offset */
+
+static uint8_t *file_buffer = NULL; /* file buffer contains the actual erase block*/
+static unsigned int file_ofs = 0; /* position in the buffer */
+
+int target_endian = __BYTE_ORDER;
+
+static struct option long_options[] = {
+ {"output", 1, NULL, 'o'},
+ {"input", 1, NULL, 'i'},
+ {"eraseblock", 1, NULL, 'e'},
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"version", 0, NULL, 'V'},
+ {"bigendian", 0, NULL, 'b'},
+ {"littleendian", 0, NULL, 'l'},
+ {"no-cleanmarkers", 0, NULL, 'n'},
+ {"cleanmarker", 1, NULL, 'c'},
+ {"pad", 0, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+};
+
+static char *helptext =
+ "Usage: sumtool [OPTIONS] -i inputfile -o outputfile\n\n"
+ "Convert the input JFFS2 image to a summarized JFFS2 image\n"
+ "Summary makes mounting faster - if summary support enabled in your kernel\n\n"
+ "Options:\n"
+ " -e, --eraseblock=SIZE Use erase block size SIZE (default: 64KiB)\n"
+ " (usually 16KiB on NAND)\n"
+ " -c, --cleanmarker=SIZE Size of cleanmarker (default 12).\n"
+ " (usually 16 bytes on NAND, and will be set to\n"
+ " this value if left at the default 12). Will be\n"
+ " stored in OOB after each physical page composing\n"
+ " a physical eraseblock.\n"
+ " -n, --no-cleanmarkers Don't add a cleanmarker to every eraseblock\n"
+ " -o, --output=FILE Output to FILE \n"
+ " -i, --input=FILE Input from FILE \n"
+ " -b, --bigendian Image is big endian\n"
+ " -l --littleendian Image is little endian\n"
+ " -h, --help Display this help text\n"
+ " -v, --verbose Verbose operation\n"
+ " -V, --version Display version information\n"
+ " -p, --pad Pad the OUTPUT with 0xFF to the end of the final\n"
+ " eraseblock\n\n";
+
+
+static char *revtext = "$Revision: 1.9 $";
+
+static unsigned char ffbuf[16] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void verror_msg(const char *s, va_list p)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s: ", app_name);
+ vfprintf(stderr, s, p);
+}
+
+static void error_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ verror_msg(s, p);
+ va_end(p);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+static void vperror_msg(const char *s, va_list p)
+{
+ int err = errno;
+
+ if (s == 0)
+ s = "";
+ verror_msg(s, p);
+ if (*s)
+ s = ": ";
+ fprintf(stderr, "%s%s\n", s, strerror(err));
+}
+
+static void perror_msg_and_die(const char *s, ...)
+{
+ va_list p;
+
+ va_start(p, s);
+ vperror_msg(s, p);
+ va_end(p);
+ exit(EXIT_FAILURE);
+}
+
+
+
+static void full_write(void *target_buff, const void *buf, int len);
+
+void setup_cleanmarker()
+{
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.totlen = cpu_to_je32(cleanmarker_size);
+ cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4));
+}
+
+void process_options (int argc, char **argv)
+{
+ int opt,c;
+
+ while ((opt = getopt_long(argc, argv, short_options, long_options, &c)) >= 0) {
+ switch (opt) {
+ case 'o':
+ if (out_fd != -1)
+ error_msg_and_die("output filename specified more than once");
+ out_fd = open(optarg, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (out_fd == -1)
+ perror_msg_and_die("open output file");
+ break;
+
+ case 'i':
+ if (in_fd != -1)
+ error_msg_and_die("input filename specified more than once");
+ in_fd = open(optarg, O_RDONLY);
+ if (in_fd == -1)
+ perror_msg_and_die("open input file");
+ break;
+ case 'b':
+ target_endian = __BIG_ENDIAN;
+ break;
+ case 'l':
+ target_endian = __LITTLE_ENDIAN;
+ break;
+ case 'h':
+ case '?':
+ error_msg_and_die(helptext);
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ error_msg_and_die("revision %.*s\n",
+ (int) strlen(revtext) - 13, revtext + 11);
+
+ case 'e': {
+ char *next;
+ unsigned units = 0;
+ erase_block_size = strtol(optarg, &next, 0);
+ if (!erase_block_size)
+ error_msg_and_die("Unrecognisable erase size\n");
+
+ if (*next) {
+ if (!strcmp(next, "KiB")) {
+ units = 1024;
+ } else if (!strcmp(next, "MiB")) {
+ units = 1024 * 1024;
+ } else {
+ error_msg_and_die("Unknown units in erasesize\n");
+ }
+ } else {
+ if (erase_block_size < 0x1000)
+ units = 1024;
+ else
+ units = 1;
+ }
+ erase_block_size *= units;
+
+ /* If it's less than 8KiB, they're not allowed */
+ if (erase_block_size < 0x2000) {
+ fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
+ erase_block_size);
+ erase_block_size = 0x2000;
+ }
+ break;
+ }
+
+ case 'n':
+ add_cleanmarkers = 0;
+ break;
+ case 'c':
+ cleanmarker_size = strtol(optarg, NULL, 0);
+
+ if (cleanmarker_size < sizeof(cleanmarker)) {
+ error_msg_and_die("cleanmarker size must be >= 12");
+ }
+ if (cleanmarker_size >= erase_block_size) {
+ error_msg_and_die("cleanmarker size must be < eraseblock size");
+ }
+
+ use_input_cleanmarker_size = 0;
+ found_cleanmarkers = 1;
+ setup_cleanmarker();
+
+ break;
+ case 'p':
+ padto = 1;
+ break;
+ }
+ }
+}
+
+
+void init_buffers()
+{
+ data_buffer = malloc(erase_block_size);
+
+ if (!data_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+
+ file_buffer = malloc(erase_block_size);
+
+ if (!file_buffer) {
+ perror("out of memory");
+ close (in_fd);
+ close (out_fd);
+ exit(1);
+ }
+}
+
+void init_sumlist()
+{
+ sum_collected = (struct jffs2_summary *) malloc (sizeof(struct jffs2_summary));
+
+ if (!sum_collected)
+ error_msg_and_die("Can't allocate memory for jffs2_summary!\n");
+
+ memset(sum_collected, 0, sizeof(struct jffs2_summary));
+}
+
+void clean_buffers()
+{
+ if (data_buffer)
+ free(data_buffer);
+ if (file_buffer)
+ free(file_buffer);
+}
+
+void clean_sumlist()
+{
+ union jffs2_sum_mem *temp;
+
+ if (sum_collected) {
+
+ while (sum_collected->sum_list_head) {
+ temp = sum_collected->sum_list_head;
+ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next;
+ free(temp);
+ sum_collected->sum_num--;
+ }
+
+ if (sum_collected->sum_num != 0)
+ printf("Ooops, something wrong happened! sum_num != 0, but sum_list = null ???");
+
+ free(sum_collected);
+ }
+}
+
+int load_next_block()
+{
+ int ret;
+ ret = read(in_fd, file_buffer, erase_block_size);
+ file_ofs = 0;
+
+ if (verbose)
+ printf("Load next block : %d bytes read\n",ret);
+
+ return ret;
+}
+
+void write_buff_to_file()
+{
+ int ret;
+ int len = data_ofs;
+
+ uint8_t *buf = NULL;
+
+ buf = data_buffer;
+ while (len > 0) {
+ ret = write(out_fd, buf, len);
+
+ if (ret < 0)
+ perror_msg_and_die("write");
+
+ if (ret == 0)
+ perror_msg_and_die("write returned zero");
+
+ len -= ret;
+ buf += ret;
+ }
+
+ data_ofs = 0;
+}
+
+void dump_sum_records()
+{
+
+ struct jffs2_raw_summary isum;
+ struct jffs2_sum_marker *sm;
+ union jffs2_sum_mem *temp;
+ jint32_t offset;
+ jint32_t *tpage;
+ void *wpage;
+ int datasize, infosize, padsize;
+ jint32_t magic = cpu_to_je32(JFFS2_SUM_MAGIC);
+
+ if (!sum_collected->sum_num || !sum_collected->sum_list_head)
+ return;
+
+ datasize = sum_collected->sum_size + sizeof(struct jffs2_sum_marker);
+ infosize = sizeof(struct jffs2_raw_summary) + datasize;
+ padsize = erase_block_size - data_ofs - infosize;
+ infosize += padsize; datasize += padsize;
+ offset = cpu_to_je32(data_ofs);
+
+ tpage = (jint32_t *) malloc(datasize);
+
+ if(!tpage)
+ error_msg_and_die("Can't allocate memory to dump summary information!\n");
+
+ memset(tpage, 0xff, datasize);
+ memset(&isum, 0, sizeof(isum));
+
+ isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
+ isum.totlen = cpu_to_je32(infosize);
+ isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4));
+ isum.padded = cpu_to_je32(0);
+
+ if (add_cleanmarkers && found_cleanmarkers) {
+ isum.cln_mkr = cpu_to_je32(cleanmarker_size);
+ } else {
+ isum.cln_mkr = cpu_to_je32(0);
+ }
+
+ isum.sum_num = cpu_to_je32(sum_collected->sum_num);
+ wpage = tpage;
+
+ while (sum_collected->sum_num) {
+ switch(je16_to_cpu(sum_collected->sum_list_head->u.nodetype)) {
+
+ case JFFS2_NODETYPE_INODE : {
+ struct jffs2_sum_inode_flash *sino_ptr = wpage;
+
+ sino_ptr->nodetype = sum_collected->sum_list_head->i.nodetype;
+ sino_ptr->inode = sum_collected->sum_list_head->i.inode;
+ sino_ptr->version = sum_collected->sum_list_head->i.version;
+ sino_ptr->offset = sum_collected->sum_list_head->i.offset;
+ sino_ptr->totlen = sum_collected->sum_list_head->i.totlen;
+
+ wpage += JFFS2_SUMMARY_INODE_SIZE;
+ break;
+ }
+
+ case JFFS2_NODETYPE_DIRENT : {
+ struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
+
+ sdrnt_ptr->nodetype = sum_collected->sum_list_head->d.nodetype;
+ sdrnt_ptr->totlen = sum_collected->sum_list_head->d.totlen;
+ sdrnt_ptr->offset = sum_collected->sum_list_head->d.offset;
+ sdrnt_ptr->pino = sum_collected->sum_list_head->d.pino;
+ sdrnt_ptr->version = sum_collected->sum_list_head->d.version;
+ sdrnt_ptr->ino = sum_collected->sum_list_head->d.ino;
+ sdrnt_ptr->nsize = sum_collected->sum_list_head->d.nsize;
+ sdrnt_ptr->type = sum_collected->sum_list_head->d.type;
+
+ memcpy(sdrnt_ptr->name, sum_collected->sum_list_head->d.name,
+ sum_collected->sum_list_head->d.nsize);
+
+ wpage += JFFS2_SUMMARY_DIRENT_SIZE(sum_collected->sum_list_head->d.nsize);
+ break;
+ }
+
+ default : {
+ printf("Unknown node type!\n");
+ }
+ }
+
+ temp = sum_collected->sum_list_head;
+ sum_collected->sum_list_head = sum_collected->sum_list_head->u.next;
+ free(temp);
+
+ sum_collected->sum_num--;
+ }
+
+ sum_collected->sum_size = 0;
+ sum_collected->sum_num = 0;
+ sum_collected->sum_list_tail = NULL;
+
+ wpage += padsize;
+
+ sm = wpage;
+ sm->offset = offset;
+ sm->magic = magic;
+
+ isum.sum_crc = cpu_to_je32(crc32(0, tpage, datasize));
+ isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8));
+
+ full_write(data_buffer + data_ofs, &isum, sizeof(isum));
+ full_write(data_buffer + data_ofs, tpage, datasize);
+
+ free(tpage);
+}
+
+static void full_write(void *target_buff, const void *buf, int len)
+{
+ memcpy(target_buff, buf, len);
+ data_ofs += len;
+}
+
+static void pad(int req)
+{
+ while (req) {
+ if (req > sizeof(ffbuf)) {
+ full_write(data_buffer + data_ofs, ffbuf, sizeof(ffbuf));
+ req -= sizeof(ffbuf);
+ } else {
+ full_write(data_buffer + data_ofs, ffbuf, req);
+ req = 0;
+ }
+ }
+}
+
+static inline void padword()
+{
+ if (data_ofs % 4)
+ full_write(data_buffer + data_ofs, ffbuf, 4 - (data_ofs % 4));
+}
+
+
+static inline void pad_block_if_less_than(int req,int plus)
+{
+
+ int datasize = req + plus + sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ if (data_ofs + req > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ }
+
+ if (add_cleanmarkers && found_cleanmarkers) {
+ if (!data_ofs) {
+ full_write(data_buffer, &cleanmarker, sizeof(cleanmarker));
+ pad(cleanmarker_size - sizeof(cleanmarker));
+ padword();
+ }
+ }
+}
+
+void flush_buffers()
+{
+
+ if ((add_cleanmarkers == 1) && (found_cleanmarkers == 1)) { /* CLEANMARKER */
+ if (data_ofs != cleanmarker_size) { /* INODE BUFFER */
+
+ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ /* If we have a full inode buffer, then write out inode and summary data */
+ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ } else { /* else just write out inode data */
+ if (padto)
+ pad(erase_block_size - data_ofs);
+ write_buff_to_file();
+ }
+ }
+ } else { /* NO CLEANMARKER */
+ if (data_ofs != 0) { /* INODE BUFFER */
+
+ int datasize = sum_collected->sum_size + sizeof(struct jffs2_raw_summary) + 8;
+ datasize += (4 - (datasize % 4)) % 4;
+
+ /* If we have a full inode buffer, then write out inode and summary data */
+ if (data_ofs + sizeof(struct jffs2_raw_inode) + 2*JFFS2_MIN_DATA_LEN > erase_block_size - datasize) {
+ dump_sum_records();
+ write_buff_to_file();
+ } else { /* Else just write out inode data */
+ if(padto)
+ pad(erase_block_size - data_ofs);
+ write_buff_to_file();
+ }
+ }
+ }
+}
+
+int add_sum_mem(union jffs2_sum_mem *item)
+{
+
+ if (!sum_collected->sum_list_head)
+ sum_collected->sum_list_head = (union jffs2_sum_mem *) item;
+ if (sum_collected->sum_list_tail)
+ sum_collected->sum_list_tail->u.next = (union jffs2_sum_mem *) item;
+ sum_collected->sum_list_tail = (union jffs2_sum_mem *) item;
+
+ switch (je16_to_cpu(item->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ sum_collected->sum_size += JFFS2_SUMMARY_INODE_SIZE;
+ sum_collected->sum_num++;
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ sum_collected->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize);
+ sum_collected->sum_num++;
+ break;
+
+ default:
+ error_msg_and_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype));
+ }
+ return 0;
+}
+
+void add_sum_inode_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_inode_mem *temp = (struct jffs2_sum_inode_mem *) malloc(sizeof(struct jffs2_sum_inode_mem));
+
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->i.nodetype;
+ temp->inode = node->i.ino;
+ temp->version = node->i.version;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->totlen = node->i.totlen;
+ temp->next = NULL;
+
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
+void add_sum_dirent_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_dirent_mem *temp = (struct jffs2_sum_dirent_mem *)
+ malloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize);
+
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->d.nodetype;
+ temp->totlen = node->d.totlen;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->pino = node->d.pino;
+ temp->version = node->d.version;
+ temp->ino = node->d.ino;
+ temp->nsize = node->d.nsize;
+ temp->type = node->d.type;
+ temp->next = NULL;
+
+ memcpy(temp->name,node->d.name,node->d.nsize);
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
+void write_dirent_to_buff(union jffs2_node_union *node)
+{
+ pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize));
+ add_sum_dirent_mem(node);
+ full_write(data_buffer + data_ofs, &(node->d), je32_to_cpu (node->d.totlen));
+ padword();
+}
+
+
+void write_inode_to_buff(union jffs2_node_union *node)
+{
+ pad_block_if_less_than(je32_to_cpu (node->i.totlen),JFFS2_SUMMARY_INODE_SIZE);
+ add_sum_inode_mem(node); /* Add inode summary mem to summary list */
+ full_write(data_buffer + data_ofs, &(node->i), je32_to_cpu (node->i.totlen)); /* Write out the inode to inode_buffer */
+ padword();
+}
+
+void create_summed_image(int inp_size)
+{
+ uint8_t *p = file_buffer;
+ union jffs2_node_union *node;
+ uint32_t crc;
+ uint16_t type;
+ int bitchbitmask = 0;
+ int obsolete;
+ char name[256];
+
+ while ( p < (file_buffer + inp_size)) {
+
+ node = (union jffs2_node_union *) p;
+
+ /* Skip empty space */
+ if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) {
+ p += 4;
+ continue;
+ }
+
+ if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) {
+ if (!bitchbitmask++)
+ printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - file_buffer, je16_to_cpu (node->u.magic));
+ p += 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;
+ }
+
+ 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 - file_buffer, je32_to_cpu (node->u.hdr_crc), crc);
+ p += 4;
+ continue;
+ }
+
+ switch(je16_to_cpu(node->u.nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+ if (verbose)
+ 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 - file_buffer, 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 - file_buffer, je32_to_cpu (node->i.node_crc), crc);
+ p += 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 - file_buffer, je32_to_cpu (node->i.data_crc), crc);
+ p += PAD(je32_to_cpu (node->i.totlen));
+ continue;
+ }
+
+ write_inode_to_buff(node);
+
+ 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;
+
+ if (verbose)
+ printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, 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 - file_buffer, je32_to_cpu (node->d.node_crc), crc);
+ p += 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 - file_buffer, je32_to_cpu (node->d.name_crc), crc);
+ p += PAD(je32_to_cpu (node->d.totlen));
+ continue;
+ }
+
+ write_dirent_to_buff(node);
+
+ p += PAD(je32_to_cpu (node->d.totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (verbose) {
+ printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ if (!found_cleanmarkers) {
+ found_cleanmarkers = 1;
+
+ if (add_cleanmarkers == 1 && use_input_cleanmarker_size == 1){
+ cleanmarker_size = je32_to_cpu (node->u.totlen);
+ setup_cleanmarker();
+ }
+ }
+
+ 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 - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+ p += PAD(je32_to_cpu (node->u.totlen));
+ break;
+
+ case 0xffff:
+ p += 4;
+ break;
+
+ default:
+ if (verbose) {
+ printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->u.totlen));
+ }
+
+ p += PAD(je32_to_cpu (node->u.totlen));
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ process_options(argc,argv);
+
+ if ((in_fd == -1) || (out_fd == -1)) {
+ if(in_fd != -1)
+ close(in_fd);
+ if(out_fd != -1)
+ close(out_fd);
+ fprintf(stderr,helptext);
+ error_msg_and_die("You must specify input and output files!\n");
+ }
+
+ init_buffers();
+ init_sumlist();
+
+ while ((ret = load_next_block())) {
+ create_summed_image(ret);
+ }
+
+ flush_buffers();
+ clean_buffers();
+ clean_sumlist();
+
+ if (in_fd != -1)
+ close(in_fd);
+ if (out_fd != -1)
+ close(out_fd);
+
+ return 0;
+}