diff options
-rwxr-xr-x | MAKEDEV | 43 | ||||
-rw-r--r-- | Makefile | 63 | ||||
-rw-r--r-- | Makefile.am | 18 | ||||
-rw-r--r-- | checkfs/Makefile | 14 | ||||
-rw-r--r-- | checkfs/README | 173 | ||||
-rw-r--r-- | checkfs/checkfs.c | 695 | ||||
-rw-r--r-- | checkfs/comm.c | 67 | ||||
-rw-r--r-- | checkfs/common.h | 7 | ||||
-rw-r--r-- | checkfs/makefiles.c | 264 | ||||
-rw-r--r-- | compr.c | 489 | ||||
-rw-r--r-- | compr.h | 115 | ||||
-rw-r--r-- | compr_rtime.c | 122 | ||||
-rw-r--r-- | compr_zlib.c | 148 | ||||
-rw-r--r-- | crc32.c | 97 | ||||
-rw-r--r-- | crc32.h | 21 | ||||
-rw-r--r-- | device_table.txt | 129 | ||||
-rw-r--r-- | doc_loadbios.c | 149 | ||||
-rw-r--r-- | docfdisk.c | 340 | ||||
-rw-r--r-- | flash_erase.c | 188 | ||||
-rw-r--r-- | flash_eraseall.c | 308 | ||||
-rw-r--r-- | flash_info.c | 55 | ||||
-rw-r--r-- | flash_lock.c | 84 | ||||
-rw-r--r-- | flash_otp_dump.c | 54 | ||||
-rw-r--r-- | flash_otp_info.c | 63 | ||||
-rw-r--r-- | flash_otp_lock.c | 70 | ||||
-rw-r--r-- | flash_otp_write.c | 96 | ||||
-rw-r--r-- | flash_unlock.c | 64 | ||||
-rw-r--r-- | flashcp.c | 392 | ||||
-rw-r--r-- | ftl_check.c | 233 | ||||
-rw-r--r-- | ftl_format.c | 343 | ||||
-rw-r--r-- | jffs-dump.c | 361 | ||||
-rw-r--r-- | jffs2dump.c | 725 | ||||
-rw-r--r-- | jffs2reader.c | 941 | ||||
-rw-r--r-- | jittertest/COPYING | 340 | ||||
-rw-r--r-- | jittertest/JitterTest.c | 1044 | ||||
-rw-r--r-- | jittertest/Makefile | 46 | ||||
-rw-r--r-- | jittertest/README | 197 | ||||
-rw-r--r-- | jittertest/filljffs2.sh | 16 | ||||
-rw-r--r-- | jittertest/plotJittervsFill.c | 312 | ||||
-rw-r--r-- | mkfs.ffs2.c | 239 | ||||
-rw-r--r-- | mkfs.jffs.c | 739 | ||||
-rw-r--r-- | mkfs.jffs2.1 | 242 | ||||
-rw-r--r-- | mkfs.jffs2.c | 1583 | ||||
-rw-r--r-- | mtd-utils.spec | 40 | ||||
-rw-r--r-- | mtd_debug.c | 445 | ||||
-rw-r--r-- | nanddump.c | 312 | ||||
-rw-r--r-- | nandwrite.c | 460 | ||||
-rw-r--r-- | nftl_format.c | 442 | ||||
-rw-r--r-- | nftldump.c | 299 | ||||
-rw-r--r-- | rfddump.c | 338 | ||||
-rw-r--r-- | rfdformat.c | 160 | ||||
-rw-r--r-- | summary.h | 143 | ||||
-rw-r--r-- | sumtool.c | 822 |
53 files changed, 15150 insertions, 0 deletions
@@ -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()*/ + + + + + + + @@ -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; +} @@ -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); +} @@ -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 +}; @@ -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,®count) == 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,®count) == 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, ®info) == 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,®ions[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; +} |